From f4881eb5e2892e75723b8cd26749b735741b52c4 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 29 Dec 2018 13:30:53 +0100 Subject: [PATCH 01/29] Build system, ini file loading --- meson.build | 6 ++ src/config-ini.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++ src/config-ini.h | 22 +++++++ src/config-load.c | 55 +++++++++++++++++ src/config-load.h | 5 ++ src/meson.build | 7 +++ src/pnc.c | 6 ++ src/types.h | 38 ++++++++++++ 8 files changed, 286 insertions(+) create mode 100644 meson.build create mode 100644 src/config-ini.c create mode 100644 src/config-ini.h create mode 100644 src/config-load.c create mode 100644 src/config-load.h create mode 100644 src/meson.build create mode 100644 src/pnc.c create mode 100644 src/types.h diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..aaa8cf6 --- /dev/null +++ b/meson.build @@ -0,0 +1,6 @@ +project('pnc', 'c') + +cc = meson.get_compiler('c') +ubox_dep = cc.find_library('ubox') + +subdir('src') diff --git a/src/config-ini.c b/src/config-ini.c new file mode 100644 index 0000000..7cfa1ff --- /dev/null +++ b/src/config-ini.c @@ -0,0 +1,147 @@ +#include "config-ini.h" +#include "types.h" + +#include +#include +#include +#include +#include + +static void free_field(ini_field_t *field) { + free(field->key); + free(field->value); + free(field); +} + +static void free_section(ini_section_t *section) { + ini_field_t *field, *tmp; + + list_for_each_entry_safe(field, tmp, §ion->fields, node) + free_field(field); + + free(section->name); + free(section); +} + +void free_ini_file(ini_file_t *file) { + ini_section_t *section, *tmp; + + list_for_each_entry_safe(section, tmp, &file->sections, node) + free_section(section); + + free(file); +} + +static bool add_field(ini_section_t *section, const char *key, const char *value) { + ini_field_t *field = calloc(1, sizeof(*field)); + if (!field) + return false; + + field->key = strdup(key); + field->value = strdup(value); + + if (!field->key || !field->value) { + free_field(field); + return false; + } + + list_add_tail(&field->node, §ion->fields); + + return true; +} + +static ini_section_t * add_section(ini_file_t *file, const char *name) { + ini_section_t *section = calloc(1, sizeof(*section)); + if (!section) + return NULL; + + section->name = strdup(name); + if (!section->name) { + free(section); + return false; + } + + INIT_LIST_HEAD(§ion->fields); + list_add_tail(§ion->node, &file->sections); + + return section; +} + +ini_file_t * read_ini_file(const char *filename) { + FILE *f = fopen(filename, "r"); + if (!f) + return NULL; + + ini_file_t *file = calloc(1, sizeof(*file)); + if (!file) + goto error; + + INIT_LIST_HEAD(&file->sections); + + ini_section_t *section = NULL; + int err = 0; + + char *line = NULL; + size_t n = 0; + + while (getline(&line, &n, f) >= 0) { + char *input = line; + + while (isspace(input[0])) + input++; + + if (input[0] == '#' || input[0] == '\0') + continue; + + size_t len = strlen(input); + + while (isspace(input[len-1])) + len--; + + if (input[0] == '[') { + if (input[len-1] != ']') { + err = EINVAL; + goto error; + } + + input[len-1] = '\0'; + + section = add_section(file, input+1); + if (!section) + goto error; + } else { + if (!section) { + err = EINVAL; + goto error; + } + + input[len] = '\0'; + + char *delim = strchr(input, '='); + if (!delim) { + err = EINVAL; + goto error; + } + + *delim = '\0'; + + if (!add_field(section, input, delim+1)) + goto error; + } + } + + if (ferror(f)) + err = EIO; + +error: + free(line); + fclose(f); + + if (err) { + free_ini_file(file); + errno = err; + return NULL; + } + + return file; +} diff --git a/src/config-ini.h b/src/config-ini.h new file mode 100644 index 0000000..802195d --- /dev/null +++ b/src/config-ini.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef struct { + struct list_head node; + char *key; + char *value; +} ini_field_t; + +typedef struct { + struct list_head node; + struct list_head fields; + char *name; +} ini_section_t; + +typedef struct { + struct list_head sections; +} ini_file_t; + +ini_file_t * read_ini_file(const char *filename); +void free_ini_file(ini_file_t *file); diff --git a/src/config-load.c b/src/config-load.c new file mode 100644 index 0000000..9335813 --- /dev/null +++ b/src/config-load.c @@ -0,0 +1,55 @@ +#include "config-load.h" +#include "config-ini.h" +#include "types.h" + +#include +#include +#include +#include + +static const char * simple_basename(const char *path) { + const char *slash = strrchr(path, '/'); + return slash ? (slash+1) : path; +} + +static const char * extname(const char *filename) { + const char *dot = strrchr(filename, '.'); + return dot ? (dot+1) : NULL; +} + +static interface_config_t * read_interface_config(const char *path) { + ini_file_t *file = read_ini_file(path); + ini_section_t *section; + ini_field_t *field; + + list_for_each_entry(section, &file->sections, node) { + printf("%s\n", section->name); + + list_for_each_entry(field, §ion->fields, node) + printf("%s.%s=%s\n", section->name, field->key, field->value); + } + + free_ini_file(file); + + return NULL; +} + +bool read_config_file(const char *path) { + const char *filename = simple_basename(path); + const char *ext = extname(filename); + if (!ext) { + errno = EINVAL; + return false; + } + + char *name = strndup(filename, (ext - filename) - 1); + + if (strcmp(ext, "interface") == 0) { + interface_config_t *iface = read_interface_config(path); + free(iface); + } + + free(name); + + return true; +} diff --git a/src/config-load.h b/src/config-load.h new file mode 100644 index 0000000..0c278ee --- /dev/null +++ b/src/config-load.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +bool read_config_file(const char *path); diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..c24ca21 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,7 @@ +src = [ + 'pnc.c', + 'config-ini.c', + 'config-load.c', +] + +executable('pnc', sources: src, dependencies: [ubox_dep]) diff --git a/src/pnc.c b/src/pnc.c new file mode 100644 index 0000000..651d140 --- /dev/null +++ b/src/pnc.c @@ -0,0 +1,6 @@ +#include "config-load.h" + +int main(int argc, char *argv[]) { + read_config_file(argv[1]); + return 0; +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..cabaac6 --- /dev/null +++ b/src/types.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include + +typedef struct { + uint8_t addr[6]; +} macaddr_t; + +#define NODE_NAME(c) (*(char **)&(c)->node.key) + +typedef struct { + struct avl_tree subtypes; + struct avl_tree interfaces; +} config_t; + +typedef struct { + struct avl_node node; +} subconfig_type_t; + +typedef struct { + struct avl_node node; + struct avl_tree subconfigs; + + macaddr_t macaddr; + uint16_t mtu; +} interface_config_t; + +typedef struct { + struct avl_node node; + const char *type; +} interface_subconfig_t; + + +typedef struct { +} interface_generator_t; From 9e059f898ae1b0528cdde553fd51c661901e283a Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 29 Dec 2018 17:00:31 +0100 Subject: [PATCH 02/29] Load config dirs --- meson.build | 3 +- src/config-ini.c | 8 +-- src/config-ini.h | 4 +- src/config-load.c | 144 +++++++++++++++++++++++++++++++++++++--------- src/config-load.h | 2 +- src/pnc.c | 2 +- 6 files changed, 125 insertions(+), 38 deletions(-) diff --git a/meson.build b/meson.build index aaa8cf6..f8089e4 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,5 @@ -project('pnc', 'c') +project('pnc', 'c', default_options : ['c_std=c11']) +add_project_arguments('-D_DEFAULT_SOURCE', language : 'c') cc = meson.get_compiler('c') ubox_dep = cc.find_library('ubox') diff --git a/src/config-ini.c b/src/config-ini.c index 7cfa1ff..30275fa 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -67,11 +66,7 @@ static ini_section_t * add_section(ini_file_t *file, const char *name) { return section; } -ini_file_t * read_ini_file(const char *filename) { - FILE *f = fopen(filename, "r"); - if (!f) - return NULL; - +ini_file_t * read_ini_file(FILE *f) { ini_file_t *file = calloc(1, sizeof(*file)); if (!file) goto error; @@ -135,7 +130,6 @@ ini_file_t * read_ini_file(const char *filename) { error: free(line); - fclose(f); if (err) { free_ini_file(file); diff --git a/src/config-ini.h b/src/config-ini.h index 802195d..58f6608 100644 --- a/src/config-ini.h +++ b/src/config-ini.h @@ -2,6 +2,8 @@ #include +#include + typedef struct { struct list_head node; char *key; @@ -18,5 +20,5 @@ typedef struct { struct list_head sections; } ini_file_t; -ini_file_t * read_ini_file(const char *filename); +ini_file_t * read_ini_file(FILE *f); void free_ini_file(ini_file_t *file); diff --git a/src/config-load.c b/src/config-load.c index 9335813..28b5953 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -2,54 +2,144 @@ #include "config-ini.h" #include "types.h" +#include +#include + +#include +#include #include -#include +#include #include #include +#include -static const char * simple_basename(const char *path) { - const char *slash = strrchr(path, '/'); - return slash ? (slash+1) : path; -} +typedef enum { + LOAD_TYPE_INTERFACE, + LOAD_TYPE_SUBCONFIG, + LOAD_TYPE_GENERATOR, + LOAD_TYPE_DEVICE, + N_LOAD_TYPES, +} load_type_t; + +typedef struct { + struct avl_node node; + char *type; + ini_file_t *data; +} load_object_t; + +typedef struct { + struct avl_tree objects[N_LOAD_TYPES]; +} load_ctx_t; static const char * extname(const char *filename) { const char *dot = strrchr(filename, '.'); return dot ? (dot+1) : NULL; } -static interface_config_t * read_interface_config(const char *path) { - ini_file_t *file = read_ini_file(path); - ini_section_t *section; - ini_field_t *field; - - list_for_each_entry(section, &file->sections, node) { - printf("%s\n", section->name); - - list_for_each_entry(field, §ion->fields, node) - printf("%s.%s=%s\n", section->name, field->key, field->value); - } - - free_ini_file(file); - - return NULL; +static void free_object(load_object_t *obj) { + free_ini_file(obj->data); + free(obj->type); + free(NODE_NAME(obj)); + free(obj); } -bool read_config_file(const char *path) { - const char *filename = simple_basename(path); +static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { const char *ext = extname(filename); if (!ext) { errno = EINVAL; return false; } - char *name = strndup(filename, (ext - filename) - 1); + load_type_t type; + if (strcmp(ext, "interface") == 0) + type = LOAD_TYPE_INTERFACE; + else if (strcmp(ext, "sub") == 0) + type = LOAD_TYPE_SUBCONFIG; + else if (strcmp(ext, "gen") == 0) + type = LOAD_TYPE_GENERATOR; + else if (strcmp(ext, "bridge") == 0) + type = LOAD_TYPE_DEVICE; + else + return true; + + int fd = openat(dirfd, filename, O_RDONLY); + if (fd < 0) + return false; + + FILE *f = fdopen(fd, "r"); + if (!f) { + close(fd); + return false; + } + + ini_file_t *data = read_ini_file(f); + fclose(f); - if (strcmp(ext, "interface") == 0) { - interface_config_t *iface = read_interface_config(path); - free(iface); + if (!data) + return false; + + load_object_t *obj = calloc(1, sizeof(*obj)); + if (!obj) { + free_ini_file(data); + return false; + } + obj->data = data; + + char *name = strndup(filename, (ext - filename) - 1); + if (!name) { + free_object(obj); + return false; + } + NODE_NAME(obj) = name; + + obj->type = strdup(ext); + if (!obj->type) { + free_object(obj); + return false; } - free(name); + avl_insert(&ctx->objects[type], &obj->node); return true; } + +static bool read_config_dir(load_ctx_t *ctx, const char *path) { + DIR *dir = opendir(path); + if (!dir) + return false; + + int fd = dirfd(dir); + + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_type == DT_REG) + read_config_file(ctx, fd, ent->d_name); + } + + closedir(dir); + + return true; +} + +bool read_config(const char *path) { + load_ctx_t ctx; + for (size_t i = 0; i < N_LOAD_TYPES; i++) + avl_init(&ctx.objects[i], avl_strcmp, true, NULL); + + bool ret = read_config_dir(&ctx, path); + + for (size_t i = 0; i < N_LOAD_TYPES; i++) { + load_object_t *obj; + avl_for_each_element(&ctx.objects[i], obj, node) { + printf("%s(%u): %s\n", obj->type, (unsigned)i, NODE_NAME(obj)); + } + } + + for (size_t i = 0; i < N_LOAD_TYPES; i++) { + load_object_t *obj, *tmp; + avl_for_each_element_safe(&ctx.objects[i], obj, node, tmp) + free_object(obj); + } + + return ret; +} diff --git a/src/config-load.h b/src/config-load.h index 0c278ee..e2b4736 100644 --- a/src/config-load.h +++ b/src/config-load.h @@ -2,4 +2,4 @@ #include -bool read_config_file(const char *path); +bool read_config(const char *path); diff --git a/src/pnc.c b/src/pnc.c index 651d140..942ba2f 100644 --- a/src/pnc.c +++ b/src/pnc.c @@ -1,6 +1,6 @@ #include "config-load.h" int main(int argc, char *argv[]) { - read_config_file(argv[1]); + read_config(argv[1]); return 0; } From daa68c4b2de9814b8331e7b9079d5fc49bee35c1 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 5 Jan 2019 22:06:45 +0100 Subject: [PATCH 03/29] Add first parts of interface config handling --- meson.build | 3 +- src/config-ini.c | 1 - src/config-ini.h | 6 +- src/config-load.c | 100 +++++++++----------- src/config-process.c | 135 +++++++++++++++++++++++++++ src/config-process.h | 15 +++ src/device-bridge.c | 15 +++ src/device-interface.c | 202 +++++++++++++++++++++++++++++++++++++++++ src/device.c | 15 +++ src/device.h | 23 +++++ src/keywords.c | 31 +++++++ src/keywords.def | 9 ++ src/keywords.h | 12 +++ src/meson.build | 10 +- src/{pnc.c => neco.c} | 0 src/types.h | 38 -------- src/util.h | 3 + src/vector.c | 114 +++++++++++++++++++++++ src/vector.h | 113 +++++++++++++++++++++++ 19 files changed, 741 insertions(+), 104 deletions(-) create mode 100644 src/config-process.c create mode 100644 src/config-process.h create mode 100644 src/device-bridge.c create mode 100644 src/device-interface.c create mode 100644 src/device.c create mode 100644 src/device.h create mode 100644 src/keywords.c create mode 100644 src/keywords.def create mode 100644 src/keywords.h rename src/{pnc.c => neco.c} (100%) delete mode 100644 src/types.h create mode 100644 src/util.h create mode 100644 src/vector.c create mode 100644 src/vector.h diff --git a/meson.build b/meson.build index f8089e4..81cf8ee 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,4 @@ -project('pnc', 'c', default_options : ['c_std=c11']) -add_project_arguments('-D_DEFAULT_SOURCE', language : 'c') +project('neco', 'c', default_options : ['c_std=gnu11']) cc = meson.get_compiler('c') ubox_dep = cc.find_library('ubox') diff --git a/src/config-ini.c b/src/config-ini.c index 30275fa..0461e13 100644 --- a/src/config-ini.c +++ b/src/config-ini.c @@ -1,5 +1,4 @@ #include "config-ini.h" -#include "types.h" #include #include diff --git a/src/config-ini.h b/src/config-ini.h index 58f6608..48400fd 100644 --- a/src/config-ini.h +++ b/src/config-ini.h @@ -4,19 +4,19 @@ #include -typedef struct { +typedef struct _ini_field { struct list_head node; char *key; char *value; } ini_field_t; -typedef struct { +typedef struct _ini_section { struct list_head node; struct list_head fields; char *name; } ini_section_t; -typedef struct { +typedef struct _ini_file { struct list_head sections; } ini_file_t; diff --git a/src/config-load.c b/src/config-load.c index 28b5953..92abca0 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -1,10 +1,11 @@ #include "config-load.h" -#include "config-ini.h" -#include "types.h" +#include "config-process.h" +#include "device.h" +#include "util.h" -#include #include +#include #include #include #include @@ -13,22 +14,9 @@ #include #include -typedef enum { - LOAD_TYPE_INTERFACE, - LOAD_TYPE_SUBCONFIG, - LOAD_TYPE_GENERATOR, - LOAD_TYPE_DEVICE, - N_LOAD_TYPES, -} load_type_t; - -typedef struct { - struct avl_node node; - char *type; - ini_file_t *data; -} load_object_t; - -typedef struct { - struct avl_tree objects[N_LOAD_TYPES]; +typedef struct _load_ctx { + struct avl_tree subtypes; + struct avl_tree devices; } load_ctx_t; static const char * extname(const char *filename) { @@ -36,29 +24,24 @@ static const char * extname(const char *filename) { return dot ? (dot+1) : NULL; } -static void free_object(load_object_t *obj) { - free_ini_file(obj->data); - free(obj->type); - free(NODE_NAME(obj)); - free(obj); +static bool isfile(int fd) { + struct stat buf; + if (fstat(fd, &buf) < 0) + return false; + + return (buf.st_mode & S_IFMT) == S_IFREG; } static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { const char *ext = extname(filename); - if (!ext) { - errno = EINVAL; - return false; - } + if (!ext) + return true; - load_type_t type; - if (strcmp(ext, "interface") == 0) - type = LOAD_TYPE_INTERFACE; - else if (strcmp(ext, "sub") == 0) - type = LOAD_TYPE_SUBCONFIG; - else if (strcmp(ext, "gen") == 0) - type = LOAD_TYPE_GENERATOR; - else if (strcmp(ext, "bridge") == 0) - type = LOAD_TYPE_DEVICE; + struct avl_tree *target_tree; + if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0) + target_tree = &ctx->subtypes; + else if (get_device_type(ext)) + target_tree = &ctx->devices; else return true; @@ -66,6 +49,11 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { if (fd < 0) return false; + if (!isfile(fd)) { + close(fd); + return true; + } + FILE *f = fdopen(fd, "r"); if (!f) { close(fd); @@ -78,7 +66,7 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { if (!data) return false; - load_object_t *obj = calloc(1, sizeof(*obj)); + config_object_t *obj = calloc(1, sizeof(*obj)); if (!obj) { free_ini_file(data); return false; @@ -87,18 +75,18 @@ static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { char *name = strndup(filename, (ext - filename) - 1); if (!name) { - free_object(obj); + config_object_free(obj); return false; } NODE_NAME(obj) = name; obj->type = strdup(ext); if (!obj->type) { - free_object(obj); + config_object_free(obj); return false; } - avl_insert(&ctx->objects[type], &obj->node); + avl_insert(target_tree, &obj->node); return true; } @@ -111,10 +99,8 @@ static bool read_config_dir(load_ctx_t *ctx, const char *path) { int fd = dirfd(dir); struct dirent *ent; - while ((ent = readdir(dir)) != NULL) { - if (ent->d_type == DT_REG) - read_config_file(ctx, fd, ent->d_name); - } + while ((ent = readdir(dir)) != NULL) + read_config_file(ctx, fd, ent->d_name); closedir(dir); @@ -123,23 +109,21 @@ static bool read_config_dir(load_ctx_t *ctx, const char *path) { bool read_config(const char *path) { load_ctx_t ctx; - for (size_t i = 0; i < N_LOAD_TYPES; i++) - avl_init(&ctx.objects[i], avl_strcmp, true, NULL); + avl_init(&ctx.subtypes, avl_strcmp, true, NULL); + avl_init(&ctx.devices, avl_strcmp, true, NULL); bool ret = read_config_dir(&ctx, path); - for (size_t i = 0; i < N_LOAD_TYPES; i++) { - load_object_t *obj; - avl_for_each_element(&ctx.objects[i], obj, node) { - printf("%s(%u): %s\n", obj->type, (unsigned)i, NODE_NAME(obj)); - } - } + struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes); + struct avl_tree *devices = config_process_devices(&ctx.devices); - for (size_t i = 0; i < N_LOAD_TYPES; i++) { - load_object_t *obj, *tmp; - avl_for_each_element_safe(&ctx.objects[i], obj, node, tmp) - free_object(obj); - } + free(subtypes); + + device_t *dev, *tmp; + avl_remove_all_elements(devices, dev, node, tmp) + dev->type->free_device(dev); + + free(devices); return ret; } diff --git a/src/config-process.c b/src/config-process.c new file mode 100644 index 0000000..e71b228 --- /dev/null +++ b/src/config-process.c @@ -0,0 +1,135 @@ +#include "config-process.h" +#include "device.h" +#include "keywords.h" +#include "util.h" + +#include + +#include +#include +#include + +typedef struct _process_ctx { + struct avl_tree *ret; +} process_ctx_t; + +typedef struct _config_subtype { + struct avl_node node; +} config_subtype_t; + +void config_object_free(config_object_t *obj) { + free_ini_file(obj->data); + free(obj->type); + free(NODE_NAME(obj)); + free(obj); +} + +static bool subtype_supported(const process_ctx_t *ctx, const char *type) { + switch (lookup_keyword(type)) { + case KW_Properties: + case KW_Generate: + case KW_Static: + case KW_DHCP: + case KW_DHCPv6: + return true; + + default: + return avl_find(ctx->ret, type); + } +} + +static config_subtype_t * config_process_subtype(const process_ctx_t *ctx, config_object_t *obj) { + ini_section_t *section; + list_for_each_entry(section, &obj->data->sections, node) { + printf("%s %s %i\n", NODE_NAME(obj), section->name, subtype_supported(ctx, section->name)); + + if (!subtype_supported(ctx, section->name)) + return NULL; + } + + config_subtype_t *ret = calloc(1, sizeof(*ret)); + if (!ret) + return NULL; + + char *name = strdup(NODE_NAME(obj)); + if (!name) { + free(ret); + return NULL; + } + + NODE_NAME(ret) = name; + + return ret; +} + +struct avl_tree * config_process_subtypes(struct avl_tree *sub) { + process_ctx_t ctx; + ctx.ret = calloc(1, sizeof(*ctx.ret)); + if (!ctx.ret) + return NULL; + + avl_init(ctx.ret, avl_strcmp, false, NULL); + + while (true) { + size_t processed = 0; + + config_object_t *obj, *tmp; + avl_for_each_element_safe(sub, obj, node, tmp) { + config_subtype_t *t = config_process_subtype(&ctx, obj); + if (!t) + continue; + + avl_delete(sub, &obj->node); + config_object_free(obj); + + avl_insert(ctx.ret, &t->node); + + processed++; + } + + if (!processed) + break; + } + + return ctx.ret; +} + +static device_t * config_process_device(config_object_t *obj) { + const device_type_t *type = get_device_type(obj->type); + + assert(type != NULL); + + return type->process_config(NODE_NAME(obj), obj->data); +} + +struct avl_tree * config_process_devices(struct avl_tree *devices) { + process_ctx_t ctx; + ctx.ret = calloc(1, sizeof(*ctx.ret)); + if (!ctx.ret) + return NULL; + + avl_init(ctx.ret, avl_strcmp, false, NULL); + + while (true) { + size_t processed = 0; + + config_object_t *obj, *tmp; + avl_for_each_element_safe(devices, obj, node, tmp) { + device_t *device = config_process_device(obj); + if (!device) + continue; + + avl_delete(devices, &obj->node); + config_object_free(obj); + + avl_insert(ctx.ret, &device->node); + + processed++; + } + + if (!processed) + break; + } + + return ctx.ret; +} diff --git a/src/config-process.h b/src/config-process.h new file mode 100644 index 0000000..501e765 --- /dev/null +++ b/src/config-process.h @@ -0,0 +1,15 @@ +#pragma once + +#include "config-ini.h" + +#include + +typedef struct _config_object { + struct avl_node node; + char *type; + ini_file_t *data; +} config_object_t; + +void config_object_free(config_object_t *obj); +struct avl_tree * config_process_subtypes(struct avl_tree *sub); +struct avl_tree * config_process_devices(struct avl_tree *devices); diff --git a/src/device-bridge.c b/src/device-bridge.c new file mode 100644 index 0000000..f973dbf --- /dev/null +++ b/src/device-bridge.c @@ -0,0 +1,15 @@ +#include "device.h" + +static device_t * bridge_process_config(const char *name, const ini_file_t *config) { + printf("Bridge: %s\n", name); + return NULL; +} + +device_type_t device_type_bridge = { + .process_config = bridge_process_config, +}; + +__attribute__((constructor)) +static void bridge_constructor(void) { + register_device_type("bridge", &device_type_bridge); +} diff --git a/src/device-interface.c b/src/device-interface.c new file mode 100644 index 0000000..5ac7cee --- /dev/null +++ b/src/device-interface.c @@ -0,0 +1,202 @@ +#include "device.h" +#include "keywords.h" +#include "util.h" +#include "vector.h" + +#include +#include +#include + +#include +#include +#include +#include + +static device_type_t device_type_interface; + +typedef struct _ipaddr { + int af; + union { + struct in_addr addr4; + struct in6_addr addr6; + }; +} ipaddr_t; + +typedef struct _ipprefix { + ipaddr_t addr; + uint8_t plen; +} ipaddr_prefix_t; + +typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t; + +typedef struct _device_interface { + device_t device; + + struct ether_addr macaddr; + uint16_t mtu; + + ipaddr_prefix_vector_t addrs; +} device_interface_t; + +static unsigned long strtoul_safe(const char *str) { + char *endptr; + + errno = 0; + unsigned long val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr) + errno = EINVAL; + + return val; +} + +static bool parse_address(ipaddr_t *addr, const char *str) { + if (inet_pton(AF_INET, str, &addr->addr4)) { + addr->af = AF_INET; + return true; + } + + if (inet_pton(AF_INET6, str, &addr->addr6)) { + addr->af = AF_INET6; + return true; + } + + return false; +} + +static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) { + const char *slash = strrchr(str, '/'); + if (!slash) + return false; + + size_t len = slash - str; + char buf[len+1]; + memcpy(buf, str, len); + buf[len] = 0; + + if (!parse_address(&prefix->addr, buf)) + return false; + + long plen = strtoul_safe(slash + 1); + if (errno) + return false; + + switch (prefix->addr.af) { + case AF_INET: + if (plen > 32) + return false; + break; + + case AF_INET6: + if (plen > 128) + return false; + break; + + default: + assert(false); + __builtin_unreachable(); + } + + prefix->plen = plen; + + // TODO: Implement allow_host + + return true; +} + +static bool process_section_device(device_interface_t *iface, const ini_section_t *section) { + ini_field_t *field; + + list_for_each_entry(field, §ion->fields, node) { + switch (lookup_keyword(field->key)) { + default: + fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key); + } + } + + return true; +} + +static bool process_section_static(device_interface_t *iface, const ini_section_t *section) { + ini_field_t *field; + + list_for_each_entry(field, §ion->fields, node) { + switch (lookup_keyword(field->key)) { + case KW_Address: { + ipaddr_prefix_t p; + if (!parse_prefix(&p, field->value, true)) { + fprintf(stderr, "interface: [Static]: unable to parse Address %s\n", field->value); + break; + } + + if (!VECTOR_ADD(iface->addrs, p)) { + fprintf(stderr, "interface: [Static]: adding address failed\n"); + return false; + } + + break; + } + + default: + fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key); + } + } + + return true; +} + +static void interface_free_device(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + VECTOR_FREE(iface->addrs); + free(NODE_NAME(dev)); + free(iface); +} + +static device_t * interface_process_config(const char *name, const ini_file_t *config) { + device_interface_t *iface = calloc(1, sizeof(*iface)); + if (!iface) + return NULL; + + device_t *dev = &iface->device; + dev->type = &device_type_interface; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + const ini_section_t *section; + list_for_each_entry(section, &config->sections, node) { + switch (lookup_keyword(section->name)) { + case KW_Device: + if (!process_section_device(iface, section)) + goto err; + break; + + case KW_Static: + if (!process_section_static(iface, section)) + goto err; + break; + + default: + fprintf(stderr, "interface: unknown section %s\n", section->name); + } + } + + return dev; + +err: + interface_free_device(dev); + return NULL; +} + +static device_type_t device_type_interface = { + .process_config = interface_process_config, + .free_device = interface_free_device, +}; + +__attribute__((constructor)) +static void interface_constructor(void) { + register_device_type("interface", &device_type_interface); +} diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..a773036 --- /dev/null +++ b/src/device.c @@ -0,0 +1,15 @@ +#include "device.h" +#include "util.h" + +#include + +AVL_TREE(device_types, avl_strcmp, false, NULL); + +void register_device_type(const char *name, device_type_t *device_type) { + NODE_NAME(device_type) = (char *)name; + avl_insert(&device_types, &device_type->node); +} + +const device_type_t * get_device_type(const char *name) { + return avl_find_element(&device_types, name, (device_type_t *)NULL, node); +} diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..f437021 --- /dev/null +++ b/src/device.h @@ -0,0 +1,23 @@ +#pragma once + +#include "config-ini.h" + +#include + +extern struct avl_tree device_types; + +typedef struct _device_type device_type_t; + +typedef struct _device { + struct avl_node node; + const device_type_t *type; +} device_t; + +struct _device_type { + struct avl_node node; + device_t * (*process_config)(const char *name, const ini_file_t *config); + void (*free_device)(device_t *device); +}; + +void register_device_type(const char *name, device_type_t *device_type); +const device_type_t * get_device_type(const char *name); diff --git a/src/keywords.c b/src/keywords.c new file mode 100644 index 0000000..12393bd --- /dev/null +++ b/src/keywords.c @@ -0,0 +1,31 @@ +#include "keywords.h" + +#include + +#include +#include + +static const char *const keywords[] = { + +#define KW(kw) #kw, +#include "keywords.def" +#undef KW + +}; + +static int compare_keywords(const void *a, const void *b) { + const char *const *ea = a, *const *eb = b; + return strcmp(*ea, *eb); +} + +keyword_t lookup_keyword(const char *keyword) { + const char *const *entry = bsearch( + &keyword, keywords, ARRAY_SIZE(keywords), sizeof(const char *), + compare_keywords + ); + + if (!entry) + return UNKNOWN_KEYWORD; + + return (keyword_t) (entry - keywords + 1); +} diff --git a/src/keywords.def b/src/keywords.def new file mode 100644 index 0000000..0dbd866 --- /dev/null +++ b/src/keywords.def @@ -0,0 +1,9 @@ +/* Config section and field names; keep sorted! */ + +KW(Address) +KW(DHCP) +KW(DHCPv6) +KW(Device) +KW(Generate) +KW(Properties) +KW(Static) diff --git a/src/keywords.h b/src/keywords.h new file mode 100644 index 0000000..05d809e --- /dev/null +++ b/src/keywords.h @@ -0,0 +1,12 @@ +#pragma once + +typedef enum _keyword { + UNKNOWN_KEYWORD = 0, + +#define KW(kw) KW_##kw, +#include "keywords.def" +#undef KW + +} keyword_t; + +keyword_t lookup_keyword(const char *keyword); diff --git a/src/meson.build b/src/meson.build index c24ca21..b088e39 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,7 +1,13 @@ src = [ - 'pnc.c', + 'neco.c', 'config-ini.c', 'config-load.c', + 'config-process.c', + 'device.c', + 'device-bridge.c', + 'device-interface.c', + 'keywords.c', + 'vector.c', ] -executable('pnc', sources: src, dependencies: [ubox_dep]) +executable('neco', sources: src, dependencies: [ubox_dep]) diff --git a/src/pnc.c b/src/neco.c similarity index 100% rename from src/pnc.c rename to src/neco.c diff --git a/src/types.h b/src/types.h deleted file mode 100644 index cabaac6..0000000 --- a/src/types.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include -#include - -typedef struct { - uint8_t addr[6]; -} macaddr_t; - -#define NODE_NAME(c) (*(char **)&(c)->node.key) - -typedef struct { - struct avl_tree subtypes; - struct avl_tree interfaces; -} config_t; - -typedef struct { - struct avl_node node; -} subconfig_type_t; - -typedef struct { - struct avl_node node; - struct avl_tree subconfigs; - - macaddr_t macaddr; - uint16_t mtu; -} interface_config_t; - -typedef struct { - struct avl_node node; - const char *type; -} interface_subconfig_t; - - -typedef struct { -} interface_generator_t; diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..31b13b6 --- /dev/null +++ b/src/util.h @@ -0,0 +1,3 @@ +#pragma once + +#define NODE_NAME(c) (*(char **)&(c)->node.key) diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..41cd0cc --- /dev/null +++ b/src/vector.c @@ -0,0 +1,114 @@ +/** + \file + + Typesafe dynamically sized arrays +*/ + + +#include "vector.h" + +#include +#include +#include + +/** The minimum number of elements to allocate even when fewer elements are used */ +#define MIN_VECTOR_ALLOC 4 + +/** + Multiplies two size_t values, checking for overflows + + Both arguments are limited to the size of a uint32_t (to keep the check simple) + + Returns SIZE_MAX and sets errno on failure. +*/ +static inline size_t mul_check(size_t members, size_t size) { + uint64_t v = (uint64_t)members * size; + + if (members > UINT32_MAX || size > UINT32_MAX || v > SIZE_MAX) { + errno = EOVERFLOW; + return SIZE_MAX; + } + + return v; +} + +/** + Reallocates a block of memory for an array on the heap + + May fail with EOVERFLOW (when members or size does not fit into a uint32_t, + or their product does not fit into a size_t) or ENOMEM. +*/ +static inline void * realloc_array(void *ptr, size_t members, size_t size) { + errno = 0; + size_t array_size = mul_check(members, size); + if (errno) + return NULL; + + return realloc(ptr, array_size); +} + +/** + Resizes a vector + + Vector allocations are always powers of 2. + + Internal function, use VECTOR_RESIZE() instead. +*/ +bool _vector_resize(vector_desc_t *desc, void **data, size_t n, size_t elemsize) { + desc->length = n; + + size_t alloc = desc->allocated; + + if (!alloc) { + alloc = MIN_VECTOR_ALLOC; + n = n*3/2; + } + + while (alloc < n) { + alloc <<= 1; + if (!alloc) { + errno = ENOMEM; + return false; + } + } + + if (alloc != desc->allocated) { + desc->allocated = alloc; + void *tmp = realloc_array(*data, alloc, elemsize); + if (!tmp) + return false; + + *data = tmp; + } + + return true; +} + +/** + Inserts an element into a vector + + Internal function, use VECTOR_INSERT() and VECTOR_ADD() instead. +*/ +bool _vector_insert(vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize) { + if (!_vector_resize(desc, data, desc->length+1, elemsize)) + return false; + + void *p = *data + pos*elemsize; + + memmove(p+elemsize, p, (desc->length-pos-1)*elemsize); + memcpy(p, element, elemsize); + + return true; +} + +/** + Deletes an element from a vector + + Internal function, use VECTOR_DELETE() instead. +*/ +void _vector_delete(vector_desc_t *desc, void **data, size_t pos, size_t elemsize) { + void *p = *data + pos*elemsize; + memmove(p, p+elemsize, (desc->length-pos-1)*elemsize); + + desc->length--; +} diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 0000000..02ea804 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,113 @@ +/** + \file + + Typesafe dynamically sized arrays +*/ + + +#pragma once + +#include +#include + +/** A vector descriptor */ +typedef struct _vector_desc { + size_t allocated; /**< The number of elements currently allocated */ + size_t length; /**< The actual number of elements in the vector */ +} vector_desc_t; + +/** + A type for a vector of \e type elements + + \hideinitializer +*/ +#define VECTOR(type) struct { \ + vector_desc_t desc; \ + type *data; \ +} + +bool _vector_resize(vector_desc_t *desc, void **data, size_t n, size_t elemsize); +bool _vector_insert(vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize); +void _vector_delete(vector_desc_t *desc, void **data, size_t pos, size_t elemsize); + +/** + Resizes the vector \e a to \e n elements + + \hideinitializer +*/ +#define VECTOR_RESIZE(v, n) ({ \ + __typeof__(v) *_v = &(v); \ + _vector_resize(&_v->desc, (void **)&_v->data, (n), sizeof(*_v->data)); \ +}) + +/** + Frees all resources used by the vector \e v + + \hideinitializer +*/ +#define VECTOR_FREE(v) free((v).data) + +/** + Returns the number of elements in the vector \e v + + \hideinitializer +*/ +#define VECTOR_LEN(v) ((v).desc.length) + +/** + Returns the element with index \e i in the vector \e v + + \hideinitializer +*/ +#define VECTOR_INDEX(v, i) ((v).data[i]) + +/** + Returns a pointer to the vector elements of \e v + + \hideinitializer +*/ +#define VECTOR_DATA(v) ((v).data) + +/** + Inserts the element \e elem at index \e pos of vector \e v + + \hideinitializer +*/ +#define VECTOR_INSERT(v, elem, pos) ({ \ + __typeof__(v) *_v = &(v); \ + __typeof__(*_v->data) _e = (elem); \ + _vector_insert(&_v->desc, (void **)&_v->data, &_e, (pos), sizeof(_e)); \ +}) + +/** + Adds the element \e elem at the end of vector \e v + + \hideinitializer +*/ +#define VECTOR_ADD(v, elem) ({ \ + __typeof__(v) *_v = &(v); \ + __typeof__(*_v->data) _e = (elem); \ + _vector_insert(&_v->desc, (void **)&_v->data, &_e, _v->desc.length, sizeof(_e)); \ +}) + +/** + Deletes the element at index \e pos of vector \e v + + \hideinitializer +*/ +#define VECTOR_DELETE(v, pos) ({ \ + __typeof__(v) *_v = &(v); \ + _vector_delete(&_v->desc, (void **)&_v->data, (pos), sizeof(*_v->data)); \ +}) + +/** + Performs a binary search on the vector \e v, returning a pointer to a matching vector element + + \hideinitializer +*/ +#define VECTOR_BSEARCH(key, v, cmp) ({ \ + __typeof__(v) *_v = &(v); \ + const __typeof__(*_v->data) *_key = (key); \ + int (*_cmp)(__typeof__(_key), __typeof__(_key)) = (cmp); \ + (__typeof__(_v->data))bsearch(_key, _v->data, _v->desc.length, sizeof(*_v->data), (int (*)(const void *, const void *))_cmp); \ +}) From b4bdc797028a3789bb5e0c68bc428bfd54bd200e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 5 Jan 2019 22:12:18 +0100 Subject: [PATCH 04/29] Add LICENSE --- LICENSE | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4bad81e --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012-2019, Matthias Schiffer +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 44cb17317c40fa9d39b3402f0826006f20387be5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 6 Jan 2019 07:20:08 +0100 Subject: [PATCH 05/29] Implement simple setting of link state and IP addresses --- meson.build | 4 +- src/config-load.c | 8 ++- src/device-interface.c | 147 +++++++++++++++++++++++++++++++++++++++-- src/device.h | 6 +- src/meson.build | 8 ++- src/neco.c | 7 ++ src/netlink.c | 38 +++++++++++ src/netlink.h | 12 ++++ 8 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 src/netlink.c create mode 100644 src/netlink.h diff --git a/meson.build b/meson.build index 81cf8ee..d7a554a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,8 @@ project('neco', 'c', default_options : ['c_std=gnu11']) cc = meson.get_compiler('c') -ubox_dep = cc.find_library('ubox') +libubox_dep = cc.find_library('ubox') + +libmnl_dep = dependency('libmnl', method : 'pkg-config') subdir('src') diff --git a/src/config-load.c b/src/config-load.c index 92abca0..63a87b4 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -120,8 +120,14 @@ bool read_config(const char *path) { free(subtypes); device_t *dev, *tmp; + avl_for_each_element(devices, dev, node) + dev->type->init(dev); + + //avl_for_each_element(devices, dev, node) + // dev->type->release(dev); + avl_remove_all_elements(devices, dev, node, tmp) - dev->type->free_device(dev); + dev->type->free(dev); free(devices); diff --git a/src/device-interface.c b/src/device-interface.c index 5ac7cee..a2aae14 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,17 +1,23 @@ #include "device.h" #include "keywords.h" +#include "netlink.h" #include "util.h" #include "vector.h" -#include -#include -#include - #include #include #include #include +#include +#include +#include +#include + +#include +#include +#include + static device_type_t device_type_interface; typedef struct _ipaddr { @@ -144,7 +150,7 @@ static bool process_section_static(device_interface_t *iface, const ini_section_ return true; } -static void interface_free_device(device_t *dev) { +static void interface_free(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); VECTOR_FREE(iface->addrs); @@ -152,6 +158,129 @@ static void interface_free_device(device_t *dev) { free(iface); } +static bool interface_set_link_flags(unsigned index, unsigned change, unsigned flags) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_SETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_index = index; + ifi->ifi_family = AF_UNSPEC; + + ifi->ifi_change = change; + ifi->ifi_flags = flags; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static bool interface_set_link_state(unsigned index, bool up) { + return interface_set_link_flags(index, IFF_UP, up ? IFF_UP : 0); +} + +static bool interface_set_ipaddr(unsigned index, ipaddr_prefix_t *addr, bool add) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + if (add) + nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; + + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa)); + ifa->ifa_index = index; + ifa->ifa_family = addr->addr.af; + + ifa->ifa_prefixlen = addr->plen; + + switch (addr->addr.af) { + case AF_INET: + mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4); + break; + + case AF_INET6: + mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6); + break; + + default: + errno = EINVAL; + return false; + } + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static void interface_init(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + unsigned index = if_nametoindex(NODE_NAME(dev)); + if (!index) + return; + + interface_set_link_state(index, true); + + for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), true); +} + +static void interface_update(device_t *dev) { +} + +static void interface_release(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); + + unsigned index = if_nametoindex(NODE_NAME(dev)); + if (!index) + return; + + for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), false); + + interface_set_link_state(index, false); +} + static device_t * interface_process_config(const char *name, const ini_file_t *config) { device_interface_t *iface = calloc(1, sizeof(*iface)); if (!iface) @@ -187,13 +316,17 @@ static device_t * interface_process_config(const char *name, const ini_file_t *c return dev; err: - interface_free_device(dev); + interface_free(dev); return NULL; } static device_type_t device_type_interface = { .process_config = interface_process_config, - .free_device = interface_free_device, + .free = interface_free, + + .init = interface_init, + .update = interface_update, + .release = interface_release, }; __attribute__((constructor)) diff --git a/src/device.h b/src/device.h index f437021..33acfbb 100644 --- a/src/device.h +++ b/src/device.h @@ -16,7 +16,11 @@ typedef struct _device { struct _device_type { struct avl_node node; device_t * (*process_config)(const char *name, const ini_file_t *config); - void (*free_device)(device_t *device); + void (*free)(device_t *device); + + void (*init)(device_t *device); + void (*update)(device_t *device); + void (*release)(device_t *device); }; void register_device_type(const char *name, device_type_t *device_type); diff --git a/src/meson.build b/src/meson.build index b088e39..9056cf3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,7 +7,13 @@ src = [ 'device-bridge.c', 'device-interface.c', 'keywords.c', + 'netlink.c', 'vector.c', ] -executable('neco', sources: src, dependencies: [ubox_dep]) +dep = [ + libubox_dep, + libmnl_dep, +] + +executable('neco', sources: src, dependencies: dep) diff --git a/src/neco.c b/src/neco.c index 942ba2f..4e4d297 100644 --- a/src/neco.c +++ b/src/neco.c @@ -1,6 +1,13 @@ #include "config-load.h" +#include "netlink.h" int main(int argc, char *argv[]) { + if (!nl_init()) + return 1; + read_config(argv[1]); + + nl_deinit(); + return 0; } diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 0000000..ec9c935 --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,38 @@ +#include "netlink.h" + +#include + +static struct mnl_socket *nl; +static uint32_t seq; + +bool nl_init(void) { + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) { + perror("mnl_socket_open"); + return false; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + nl_deinit(); + return false; + } + + return true; +} + +void nl_deinit(void) { + if (!nl) + return; + + mnl_socket_close(nl); + nl = NULL; +} + +struct mnl_socket * nl_socket(void) { + return nl; +} + +uint32_t nl_seq(void) { + return seq++; +} diff --git a/src/netlink.h b/src/netlink.h new file mode 100644 index 0000000..6529e92 --- /dev/null +++ b/src/netlink.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include +#include + +bool nl_init(void); +void nl_deinit(void); + +struct mnl_socket * nl_socket(void); +uint32_t nl_seq(void); From 0abdc827c9aa9e3f64d31d62663ea232863e4e76 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 12 May 2019 02:39:08 +0200 Subject: [PATCH 06/29] Safe libc test code --- .gitignore | 2 + Cargo.lock | 16 +++++ Cargo.toml | 14 ++++ src/c/mod.rs | 2 + src/c/posix.rs | 8 +++ src/c/string.rs | 188 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 19 +++++ 7 files changed, 249 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/c/mod.rs create mode 100644 src/c/posix.rs create mode 100644 src/c/string.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0e3bca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e536a5d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "libc" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "neco" +version = "0.1.0" +dependencies = [ + "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..769482b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "neco" +version = "0.1.0" +authors = ["Matthias Schiffer "] +edition = "2018" + +[dependencies] +libc = { version = "0.2.54", default-features = false } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/src/c/mod.rs b/src/c/mod.rs new file mode 100644 index 0000000..1b643c4 --- /dev/null +++ b/src/c/mod.rs @@ -0,0 +1,2 @@ +pub mod posix; +pub mod string; diff --git a/src/c/posix.rs b/src/c/posix.rs new file mode 100644 index 0000000..3933a2e --- /dev/null +++ b/src/c/posix.rs @@ -0,0 +1,8 @@ +extern "C" { + pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; + + pub static mut stdin: *mut libc::FILE; + pub static mut stdout: *mut libc::FILE; + pub static mut stderr: *mut libc::FILE; + +} diff --git a/src/c/string.rs b/src/c/string.rs new file mode 100644 index 0000000..8babef6 --- /dev/null +++ b/src/c/string.rs @@ -0,0 +1,188 @@ +use super::posix; +use core::{mem, ptr, slice}; +use core::ops::{Deref, DerefMut}; + +const SENTINEL: *mut libc::c_void = 1 as *mut libc::c_void; + +fn must_succeed(p: *mut T) -> *mut T { + if p.is_null() { + panic!("allocation failure"); + } + p +} + +unsafe fn malloc() -> *mut T { + let size = mem::size_of::(); + /*if size == 0 { + return SENTINEL as *mut T; + }*/ + must_succeed(0 as *mut T) + /*must_succeed( + libc::memalign(mem::align_of::() as libc::size_t, size as libc::size_t) + ) as *mut T*/ +} + +pub struct CBox(*mut T); + +impl CBox { + pub const unsafe fn from_raw(p: *mut T) -> CBox { + CBox(p) + } + + pub fn into_raw(self) -> *mut T { + let p = self.0; + mem::forget(self); + p + } + + pub const fn as_ptr(&self) -> *const T { + self.0 + } + + pub fn as_mut_ptr(&mut self) -> *mut T { + self.0 + } +} + +impl CBox { + pub fn new(value: T) -> CBox { + unsafe { + let p = malloc(); + ptr::write(p, value); + CBox::from_raw(p) + } + } +} + +impl Drop for CBox { + fn drop(&mut self) { + let p = self.0 as *mut libc::c_void; + if p != SENTINEL { + unsafe { libc::free(p); } + } + } +} + +impl Deref for CBox { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.0 } + } +} + +impl DerefMut for CBox { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.0 } + } +} + +//pub struct FromBytesWithNulError {} + +pub struct CStr { inner: libc::c_char } + +impl CStr { + pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { + &*(p as *const CStr) + } + + pub unsafe fn from_ptr_mut<'a>(p: *mut libc::c_char) -> &'a mut CStr { + &mut *(p as *mut CStr) + } + + pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { + CStr::from_ptr(bytes.as_ptr() as *const libc::c_char) + } + + // TODO + //pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { + //} + + pub fn len(&self) -> usize { + unsafe { libc::strlen(self.as_ptr()) as usize } + } + + pub const fn as_ptr(&self) -> *const libc::c_char { + &self.inner + } + + pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { + &mut self.inner + } + + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts( + self.as_ptr() as *const u8, + self.len(), + ) } + } + + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut u8, + self.len(), + ) } + } + + pub fn to_owned(self: &CStr) -> CString { + CString::from(self) + } +} + +#[macro_export] +macro_rules! cstr { + ($s:expr) => ( + unsafe { $crate::c::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } + ) +} + +pub struct CString { inner: CBox } + +impl CString { + pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { + CString { inner: CBox::from_raw(p) } + } + + pub fn into_raw(self) -> *mut libc::c_char { + self.inner.into_raw() + } +} + +impl Deref for CString { + type Target = CStr; + + fn deref(&self) -> &CStr { + unsafe { CStr::from_ptr(&*self.inner) } + } +} + +impl DerefMut for CString { + fn deref_mut(&mut self) -> &mut CStr { + unsafe { CStr::from_ptr_mut(&mut *self.inner) } + } +} + +impl From<&[u8]> for CString { + fn from(s: &[u8]) -> CString { + unsafe { + CString::from_raw(must_succeed(posix::strndup( + s.as_ptr() as *const libc::c_char, + s.len() as libc::size_t, + ))) + } + } +} + +impl From<&str> for CString { + fn from(s: &str) -> CString { + CString::from(s.as_bytes()) + } +} + +impl From<&CStr> for CString { + fn from(s: &CStr) -> CString { + unsafe { + CString::from_raw(must_succeed(libc::strdup(s.as_ptr()))) + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7cdfb3f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,19 @@ +#![no_main] +#![no_std] + +extern crate libc; + +mod c; + +#[no_mangle] +pub extern fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { + let foo = cstr!("Foo! %p\n"); + let x = c::string::CBox::new(()); + unsafe { libc::printf(foo.as_ptr(), &*x as *const ()); } + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + unsafe { libc::abort() } +} From bcef77a7fb3b73d2a2fbcea51012014b62755bb5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 18:42:01 +0200 Subject: [PATCH 07/29] Switch to a JSON-based config format We can get rid of a lot of code by ditching out INI format parser. We also remove the support for "subtype" and "generator" templates for now; rather than implementing this in NeCo itself, templates could be implemented as a Lua DSL later. --- meson.build | 1 + src/config-ini.c | 140 ----------------------------------------- src/config-ini.h | 24 ------- src/config-load.c | 110 +++----------------------------- src/config-process.c | 121 +++++++---------------------------- src/config-process.h | 13 +--- src/device-bridge.c | 4 +- src/device-interface.c | 117 ++++++++++++++++++---------------- src/device.h | 5 +- src/keywords.c | 31 --------- src/keywords.def | 9 --- src/keywords.h | 12 ---- src/meson.build | 5 +- src/util.h | 26 ++++++++ 14 files changed, 128 insertions(+), 490 deletions(-) delete mode 100644 src/config-ini.c delete mode 100644 src/config-ini.h delete mode 100644 src/keywords.c delete mode 100644 src/keywords.def delete mode 100644 src/keywords.h diff --git a/meson.build b/meson.build index d7a554a..0cbd657 100644 --- a/meson.build +++ b/meson.build @@ -3,6 +3,7 @@ project('neco', 'c', default_options : ['c_std=gnu11']) cc = meson.get_compiler('c') libubox_dep = cc.find_library('ubox') +libjson_c_dep = dependency('json-c', method : 'pkg-config') libmnl_dep = dependency('libmnl', method : 'pkg-config') subdir('src') diff --git a/src/config-ini.c b/src/config-ini.c deleted file mode 100644 index 0461e13..0000000 --- a/src/config-ini.c +++ /dev/null @@ -1,140 +0,0 @@ -#include "config-ini.h" - -#include -#include -#include -#include - -static void free_field(ini_field_t *field) { - free(field->key); - free(field->value); - free(field); -} - -static void free_section(ini_section_t *section) { - ini_field_t *field, *tmp; - - list_for_each_entry_safe(field, tmp, §ion->fields, node) - free_field(field); - - free(section->name); - free(section); -} - -void free_ini_file(ini_file_t *file) { - ini_section_t *section, *tmp; - - list_for_each_entry_safe(section, tmp, &file->sections, node) - free_section(section); - - free(file); -} - -static bool add_field(ini_section_t *section, const char *key, const char *value) { - ini_field_t *field = calloc(1, sizeof(*field)); - if (!field) - return false; - - field->key = strdup(key); - field->value = strdup(value); - - if (!field->key || !field->value) { - free_field(field); - return false; - } - - list_add_tail(&field->node, §ion->fields); - - return true; -} - -static ini_section_t * add_section(ini_file_t *file, const char *name) { - ini_section_t *section = calloc(1, sizeof(*section)); - if (!section) - return NULL; - - section->name = strdup(name); - if (!section->name) { - free(section); - return false; - } - - INIT_LIST_HEAD(§ion->fields); - list_add_tail(§ion->node, &file->sections); - - return section; -} - -ini_file_t * read_ini_file(FILE *f) { - ini_file_t *file = calloc(1, sizeof(*file)); - if (!file) - goto error; - - INIT_LIST_HEAD(&file->sections); - - ini_section_t *section = NULL; - int err = 0; - - char *line = NULL; - size_t n = 0; - - while (getline(&line, &n, f) >= 0) { - char *input = line; - - while (isspace(input[0])) - input++; - - if (input[0] == '#' || input[0] == '\0') - continue; - - size_t len = strlen(input); - - while (isspace(input[len-1])) - len--; - - if (input[0] == '[') { - if (input[len-1] != ']') { - err = EINVAL; - goto error; - } - - input[len-1] = '\0'; - - section = add_section(file, input+1); - if (!section) - goto error; - } else { - if (!section) { - err = EINVAL; - goto error; - } - - input[len] = '\0'; - - char *delim = strchr(input, '='); - if (!delim) { - err = EINVAL; - goto error; - } - - *delim = '\0'; - - if (!add_field(section, input, delim+1)) - goto error; - } - } - - if (ferror(f)) - err = EIO; - -error: - free(line); - - if (err) { - free_ini_file(file); - errno = err; - return NULL; - } - - return file; -} diff --git a/src/config-ini.h b/src/config-ini.h deleted file mode 100644 index 48400fd..0000000 --- a/src/config-ini.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include - -typedef struct _ini_field { - struct list_head node; - char *key; - char *value; -} ini_field_t; - -typedef struct _ini_section { - struct list_head node; - struct list_head fields; - char *name; -} ini_section_t; - -typedef struct _ini_file { - struct list_head sections; -} ini_file_t; - -ini_file_t * read_ini_file(FILE *f); -void free_ini_file(ini_file_t *file); diff --git a/src/config-load.c b/src/config-load.c index 63a87b4..19b9d3a 100644 --- a/src/config-load.c +++ b/src/config-load.c @@ -14,110 +14,16 @@ #include #include -typedef struct _load_ctx { - struct avl_tree subtypes; - struct avl_tree devices; -} load_ctx_t; - -static const char * extname(const char *filename) { - const char *dot = strrchr(filename, '.'); - return dot ? (dot+1) : NULL; -} - -static bool isfile(int fd) { - struct stat buf; - if (fstat(fd, &buf) < 0) - return false; - - return (buf.st_mode & S_IFMT) == S_IFREG; -} - -static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) { - const char *ext = extname(filename); - if (!ext) - return true; - - struct avl_tree *target_tree; - if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0) - target_tree = &ctx->subtypes; - else if (get_device_type(ext)) - target_tree = &ctx->devices; - else - return true; - - int fd = openat(dirfd, filename, O_RDONLY); - if (fd < 0) - return false; - - if (!isfile(fd)) { - close(fd); - return true; - } - - FILE *f = fdopen(fd, "r"); - if (!f) { - close(fd); - return false; - } - - ini_file_t *data = read_ini_file(f); - fclose(f); - - if (!data) - return false; - - config_object_t *obj = calloc(1, sizeof(*obj)); - if (!obj) { - free_ini_file(data); - return false; - } - obj->data = data; - - char *name = strndup(filename, (ext - filename) - 1); - if (!name) { - config_object_free(obj); - return false; - } - NODE_NAME(obj) = name; - - obj->type = strdup(ext); - if (!obj->type) { - config_object_free(obj); - return false; - } - - avl_insert(target_tree, &obj->node); - - return true; -} - -static bool read_config_dir(load_ctx_t *ctx, const char *path) { - DIR *dir = opendir(path); - if (!dir) - return false; - - int fd = dirfd(dir); - - struct dirent *ent; - while ((ent = readdir(dir)) != NULL) - read_config_file(ctx, fd, ent->d_name); - - closedir(dir); - - return true; -} - bool read_config(const char *path) { - load_ctx_t ctx; - avl_init(&ctx.subtypes, avl_strcmp, true, NULL); - avl_init(&ctx.devices, avl_strcmp, true, NULL); - - bool ret = read_config_dir(&ctx, path); + struct json_object *config = json_object_from_file(path); + if (!config) + return false; - struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes); - struct avl_tree *devices = config_process_devices(&ctx.devices); + struct avl_tree *devices = config_process(config); + json_object_put(config); - free(subtypes); + if (!devices) + return false; device_t *dev, *tmp; avl_for_each_element(devices, dev, node) @@ -131,5 +37,5 @@ bool read_config(const char *path) { free(devices); - return ret; + return true; } diff --git a/src/config-process.c b/src/config-process.c index e71b228..a0671cb 100644 --- a/src/config-process.c +++ b/src/config-process.c @@ -1,6 +1,5 @@ #include "config-process.h" #include "device.h" -#include "keywords.h" #include "util.h" #include @@ -9,6 +8,7 @@ #include #include + typedef struct _process_ctx { struct avl_tree *ret; } process_ctx_t; @@ -17,52 +17,28 @@ typedef struct _config_subtype { struct avl_node node; } config_subtype_t; -void config_object_free(config_object_t *obj) { - free_ini_file(obj->data); - free(obj->type); - free(NODE_NAME(obj)); - free(obj); -} -static bool subtype_supported(const process_ctx_t *ctx, const char *type) { - switch (lookup_keyword(type)) { - case KW_Properties: - case KW_Generate: - case KW_Static: - case KW_DHCP: - case KW_DHCPv6: - return true; +static device_t * config_process_device(const char *name, struct json_object *obj) { + const char *typename = NULL; + struct json_object *device = neco_json_get_value(obj, "device", json_type_object); + if (device) + typename = neco_json_get_string(obj, "type"); - default: - return avl_find(ctx->ret, type); - } -} - -static config_subtype_t * config_process_subtype(const process_ctx_t *ctx, config_object_t *obj) { - ini_section_t *section; - list_for_each_entry(section, &obj->data->sections, node) { - printf("%s %s %i\n", NODE_NAME(obj), section->name, subtype_supported(ctx, section->name)); - - if (!subtype_supported(ctx, section->name)) - return NULL; - } - - config_subtype_t *ret = calloc(1, sizeof(*ret)); - if (!ret) + const device_type_t *type = get_device_type(typename ?: "interface"); + if (!type) return NULL; - char *name = strdup(NODE_NAME(obj)); - if (!name) { - free(ret); - return NULL; - } - - NODE_NAME(ret) = name; - - return ret; + return type->process_config(name, obj); } -struct avl_tree * config_process_subtypes(struct avl_tree *sub) { +struct avl_tree * config_process(struct json_object *config) { + if (!json_object_is_type(config, json_type_object)) + return NULL; + + struct json_object *devices = neco_json_get_value(config, "devices", json_type_object); + if (!devices) + return NULL; + process_ctx_t ctx; ctx.ret = calloc(1, sizeof(*ctx.ret)); if (!ctx.ret) @@ -70,65 +46,12 @@ struct avl_tree * config_process_subtypes(struct avl_tree *sub) { avl_init(ctx.ret, avl_strcmp, false, NULL); - while (true) { - size_t processed = 0; + json_object_object_foreach(devices, name, obj) { + device_t *device = config_process_device(name, obj); + if (!device) + continue; - config_object_t *obj, *tmp; - avl_for_each_element_safe(sub, obj, node, tmp) { - config_subtype_t *t = config_process_subtype(&ctx, obj); - if (!t) - continue; - - avl_delete(sub, &obj->node); - config_object_free(obj); - - avl_insert(ctx.ret, &t->node); - - processed++; - } - - if (!processed) - break; - } - - return ctx.ret; -} - -static device_t * config_process_device(config_object_t *obj) { - const device_type_t *type = get_device_type(obj->type); - - assert(type != NULL); - - return type->process_config(NODE_NAME(obj), obj->data); -} - -struct avl_tree * config_process_devices(struct avl_tree *devices) { - process_ctx_t ctx; - ctx.ret = calloc(1, sizeof(*ctx.ret)); - if (!ctx.ret) - return NULL; - - avl_init(ctx.ret, avl_strcmp, false, NULL); - - while (true) { - size_t processed = 0; - - config_object_t *obj, *tmp; - avl_for_each_element_safe(devices, obj, node, tmp) { - device_t *device = config_process_device(obj); - if (!device) - continue; - - avl_delete(devices, &obj->node); - config_object_free(obj); - - avl_insert(ctx.ret, &device->node); - - processed++; - } - - if (!processed) - break; + avl_insert(ctx.ret, &device->node); } return ctx.ret; diff --git a/src/config-process.h b/src/config-process.h index 501e765..3903d78 100644 --- a/src/config-process.h +++ b/src/config-process.h @@ -1,15 +1,6 @@ #pragma once -#include "config-ini.h" - +#include #include -typedef struct _config_object { - struct avl_node node; - char *type; - ini_file_t *data; -} config_object_t; - -void config_object_free(config_object_t *obj); -struct avl_tree * config_process_subtypes(struct avl_tree *sub); -struct avl_tree * config_process_devices(struct avl_tree *devices); +struct avl_tree * config_process(struct json_object *config); diff --git a/src/device-bridge.c b/src/device-bridge.c index f973dbf..4a8d24c 100644 --- a/src/device-bridge.c +++ b/src/device-bridge.c @@ -1,6 +1,8 @@ #include "device.h" -static device_t * bridge_process_config(const char *name, const ini_file_t *config) { +#include + +static device_t * bridge_process_config(const char *name, struct json_object *config) { printf("Bridge: %s\n", name); return NULL; } diff --git a/src/device-interface.c b/src/device-interface.c index a2aae14..ef85089 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,5 +1,4 @@ #include "device.h" -#include "keywords.h" #include "netlink.h" #include "util.h" #include "vector.h" @@ -109,22 +108,43 @@ static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_ho return true; } -static bool process_section_device(device_interface_t *iface, const ini_section_t *section) { - ini_field_t *field; +static void interface_free(device_t *dev) { + device_interface_t *iface = container_of(dev, device_interface_t, device); - list_for_each_entry(field, §ion->fields, node) { - switch (lookup_keyword(field->key)) { - default: - fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key); - } - } + VECTOR_FREE(iface->addrs); + free(NODE_NAME(dev)); + free(iface); +} +static bool process_section_device(device_interface_t *iface, struct json_object *section) { return true; } -static bool process_section_static(device_interface_t *iface, const ini_section_t *section) { - ini_field_t *field; +static bool process_section_static(device_interface_t *iface, struct json_object *section) { + struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array); + if (addresses) { + for (size_t i = 0; i < json_object_array_length(addresses); i++) { + struct json_object *address = json_object_array_get_idx(addresses, i); + if (!json_object_is_type(address, json_type_string)) { + fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address))); + continue; + } + ipaddr_prefix_t p; + if (!parse_prefix(&p, json_object_get_string(address), true)) { + fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address)); + break; + } + + if (!VECTOR_ADD(iface->addrs, p)) { + fprintf(stderr, "interface: static: adding address failed\n"); + return false; + } + } + } + + + /* list_for_each_entry(field, §ion->fields, node) { switch (lookup_keyword(field->key)) { case KW_Address: { @@ -146,16 +166,42 @@ static bool process_section_static(device_interface_t *iface, const ini_section_ fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key); } } + */ return true; } -static void interface_free(device_t *dev) { - device_interface_t *iface = container_of(dev, device_interface_t, device); +static device_t * interface_process_config(const char *name, struct json_object *config) { + device_interface_t *iface = calloc(1, sizeof(*iface)); + if (!iface) + return NULL; - VECTOR_FREE(iface->addrs); - free(NODE_NAME(dev)); - free(iface); + device_t *dev = &iface->device; + dev->type = &device_type_interface; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); + if (sec_device) { + if (!process_section_device(iface, sec_device)) + goto err; + } + + struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); + if (sec_static) { + if (!process_section_static(iface, sec_static)) + goto err; + } + + return dev; + +err: + interface_free(dev); + return NULL; } static bool interface_set_link_flags(unsigned index, unsigned change, unsigned flags) { @@ -281,45 +327,6 @@ static void interface_release(device_t *dev) { interface_set_link_state(index, false); } -static device_t * interface_process_config(const char *name, const ini_file_t *config) { - device_interface_t *iface = calloc(1, sizeof(*iface)); - if (!iface) - return NULL; - - device_t *dev = &iface->device; - dev->type = &device_type_interface; - - NODE_NAME(dev) = strdup(name); - if (!NODE_NAME(dev)) { - free(iface); - return NULL; - } - - const ini_section_t *section; - list_for_each_entry(section, &config->sections, node) { - switch (lookup_keyword(section->name)) { - case KW_Device: - if (!process_section_device(iface, section)) - goto err; - break; - - case KW_Static: - if (!process_section_static(iface, section)) - goto err; - break; - - default: - fprintf(stderr, "interface: unknown section %s\n", section->name); - } - } - - return dev; - -err: - interface_free(dev); - return NULL; -} - static device_type_t device_type_interface = { .process_config = interface_process_config, .free = interface_free, diff --git a/src/device.h b/src/device.h index 33acfbb..e44c695 100644 --- a/src/device.h +++ b/src/device.h @@ -1,7 +1,6 @@ #pragma once -#include "config-ini.h" - +#include #include extern struct avl_tree device_types; @@ -15,7 +14,7 @@ typedef struct _device { struct _device_type { struct avl_node node; - device_t * (*process_config)(const char *name, const ini_file_t *config); + device_t * (*process_config)(const char *name, struct json_object *config); void (*free)(device_t *device); void (*init)(device_t *device); diff --git a/src/keywords.c b/src/keywords.c deleted file mode 100644 index 12393bd..0000000 --- a/src/keywords.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "keywords.h" - -#include - -#include -#include - -static const char *const keywords[] = { - -#define KW(kw) #kw, -#include "keywords.def" -#undef KW - -}; - -static int compare_keywords(const void *a, const void *b) { - const char *const *ea = a, *const *eb = b; - return strcmp(*ea, *eb); -} - -keyword_t lookup_keyword(const char *keyword) { - const char *const *entry = bsearch( - &keyword, keywords, ARRAY_SIZE(keywords), sizeof(const char *), - compare_keywords - ); - - if (!entry) - return UNKNOWN_KEYWORD; - - return (keyword_t) (entry - keywords + 1); -} diff --git a/src/keywords.def b/src/keywords.def deleted file mode 100644 index 0dbd866..0000000 --- a/src/keywords.def +++ /dev/null @@ -1,9 +0,0 @@ -/* Config section and field names; keep sorted! */ - -KW(Address) -KW(DHCP) -KW(DHCPv6) -KW(Device) -KW(Generate) -KW(Properties) -KW(Static) diff --git a/src/keywords.h b/src/keywords.h deleted file mode 100644 index 05d809e..0000000 --- a/src/keywords.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -typedef enum _keyword { - UNKNOWN_KEYWORD = 0, - -#define KW(kw) KW_##kw, -#include "keywords.def" -#undef KW - -} keyword_t; - -keyword_t lookup_keyword(const char *keyword); diff --git a/src/meson.build b/src/meson.build index 9056cf3..b359ab4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,19 +1,18 @@ src = [ 'neco.c', - 'config-ini.c', 'config-load.c', 'config-process.c', 'device.c', 'device-bridge.c', 'device-interface.c', - 'keywords.c', 'netlink.c', 'vector.c', ] dep = [ - libubox_dep, + libjson_c_dep, libmnl_dep, + libubox_dep, ] executable('neco', sources: src, dependencies: dep) diff --git a/src/util.h b/src/util.h index 31b13b6..2e60467 100644 --- a/src/util.h +++ b/src/util.h @@ -1,3 +1,29 @@ #pragma once +#include + + #define NODE_NAME(c) (*(char **)&(c)->node.key) + + +static inline struct json_object * neco_json_get_value( + struct json_object *obj, + const char *key, + enum json_type type +) { + struct json_object *value; + if (!json_object_object_get_ex(obj, key, &value)) + return NULL; + if (!json_object_is_type(value, type)) + return NULL; + + return value; +} + +static inline const char * neco_json_get_string(struct json_object *obj, const char *key) { + struct json_object *value = neco_json_get_value(obj, key, json_type_string); + if (!value) + return NULL; + + return json_object_get_string(value); +} From 7745aa34ad3fd114a0e3d0f89eef145c4736c5f3 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 20:40:13 +0200 Subject: [PATCH 08/29] config: fix resolve of device type --- src/config-process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config-process.c b/src/config-process.c index a0671cb..c1634d5 100644 --- a/src/config-process.c +++ b/src/config-process.c @@ -22,7 +22,7 @@ static device_t * config_process_device(const char *name, struct json_object *ob const char *typename = NULL; struct json_object *device = neco_json_get_value(obj, "device", json_type_object); if (device) - typename = neco_json_get_string(obj, "type"); + typename = neco_json_get_string(device, "type"); const device_type_t *type = get_device_type(typename ?: "interface"); if (!type) From f43788c9473480e127118ec0e614b9faa7eea2e2 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 21:56:08 +0200 Subject: [PATCH 09/29] device: split out common device settings --- src/device-common.c | 121 +++++++++++++++++++++++++++++ src/device-common.h | 31 ++++++++ src/device-interface.c | 170 +++-------------------------------------- src/meson.build | 1 + 4 files changed, 162 insertions(+), 161 deletions(-) create mode 100644 src/device-common.c create mode 100644 src/device-common.h diff --git a/src/device-common.c b/src/device-common.c new file mode 100644 index 0000000..4a10bf5 --- /dev/null +++ b/src/device-common.c @@ -0,0 +1,121 @@ +#include "device-common.h" +#include "util.h" + +#include +#include +#include +#include +#include + +#include + +static unsigned long strtoul_safe(const char *str) { + char *endptr; + + errno = 0; + unsigned long val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr) + errno = EINVAL; + + return val; +} + +static bool parse_address(ipaddr_t *addr, const char *str) { + if (inet_pton(AF_INET, str, &addr->addr4)) { + addr->af = AF_INET; + return true; + } + + if (inet_pton(AF_INET6, str, &addr->addr6)) { + addr->af = AF_INET6; + return true; + } + + return false; +} + +static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) { + const char *slash = strrchr(str, '/'); + if (!slash) + return false; + + size_t len = slash - str; + char buf[len+1]; + memcpy(buf, str, len); + buf[len] = 0; + + if (!parse_address(&prefix->addr, buf)) + return false; + + long plen = strtoul_safe(slash + 1); + if (errno) + return false; + + switch (prefix->addr.af) { + case AF_INET: + if (plen > 32) + return false; + break; + + case AF_INET6: + if (plen > 128) + return false; + break; + + default: + assert(false); + __builtin_unreachable(); + } + + prefix->plen = plen; + + // TODO: Implement allow_host + + return true; +} + +static bool process_section_device(device_common_t *device, struct json_object *section) { + return true; +} + +static bool process_section_static(device_common_t *device, struct json_object *section) { + struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array); + if (addresses) { + for (size_t i = 0; i < json_object_array_length(addresses); i++) { + struct json_object *address = json_object_array_get_idx(addresses, i); + if (!json_object_is_type(address, json_type_string)) { + fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address))); + continue; + } + + ipaddr_prefix_t p; + if (!parse_prefix(&p, json_object_get_string(address), true)) { + fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address)); + break; + } + + if (!VECTOR_ADD(device->addrs, p)) { + fprintf(stderr, "interface: static: adding address failed\n"); + return false; + } + } + } + + return true; +} + +bool device_common_process_config(device_common_t *device, struct json_object *config) { + struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); + if (sec_device) { + if (!process_section_device(device, sec_device)) + return false; + } + + struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); + if (sec_static) { + if (!process_section_static(device, sec_static)) + return false; + } + + return true; +} diff --git a/src/device-common.h b/src/device-common.h new file mode 100644 index 0000000..ff27961 --- /dev/null +++ b/src/device-common.h @@ -0,0 +1,31 @@ +#pragma once + +#include "device.h" +#include "vector.h" + +#include +#include + +typedef struct _ipaddr { + int af; + union { + struct in_addr addr4; + struct in6_addr addr6; + }; +} ipaddr_t; + +typedef struct _ipprefix { + ipaddr_t addr; + uint8_t plen; +} ipaddr_prefix_t; + +typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t; + +typedef struct _device_common { + struct ether_addr macaddr; + uint16_t mtu; + + ipaddr_prefix_vector_t addrs; +} device_common_t; + +bool device_common_process_config(device_common_t *device, struct json_object *config); diff --git a/src/device-interface.c b/src/device-interface.c index ef85089..aa37940 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,17 +1,13 @@ -#include "device.h" +#include "device-common.h" #include "netlink.h" #include "util.h" #include "vector.h" -#include #include #include #include -#include #include -#include -#include #include #include @@ -19,158 +15,19 @@ static device_type_t device_type_interface; -typedef struct _ipaddr { - int af; - union { - struct in_addr addr4; - struct in6_addr addr6; - }; -} ipaddr_t; - -typedef struct _ipprefix { - ipaddr_t addr; - uint8_t plen; -} ipaddr_prefix_t; - -typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t; - typedef struct _device_interface { device_t device; - - struct ether_addr macaddr; - uint16_t mtu; - - ipaddr_prefix_vector_t addrs; + device_common_t common; } device_interface_t; -static unsigned long strtoul_safe(const char *str) { - char *endptr; - - errno = 0; - unsigned long val = strtoul(str, &endptr, 10); - if (endptr == str || *endptr) - errno = EINVAL; - - return val; -} - -static bool parse_address(ipaddr_t *addr, const char *str) { - if (inet_pton(AF_INET, str, &addr->addr4)) { - addr->af = AF_INET; - return true; - } - - if (inet_pton(AF_INET6, str, &addr->addr6)) { - addr->af = AF_INET6; - return true; - } - - return false; -} - -static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) { - const char *slash = strrchr(str, '/'); - if (!slash) - return false; - - size_t len = slash - str; - char buf[len+1]; - memcpy(buf, str, len); - buf[len] = 0; - - if (!parse_address(&prefix->addr, buf)) - return false; - - long plen = strtoul_safe(slash + 1); - if (errno) - return false; - - switch (prefix->addr.af) { - case AF_INET: - if (plen > 32) - return false; - break; - - case AF_INET6: - if (plen > 128) - return false; - break; - - default: - assert(false); - __builtin_unreachable(); - } - - prefix->plen = plen; - - // TODO: Implement allow_host - - return true; -} - static void interface_free(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); - VECTOR_FREE(iface->addrs); + VECTOR_FREE(iface->common.addrs); free(NODE_NAME(dev)); free(iface); } -static bool process_section_device(device_interface_t *iface, struct json_object *section) { - return true; -} - -static bool process_section_static(device_interface_t *iface, struct json_object *section) { - struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array); - if (addresses) { - for (size_t i = 0; i < json_object_array_length(addresses); i++) { - struct json_object *address = json_object_array_get_idx(addresses, i); - if (!json_object_is_type(address, json_type_string)) { - fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address))); - continue; - } - - ipaddr_prefix_t p; - if (!parse_prefix(&p, json_object_get_string(address), true)) { - fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address)); - break; - } - - if (!VECTOR_ADD(iface->addrs, p)) { - fprintf(stderr, "interface: static: adding address failed\n"); - return false; - } - } - } - - - /* - list_for_each_entry(field, §ion->fields, node) { - switch (lookup_keyword(field->key)) { - case KW_Address: { - ipaddr_prefix_t p; - if (!parse_prefix(&p, field->value, true)) { - fprintf(stderr, "interface: [Static]: unable to parse Address %s\n", field->value); - break; - } - - if (!VECTOR_ADD(iface->addrs, p)) { - fprintf(stderr, "interface: [Static]: adding address failed\n"); - return false; - } - - break; - } - - default: - fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key); - } - } - */ - - return true; -} - static device_t * interface_process_config(const char *name, struct json_object *config) { device_interface_t *iface = calloc(1, sizeof(*iface)); if (!iface) @@ -185,17 +42,8 @@ static device_t * interface_process_config(const char *name, struct json_object return NULL; } - struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); - if (sec_device) { - if (!process_section_device(iface, sec_device)) - goto err; - } - - struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object); - if (sec_static) { - if (!process_section_static(iface, sec_static)) - goto err; - } + if (!device_common_process_config(&iface->common, config)) + goto err; return dev; @@ -307,8 +155,8 @@ static void interface_init(device_t *dev) { interface_set_link_state(index, true); - for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), true); + for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), true); } static void interface_update(device_t *dev) { @@ -321,8 +169,8 @@ static void interface_release(device_t *dev) { if (!index) return; - for (size_t i = 0; i < VECTOR_LEN(iface->addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->addrs, i), false); + for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) + interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), false); interface_set_link_state(index, false); } diff --git a/src/meson.build b/src/meson.build index b359ab4..3cc830c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,6 +4,7 @@ src = [ 'config-process.c', 'device.c', 'device-bridge.c', + 'device-common.c', 'device-interface.c', 'netlink.c', 'vector.c', From 8b5af11ebe1211861b422cc6737219c28594eecd Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 1 Jun 2019 12:30:14 +0200 Subject: [PATCH 10/29] device: move more common functions --- src/device-common.c | 112 +++++++++++++++++++++++++++++++++++++++ src/device-common.h | 3 ++ src/device-interface.c | 117 +++-------------------------------------- 3 files changed, 121 insertions(+), 111 deletions(-) diff --git a/src/device-common.c b/src/device-common.c index 4a10bf5..9e7605e 100644 --- a/src/device-common.c +++ b/src/device-common.c @@ -1,4 +1,5 @@ #include "device-common.h" +#include "netlink.h" #include "util.h" #include @@ -9,6 +10,10 @@ #include +#include +#include +#include + static unsigned long strtoul_safe(const char *str) { char *endptr; @@ -119,3 +124,110 @@ bool device_common_process_config(device_common_t *device, struct json_object *c return true; } + +static bool device_common_set_link_flags(unsigned ifindex, unsigned change, unsigned flags) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_SETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_index = ifindex; + ifi->ifi_family = AF_UNSPEC; + + ifi->ifi_change = change; + ifi->ifi_flags = flags; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static bool device_common_set_link_state(unsigned ifindex, bool up) { + return device_common_set_link_flags(ifindex, IFF_UP, up ? IFF_UP : 0); +} + +static bool device_common_set_ipaddr(unsigned ifindex, ipaddr_prefix_t *addr, bool add) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + if (add) + nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; + + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa)); + ifa->ifa_index = ifindex; + ifa->ifa_family = addr->addr.af; + + ifa->ifa_prefixlen = addr->plen; + + switch (addr->addr.af) { + case AF_INET: + mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4); + break; + + case AF_INET6: + mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6); + break; + + default: + errno = EINVAL; + return false; + } + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} +void device_common_init(device_common_t *device, int ifindex) { + device_common_set_link_state(ifindex, true); + + for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) + device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), true); +} + +void device_common_release(device_common_t *device, int ifindex) { + for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) + device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false); + + device_common_set_link_state(ifindex, false); +} diff --git a/src/device-common.h b/src/device-common.h index ff27961..398c5a1 100644 --- a/src/device-common.h +++ b/src/device-common.h @@ -29,3 +29,6 @@ typedef struct _device_common { } device_common_t; bool device_common_process_config(device_common_t *device, struct json_object *config); + +void device_common_init(device_common_t *device, int ifindex); +void device_common_release(device_common_t *device, int ifindex); diff --git a/src/device-interface.c b/src/device-interface.c index aa37940..1add2b2 100644 --- a/src/device-interface.c +++ b/src/device-interface.c @@ -1,5 +1,4 @@ #include "device-common.h" -#include "netlink.h" #include "util.h" #include "vector.h" @@ -9,10 +8,6 @@ #include -#include -#include -#include - static device_type_t device_type_interface; typedef struct _device_interface { @@ -52,111 +47,14 @@ err: return NULL; } -static bool interface_set_link_flags(unsigned index, unsigned change, unsigned flags) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - - struct mnl_socket *nl = nl_socket(); - - struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = RTM_SETLINK; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - int seq = nlh->nlmsg_seq = nl_seq(); - - struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); - ifi->ifi_index = index; - ifi->ifi_family = AF_UNSPEC; - - ifi->ifi_change = change; - ifi->ifi_flags = flags; - - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_sendto"); - return false; - } - - int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - if (ret == -1) { - perror("mnl_socket_recvfrom"); - return false; - } - - ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); - if (ret == -1) { - perror("mnl_cb_run"); - return false; - } - - return true; -} - -static bool interface_set_link_state(unsigned index, bool up) { - return interface_set_link_flags(index, IFF_UP, up ? IFF_UP : 0); -} - -static bool interface_set_ipaddr(unsigned index, ipaddr_prefix_t *addr, bool add) { - char buf[MNL_SOCKET_BUFFER_SIZE]; - - struct mnl_socket *nl = nl_socket(); - - struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); - nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - if (add) - nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; - - int seq = nlh->nlmsg_seq = nl_seq(); - - struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa)); - ifa->ifa_index = index; - ifa->ifa_family = addr->addr.af; - - ifa->ifa_prefixlen = addr->plen; - - switch (addr->addr.af) { - case AF_INET: - mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4); - break; - - case AF_INET6: - mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6); - break; - - default: - errno = EINVAL; - return false; - } - - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - perror("mnl_socket_sendto"); - return false; - } - - int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - if (ret == -1) { - perror("mnl_socket_recvfrom"); - return false; - } - - ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); - if (ret == -1) { - perror("mnl_cb_run"); - return false; - } - - return true; -} - static void interface_init(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); - unsigned index = if_nametoindex(NODE_NAME(dev)); - if (!index) + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) return; - interface_set_link_state(index, true); - - for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), true); + device_common_init(&iface->common, ifindex); } static void interface_update(device_t *dev) { @@ -165,14 +63,11 @@ static void interface_update(device_t *dev) { static void interface_release(device_t *dev) { device_interface_t *iface = container_of(dev, device_interface_t, device); - unsigned index = if_nametoindex(NODE_NAME(dev)); - if (!index) + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) return; - for (size_t i = 0; i < VECTOR_LEN(iface->common.addrs); i++) - interface_set_ipaddr(index, &VECTOR_INDEX(iface->common.addrs, i), false); - - interface_set_link_state(index, false); + device_common_release(&iface->common, ifindex); } static device_type_t device_type_interface = { From 8d15854eb57c8a16523a986aa72e3a2c993a8c50 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 1 Jun 2019 14:28:03 +0200 Subject: [PATCH 11/29] device: bridge: implement initial bridge setup --- src/device-bridge.c | 122 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/src/device-bridge.c b/src/device-bridge.c index 4a8d24c..11b24b0 100644 --- a/src/device-bridge.c +++ b/src/device-bridge.c @@ -1,14 +1,128 @@ -#include "device.h" +#include "device-common.h" +#include "netlink.h" +#include "util.h" -#include +#include + +#include + +#include +#include +#include + +static device_type_t device_type_bridge; + +typedef struct _device_bridge { + device_t device; + device_common_t common; +} device_bridge_t; + +static void bridge_free(device_t *dev) { + device_bridge_t *iface = container_of(dev, device_bridge_t, device); + + VECTOR_FREE(iface->common.addrs); + free(NODE_NAME(dev)); + free(iface); +} static device_t * bridge_process_config(const char *name, struct json_object *config) { - printf("Bridge: %s\n", name); + device_bridge_t *iface = calloc(1, sizeof(*iface)); + if (!iface) + return NULL; + + device_t *dev = &iface->device; + dev->type = &device_type_bridge; + + NODE_NAME(dev) = strdup(name); + if (!NODE_NAME(dev)) { + free(iface); + return NULL; + } + + if (!device_common_process_config(&iface->common, config)) + goto err; + + return dev; + +err: + bridge_free(dev); return NULL; } -device_type_t device_type_bridge = { +static bool bridge_nl_create(device_bridge_t *bridge) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_family = AF_UNSPEC; + + mnl_attr_put_str(nlh, IFLA_IFNAME, NODE_NAME(&bridge->device)); + + struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); + mnl_attr_put_str(nlh, IFLA_INFO_KIND, "bridge"); + mnl_attr_nest_end(nlh, linkinfo); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + +static void bridge_init(device_t *dev) { + device_bridge_t *bridge = container_of(dev, device_bridge_t, device); + + if (!bridge_nl_create(bridge)) + return; + + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) + return; + + device_common_init(&bridge->common, ifindex); +} + +static void bridge_update(device_t *dev) { +} + +static void bridge_release(device_t *dev) { + /* + device_bridge_t *bridge = container_of(dev, device_bridge_t, device); + + unsigned ifindex = if_nametoindex(NODE_NAME(dev)); + if (!ifindex) + return; + + device_common_release(&bridge->common, ifindex); + */ +} + +static device_type_t device_type_bridge = { .process_config = bridge_process_config, + .free = bridge_free, + + .init = bridge_init, + .update = bridge_update, + .release = bridge_release, }; __attribute__((constructor)) From 29f3ef36af7bb387a51fe8ad968d9770d3b54a6d Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 1 Jun 2019 19:10:17 +0200 Subject: [PATCH 12/29] device: add bridge port configuration --- src/device-common.c | 74 ++++++++++++++++++++++++++++++++++++++++++--- src/device-common.h | 2 ++ 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/device-common.c b/src/device-common.c index 9e7605e..1dc4490 100644 --- a/src/device-common.c +++ b/src/device-common.c @@ -109,6 +109,14 @@ static bool process_section_static(device_common_t *device, struct json_object * return true; } +static bool process_section_bridge(device_common_t *device, struct json_object *section) { + const char *master = neco_json_get_string(section, "master"); + if (master) + strncpy(device->master, master, sizeof(device->master)-1); + + return true; +} + bool device_common_process_config(device_common_t *device, struct json_object *config) { struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object); if (sec_device) { @@ -122,6 +130,12 @@ bool device_common_process_config(device_common_t *device, struct json_object *c return false; } + struct json_object *sec_bridge = neco_json_get_value(config, "bridge", json_type_object); + if (sec_bridge) { + if (!process_section_bridge(device, sec_bridge)) + return false; + } + return true; } @@ -218,16 +232,68 @@ static bool device_common_set_ipaddr(unsigned ifindex, ipaddr_prefix_t *addr, bo return true; } + +static bool device_common_set_master(unsigned ifindex, unsigned master) { + char buf[MNL_SOCKET_BUFFER_SIZE]; + + struct mnl_socket *nl = nl_socket(); + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_SETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + int seq = nlh->nlmsg_seq = nl_seq(); + + struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi)); + ifi->ifi_index = ifindex; + ifi->ifi_family = AF_UNSPEC; + + mnl_attr_put_u32(nlh, IFLA_MASTER, master); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + perror("mnl_socket_sendto"); + return false; + } + + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + return false; + } + + ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL); + if (ret == -1) { + perror("mnl_cb_run"); + return false; + } + + return true; +} + void device_common_init(device_common_t *device, int ifindex) { + if (*device->master) { + unsigned master = if_nametoindex(device->master); + if (!master) + return; + + device_common_set_master(ifindex, master); + } + device_common_set_link_state(ifindex, true); - for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) - device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), true); + if (!*device->master) { + for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) + device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), true); + } } void device_common_release(device_common_t *device, int ifindex) { - for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) - device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false); + if (!*device->master) { + for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++) + device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false); + } device_common_set_link_state(ifindex, false); + + if (*device->master) + device_common_set_master(ifindex, 0); } diff --git a/src/device-common.h b/src/device-common.h index 398c5a1..dc0fe28 100644 --- a/src/device-common.h +++ b/src/device-common.h @@ -3,6 +3,7 @@ #include "device.h" #include "vector.h" +#include #include #include @@ -26,6 +27,7 @@ typedef struct _device_common { uint16_t mtu; ipaddr_prefix_vector_t addrs; + char master[IF_NAMESIZE]; } device_common_t; bool device_common_process_config(device_common_t *device, struct json_object *config); From 69c9e6031abc309bd801e798a0d5d530f1e893a7 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 9 Aug 2019 21:13:19 +0200 Subject: [PATCH 13/29] WIP --- src/c/mod.rs | 1 + src/c/stdio.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/c/string.rs | 16 ++++++++-------- src/main.rs | 13 +++++++++---- 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 src/c/stdio.rs diff --git a/src/c/mod.rs b/src/c/mod.rs index 1b643c4..3694561 100644 --- a/src/c/mod.rs +++ b/src/c/mod.rs @@ -1,2 +1,3 @@ pub mod posix; +pub mod stdio; pub mod string; diff --git a/src/c/stdio.rs b/src/c/stdio.rs new file mode 100644 index 0000000..aa824ed --- /dev/null +++ b/src/c/stdio.rs @@ -0,0 +1,38 @@ +use core::fmt; + +use super::posix; +use super::string; + +pub struct OStream { + file: *mut libc::FILE +} + +pub fn stdout() -> OStream { + OStream { file: unsafe { posix::stdout } } +} + +impl OStream { + pub fn write(&mut self, b: &[u8]) { + unsafe { + libc::fwrite( + b.as_ptr() as *const libc::c_void, + 1, + b.len(), + self.file, + ); + } + } + + pub fn puts(&mut self, s: &string::CStr) { + unsafe { + libc::fputs(s.as_ptr(), self.file); + } + } +} + +impl fmt::Write for OStream { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write(s.as_bytes()); + Ok(()) + } +} diff --git a/src/c/string.rs b/src/c/string.rs index 8babef6..d0e620c 100644 --- a/src/c/string.rs +++ b/src/c/string.rs @@ -11,15 +11,14 @@ fn must_succeed(p: *mut T) -> *mut T { p } -unsafe fn malloc() -> *mut T { +fn malloc() -> *mut T { let size = mem::size_of::(); - /*if size == 0 { + if size == 0 { return SENTINEL as *mut T; - }*/ - must_succeed(0 as *mut T) - /*must_succeed( - libc::memalign(mem::align_of::() as libc::size_t, size as libc::size_t) - ) as *mut T*/ + } + must_succeed( + unsafe {libc::memalign(mem::align_of::() as libc::size_t, size as libc::size_t) } + ) as *mut T } pub struct CBox(*mut T); @@ -46,8 +45,8 @@ impl CBox { impl CBox { pub fn new(value: T) -> CBox { + let p = malloc(); unsafe { - let p = malloc(); ptr::write(p, value); CBox::from_raw(p) } @@ -56,6 +55,7 @@ impl CBox { impl Drop for CBox { fn drop(&mut self) { + unsafe { ptr::drop_in_place(self.0); } let p = self.0 as *mut libc::c_void; if p != SENTINEL { unsafe { libc::free(p); } diff --git a/src/main.rs b/src/main.rs index 7cdfb3f..15a2640 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,16 @@ extern crate libc; mod c; +use core::fmt::Write; + #[no_mangle] -pub extern fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let foo = cstr!("Foo! %p\n"); - let x = c::string::CBox::new(()); - unsafe { libc::printf(foo.as_ptr(), &*x as *const ()); } +pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { + let x = c::string::CString::from("foo"); + let y = c::string::CBox::new(x); + //let foo = cstr!("Foo! %p\n"); + //c::stdio::stdout().puts(foo); + let mut stdout = c::stdio::stdout(); + writeln!(stdout, "Foo: {}", 42); 0 } From 709f755da61a02cbdb9586c677a0fa67c25536d0 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 9 Aug 2019 22:41:05 +0200 Subject: [PATCH 14/29] Update libc --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e536a5d..188cb22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,15 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.54" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "neco" version = "0.1.0" dependencies = [ - "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6" +"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" From 9aae18d76454b01f07b177efafcc9867dfa7dff1 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 9 Aug 2019 22:41:26 +0200 Subject: [PATCH 15/29] fixes --- src/c/string.rs | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/c/string.rs b/src/c/string.rs index d0e620c..eef6371 100644 --- a/src/c/string.rs +++ b/src/c/string.rs @@ -2,8 +2,6 @@ use super::posix; use core::{mem, ptr, slice}; use core::ops::{Deref, DerefMut}; -const SENTINEL: *mut libc::c_void = 1 as *mut libc::c_void; - fn must_succeed(p: *mut T) -> *mut T { if p.is_null() { panic!("allocation failure"); @@ -13,17 +11,28 @@ fn must_succeed(p: *mut T) -> *mut T { fn malloc() -> *mut T { let size = mem::size_of::(); + let align = mem::align_of::(); if size == 0 { - return SENTINEL as *mut T; + return align as *mut T; } must_succeed( - unsafe {libc::memalign(mem::align_of::() as libc::size_t, size as libc::size_t) } - ) as *mut T + unsafe { + libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T + } + ) } -pub struct CBox(*mut T); +pub struct CBox(*mut T); + +impl CBox { + pub fn new(value: T) -> CBox { + let p = malloc(); + unsafe { + ptr::write(p, value); + CBox::from_raw(p) + } + } -impl CBox { pub const unsafe fn from_raw(p: *mut T) -> CBox { CBox(p) } @@ -43,27 +52,18 @@ impl CBox { } } -impl CBox { - pub fn new(value: T) -> CBox { - let p = malloc(); - unsafe { - ptr::write(p, value); - CBox::from_raw(p) - } - } -} - -impl Drop for CBox { +impl Drop for CBox { fn drop(&mut self) { unsafe { ptr::drop_in_place(self.0); } - let p = self.0 as *mut libc::c_void; - if p != SENTINEL { - unsafe { libc::free(p); } + if mem::size_of::() != 0 { + unsafe { + libc::free(self.0 as *mut libc::c_void); + } } } } -impl Deref for CBox { +impl Deref for CBox { type Target = T; fn deref(&self) -> &T { @@ -71,7 +71,7 @@ impl Deref for CBox { } } -impl DerefMut for CBox { +impl DerefMut for CBox { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.0 } } From 52b498494534784c77bfa4a93706be40620ebd9a Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 4 Apr 2020 01:47:30 +0200 Subject: [PATCH 16/29] Lots of changes --- Cargo.lock | 8 +-- Cargo.toml | 2 +- src/c/posix.rs | 7 +-- src/c/stdio.rs | 8 ++- src/c/string.rs | 146 +++++++++++++++++++++++++++++++++++++----------- src/main.rs | 14 +++-- 6 files changed, 135 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 188cb22..7495526 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,13 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.60" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "neco" version = "0.1.0" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] - -[metadata] -"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" diff --git a/Cargo.toml b/Cargo.toml index 769482b..4f8be4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Matthias Schiffer "] edition = "2018" [dependencies] -libc = { version = "0.2.54", default-features = false } +libc = { version = "0.2.68", default-features = false } [profile.dev] panic = "abort" diff --git a/src/c/posix.rs b/src/c/posix.rs index 3933a2e..6b63ea7 100644 --- a/src/c/posix.rs +++ b/src/c/posix.rs @@ -1,8 +1,7 @@ extern "C" { pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; - pub static mut stdin: *mut libc::FILE; - pub static mut stdout: *mut libc::FILE; - pub static mut stderr: *mut libc::FILE; - + // pub static stdin: *mut libc::FILE; + pub static stdout: *mut libc::FILE; + pub static stderr: *mut libc::FILE; } diff --git a/src/c/stdio.rs b/src/c/stdio.rs index aa824ed..c878f19 100644 --- a/src/c/stdio.rs +++ b/src/c/stdio.rs @@ -7,8 +7,12 @@ pub struct OStream { file: *mut libc::FILE } -pub fn stdout() -> OStream { - OStream { file: unsafe { posix::stdout } } +pub unsafe fn stdout() -> OStream { + OStream { file: posix::stdout } +} + +pub unsafe fn stderr() -> OStream { + OStream { file: posix::stderr } } impl OStream { diff --git a/src/c/string.rs b/src/c/string.rs index eef6371..3d991dc 100644 --- a/src/c/string.rs +++ b/src/c/string.rs @@ -3,18 +3,20 @@ use core::{mem, ptr, slice}; use core::ops::{Deref, DerefMut}; fn must_succeed(p: *mut T) -> *mut T { - if p.is_null() { - panic!("allocation failure"); - } + assert!(!p.is_null(), "allocation failure"); p } -fn malloc() -> *mut T { - let size = mem::size_of::(); - let align = mem::align_of::(); - if size == 0 { - return align as *mut T; +fn zst(count: usize) -> bool { + mem::size_of::() == 0 || count == 0 +} + +fn alloc(count: usize) -> *mut T { + if zst::(count) { + return ptr::null_mut(); } + let size = count * mem::size_of::(); + let align = mem::align_of::(); must_succeed( unsafe { libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T @@ -22,18 +24,23 @@ fn malloc() -> *mut T { ) } -pub struct CBox(*mut T); +fn dangling() -> *mut T { + mem::align_of::() as *mut T +} -impl CBox { - pub fn new(value: T) -> CBox { - let p = malloc(); - unsafe { - ptr::write(p, value); - CBox::from_raw(p) - } - } +fn check_ptr(p: *const T, len: usize) { + debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); + assert!(zst::(len) || !p.is_null(), "NULL ptr"); +} - pub const unsafe fn from_raw(p: *mut T) -> CBox { +fn slice_len(p: *const [T]) -> usize { + unsafe { mem::transmute::<*const [T], [usize; 2]>(p)[1] } +} + +pub struct CBox(*mut T); + +impl CBox { + pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { CBox(p) } @@ -43,7 +50,7 @@ impl CBox { p } - pub const fn as_ptr(&self) -> *const T { + pub fn as_ptr(&self) -> *const T { self.0 } @@ -52,13 +59,56 @@ impl CBox { } } -impl Drop for CBox { +impl CBox { + pub fn new(value: T) -> CBox { + let p = alloc(1); + unsafe { + ptr::write(p, value); + CBox::from_raw_unchecked(p) + } + } + + pub unsafe fn from_raw(p: *mut T) -> CBox { + check_ptr(p, 1); + CBox(p) + } + + pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { + CBox(ptr::slice_from_raw_parts_mut(p, len)) + } + + pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { + check_ptr(p, len); + CBox::slice_from_raw_parts_unchecked(p, len) + } + + fn safe_ptr(&self) -> *mut T { + if self.0.is_null() { + debug_assert!(zst::(1), "NULL ptr"); + return dangling(); + } + + self.0 + } +} + +impl CBox<[T]> { + fn safe_ptr(&self) -> *mut [T] { + if self.0.is_null() { + let len = slice_len(self.0); + debug_assert!(zst::(len), "NULL ptr"); + return ptr::slice_from_raw_parts_mut(dangling(), len); + } + + self.0 + } +} + +impl Drop for CBox { fn drop(&mut self) { - unsafe { ptr::drop_in_place(self.0); } - if mem::size_of::() != 0 { - unsafe { - libc::free(self.0 as *mut libc::c_void); - } + unsafe { + ptr::drop_in_place(self.0); + libc::free(self.0 as *mut libc::c_void); } } } @@ -67,13 +117,27 @@ impl Deref for CBox { type Target = T; fn deref(&self) -> &T { - unsafe { &*self.0 } + unsafe { &*self.safe_ptr() } } } impl DerefMut for CBox { fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0 } + unsafe { &mut *self.safe_ptr() } + } +} + +impl Deref for CBox<[T]> { + type Target = [T]; + + fn deref(&self) -> &[T] { + unsafe { &*self.safe_ptr() } + } +} + +impl DerefMut for CBox<[T]> { + fn deref_mut(&mut self) -> &mut [T] { + unsafe { &mut *self.safe_ptr() } } } @@ -82,16 +146,26 @@ impl DerefMut for CBox { pub struct CStr { inner: libc::c_char } impl CStr { - pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { + pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { &*(p as *const CStr) } - pub unsafe fn from_ptr_mut<'a>(p: *mut libc::c_char) -> &'a mut CStr { + pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { + check_ptr(p, 1); + CStr::from_ptr_unchecked(p) + } + + pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { &mut *(p as *mut CStr) } + pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { + check_ptr(p, 1); + CStr::from_mut_ptr_unchecked(p) + } + pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr(bytes.as_ptr() as *const libc::c_char) + CStr::from_ptr_unchecked(bytes.as_ptr() as *const libc::c_char) } // TODO @@ -117,7 +191,7 @@ impl CStr { ) } } - pub fn as_bytes_mut(&mut self) -> &mut [u8] { + pub fn as_mut_bytes(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut( self.as_mut_ptr() as *mut u8, self.len(), @@ -139,6 +213,10 @@ macro_rules! cstr { pub struct CString { inner: CBox } impl CString { + pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { + CString { inner: CBox::from_raw_unchecked(p) } + } + pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { CString { inner: CBox::from_raw(p) } } @@ -158,14 +236,14 @@ impl Deref for CString { impl DerefMut for CString { fn deref_mut(&mut self) -> &mut CStr { - unsafe { CStr::from_ptr_mut(&mut *self.inner) } + unsafe { CStr::from_mut_ptr(&mut *self.inner) } } } impl From<&[u8]> for CString { fn from(s: &[u8]) -> CString { unsafe { - CString::from_raw(must_succeed(posix::strndup( + CString::from_raw_unchecked(must_succeed(posix::strndup( s.as_ptr() as *const libc::c_char, s.len() as libc::size_t, ))) @@ -182,7 +260,7 @@ impl From<&str> for CString { impl From<&CStr> for CString { fn from(s: &CStr) -> CString { unsafe { - CString::from_raw(must_succeed(libc::strdup(s.as_ptr()))) + CString::from_raw_unchecked(must_succeed(libc::strdup(s.as_ptr()))) } } } diff --git a/src/main.rs b/src/main.rs index 15a2640..5f86a61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,16 +9,22 @@ use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { + let mut stdout = unsafe { c::stdio::stdout() }; let x = c::string::CString::from("foo"); - let y = c::string::CBox::new(x); + let l = x.len(); + let y = x.into_raw(); + let z = unsafe { + c::string::CBox::slice_from_raw_parts(y, l) + }; + //let y = unsafe { c::string::CBox::from_raw(x) }; //let foo = cstr!("Foo! %p\n"); //c::stdio::stdout().puts(foo); - let mut stdout = c::stdio::stdout(); - writeln!(stdout, "Foo: {}", 42); + let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); 0 } #[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { +fn panic(info: &core::panic::PanicInfo) -> ! { + let _ = writeln!(unsafe { c::stdio::stderr() }, "Panic: {}", info); unsafe { libc::abort() } } From f0b3d5166ef1255fac880b146875ef46d2599a13 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 4 Apr 2020 13:22:18 +0200 Subject: [PATCH 17/29] Reorganize --- .gitignore | 4 +- Cargo.lock | 7 + Cargo.toml | 2 +- safe_libc/Cargo.lock | 14 ++ safe_libc/Cargo.toml | 10 + safe_libc/src/boxed.rs | 146 ++++++++++++++ src/c/posix.rs => safe_libc/src/lib.rs | 11 + {src/c => safe_libc/src}/stdio.rs | 15 +- safe_libc/src/string.rs | 151 ++++++++++++++ safe_libc/src/util.rs | 18 ++ src/c/mod.rs | 3 - src/c/string.rs | 266 ------------------------- src/main.rs | 17 +- 13 files changed, 378 insertions(+), 286 deletions(-) create mode 100644 safe_libc/Cargo.lock create mode 100644 safe_libc/Cargo.toml create mode 100644 safe_libc/src/boxed.rs rename src/c/posix.rs => safe_libc/src/lib.rs (70%) rename {src/c => safe_libc/src}/stdio.rs (77%) create mode 100644 safe_libc/src/string.rs create mode 100644 safe_libc/src/util.rs delete mode 100644 src/c/mod.rs delete mode 100644 src/c/string.rs diff --git a/.gitignore b/.gitignore index f0e3bca..11d7baa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/target -**/*.rs.bk \ No newline at end of file +target +*.rs.bk diff --git a/Cargo.lock b/Cargo.lock index 7495526..45e6ba1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,13 @@ checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "neco" version = "0.1.0" +dependencies = [ + "safe_libc", +] + +[[package]] +name = "safe_libc" +version = "0.1.0" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 4f8be4f..b7770a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Matthias Schiffer "] edition = "2018" [dependencies] -libc = { version = "0.2.68", default-features = false } +safe_libc = { path = "safe_libc" } [profile.dev] panic = "abort" diff --git a/safe_libc/Cargo.lock b/safe_libc/Cargo.lock new file mode 100644 index 0000000..e659af2 --- /dev/null +++ b/safe_libc/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "libc" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" + +[[package]] +name = "safe_libc" +version = "0.1.0" +dependencies = [ + "libc", +] diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml new file mode 100644 index 0000000..f776d09 --- /dev/null +++ b/safe_libc/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "safe_libc" +version = "0.1.0" +authors = ["Matthias Schiffer "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = { version = "0.2.68", default-features = false } diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs new file mode 100644 index 0000000..72c961c --- /dev/null +++ b/safe_libc/src/boxed.rs @@ -0,0 +1,146 @@ +use crate::util; + +use core::{mem, ptr}; +use core::ops::{Deref, DerefMut}; + +fn alloc(len: usize) -> *mut T { + if util::zst::(len) { + return ptr::null_mut(); + } + let size = len.checked_mul(mem::size_of::()).expect("allocation overflow"); + let align = mem::align_of::(); + util::must_succeed( + unsafe { + libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T + } + ) +} + +#[inline] +fn dangling() -> *mut T { + mem::align_of::() as *mut T +} + +#[inline] +fn slice_len(p: *const [T]) -> usize { + unsafe { mem::transmute::<*const [T], [usize; 2]>(p)[1] } +} + +pub struct CBox(*mut T); + +impl CBox { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { + CBox(p) + } + + #[inline] + pub fn into_raw(self) -> *mut T { + let p = self.0; + mem::forget(self); + p + } + + #[inline] + pub fn as_ptr(&self) -> *const T { + self.0 + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.0 + } +} + +impl CBox { + #[inline] + pub fn new(value: T) -> CBox { + let p = alloc(1); + unsafe { + ptr::write(p, value); + CBox::from_raw_unchecked(p) + } + } + + #[inline] + pub unsafe fn from_raw(p: *mut T) -> CBox { + util::check_ptr(p, 1); + CBox(p) + } + + #[inline] + pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { + CBox(ptr::slice_from_raw_parts_mut(p, len)) + } + + #[inline] + pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { + util::check_ptr(p, len); + CBox::slice_from_raw_parts_unchecked(p, len) + } + + #[inline] + fn safe_ptr(&self) -> *mut T { + if util::zst::(1) { + return dangling(); + } + + debug_assert!(!self.0.is_null(), "NULL ptr"); + self.0 + } +} + +impl CBox<[T]> { + #[inline] + fn safe_ptr(&self) -> *mut [T] { + if self.0.is_null() { + let len = slice_len(self.0); + debug_assert!(util::zst::(len), "NULL ptr"); + return ptr::slice_from_raw_parts_mut(dangling(), len); + } + + self.0 + } +} + +impl Drop for CBox { + #[inline] + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + libc::free(self.0 as *mut libc::c_void); + } + } +} + +impl Deref for CBox { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.safe_ptr() } + } +} + +impl DerefMut for CBox { + #[inline] + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.safe_ptr() } + } +} + +impl Deref for CBox<[T]> { + type Target = [T]; + + #[inline] + fn deref(&self) -> &[T] { + unsafe { &*self.safe_ptr() } + } +} + +impl DerefMut for CBox<[T]> { + #[inline] + fn deref_mut(&mut self) -> &mut [T] { + unsafe { &mut *self.safe_ptr() } + } +} diff --git a/src/c/posix.rs b/safe_libc/src/lib.rs similarity index 70% rename from src/c/posix.rs rename to safe_libc/src/lib.rs index 6b63ea7..0ab0e89 100644 --- a/src/c/posix.rs +++ b/safe_libc/src/lib.rs @@ -1,3 +1,14 @@ +#![no_std] + +pub use libc::*; + +mod util; + +pub mod boxed; +pub mod stdio; +pub mod string; + + extern "C" { pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; diff --git a/src/c/stdio.rs b/safe_libc/src/stdio.rs similarity index 77% rename from src/c/stdio.rs rename to safe_libc/src/stdio.rs index c878f19..5f33f74 100644 --- a/src/c/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -1,21 +1,24 @@ -use core::fmt; +use crate as libc; +use crate::string; -use super::posix; -use super::string; +use core::fmt; pub struct OStream { file: *mut libc::FILE } +#[inline] pub unsafe fn stdout() -> OStream { - OStream { file: posix::stdout } + OStream { file: libc::stdout } } +#[inline] pub unsafe fn stderr() -> OStream { - OStream { file: posix::stderr } + OStream { file: libc::stderr } } impl OStream { + #[inline] pub fn write(&mut self, b: &[u8]) { unsafe { libc::fwrite( @@ -27,6 +30,7 @@ impl OStream { } } + #[inline] pub fn puts(&mut self, s: &string::CStr) { unsafe { libc::fputs(s.as_ptr(), self.file); @@ -35,6 +39,7 @@ impl OStream { } impl fmt::Write for OStream { + #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { self.write(s.as_bytes()); Ok(()) diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs new file mode 100644 index 0000000..c85f788 --- /dev/null +++ b/safe_libc/src/string.rs @@ -0,0 +1,151 @@ +use crate as libc; +use crate::util; +use crate::boxed::CBox; + +use core::slice; +use core::ops::{Deref, DerefMut}; + +//pub struct FromBytesWithNulError {} + +pub struct CStr { inner: libc::c_char } + +impl CStr { + #[inline] + pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { + &*(p as *const CStr) + } + + #[inline] + pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { + util::check_ptr(p, 1); + CStr::from_ptr_unchecked(p) + } + + #[inline] + pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { + &mut *(p as *mut CStr) + } + + #[inline] + pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { + util::check_ptr(p, 1); + CStr::from_mut_ptr_unchecked(p) + } + + #[inline] + pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { + CStr::from_ptr_unchecked(bytes.as_ptr() as *const libc::c_char) + } + + // TODO + //pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { + //} + + #[inline] + pub fn len(&self) -> usize { + unsafe { libc::strlen(self.as_ptr()) as usize } + } + + #[inline] + pub const fn as_ptr(&self) -> *const libc::c_char { + &self.inner + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { + &mut self.inner + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts( + self.as_ptr() as *const u8, + self.len(), + ) } + } + + #[inline] + pub fn as_mut_bytes(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut u8, + self.len(), + ) } + } + + #[inline] + pub fn to_owned(self: &CStr) -> CString { + CString::from(self) + } +} + +#[macro_export] +macro_rules! cstr { + ($s:expr) => ( + unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } + ) +} + +pub struct CString { inner: CBox } + +impl CString { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { + CString { inner: CBox::from_raw_unchecked(p) } + } + + #[inline] + pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { + util::check_ptr(p, 1); + CString::from_raw_unchecked(p) + } + + #[inline] + pub fn into_raw(self) -> *mut libc::c_char { + self.inner.into_raw() + } +} + +impl Deref for CString { + type Target = CStr; + + #[inline] + fn deref(&self) -> &CStr { + unsafe { CStr::from_ptr(&*self.inner) } + } +} + +impl DerefMut for CString { + #[inline] + fn deref_mut(&mut self) -> &mut CStr { + unsafe { CStr::from_mut_ptr(&mut *self.inner) } + } +} + +impl From<&[u8]> for CString { + fn from(s: &[u8]) -> CString { + unsafe { + CString::from_raw_unchecked( + util::must_succeed(libc::strndup( + s.as_ptr() as *const libc::c_char, + s.len() as libc::size_t, + )) + ) + } + } +} + +impl From<&str> for CString { + #[inline] + fn from(s: &str) -> CString { + CString::from(s.as_bytes()) + } +} + +impl From<&CStr> for CString { + #[inline] + fn from(s: &CStr) -> CString { + unsafe { + CString::from_raw_unchecked(util::must_succeed(libc::strdup(s.as_ptr()))) + } + } +} diff --git a/safe_libc/src/util.rs b/safe_libc/src/util.rs new file mode 100644 index 0000000..b8a4ed5 --- /dev/null +++ b/safe_libc/src/util.rs @@ -0,0 +1,18 @@ +use core::mem; + +#[inline] +pub fn zst(len: usize) -> bool { + mem::size_of::() == 0 || len == 0 +} + +#[inline] +pub fn check_ptr(p: *const T, len: usize) { + debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); + assert!(zst::(len) || !p.is_null(), "NULL ptr"); +} + +#[inline] +pub fn must_succeed(p: *mut T) -> *mut T { + assert!(!p.is_null(), "allocation failure"); + p +} diff --git a/src/c/mod.rs b/src/c/mod.rs deleted file mode 100644 index 3694561..0000000 --- a/src/c/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod posix; -pub mod stdio; -pub mod string; diff --git a/src/c/string.rs b/src/c/string.rs deleted file mode 100644 index 3d991dc..0000000 --- a/src/c/string.rs +++ /dev/null @@ -1,266 +0,0 @@ -use super::posix; -use core::{mem, ptr, slice}; -use core::ops::{Deref, DerefMut}; - -fn must_succeed(p: *mut T) -> *mut T { - assert!(!p.is_null(), "allocation failure"); - p -} - -fn zst(count: usize) -> bool { - mem::size_of::() == 0 || count == 0 -} - -fn alloc(count: usize) -> *mut T { - if zst::(count) { - return ptr::null_mut(); - } - let size = count * mem::size_of::(); - let align = mem::align_of::(); - must_succeed( - unsafe { - libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T - } - ) -} - -fn dangling() -> *mut T { - mem::align_of::() as *mut T -} - -fn check_ptr(p: *const T, len: usize) { - debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); - assert!(zst::(len) || !p.is_null(), "NULL ptr"); -} - -fn slice_len(p: *const [T]) -> usize { - unsafe { mem::transmute::<*const [T], [usize; 2]>(p)[1] } -} - -pub struct CBox(*mut T); - -impl CBox { - pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { - CBox(p) - } - - pub fn into_raw(self) -> *mut T { - let p = self.0; - mem::forget(self); - p - } - - pub fn as_ptr(&self) -> *const T { - self.0 - } - - pub fn as_mut_ptr(&mut self) -> *mut T { - self.0 - } -} - -impl CBox { - pub fn new(value: T) -> CBox { - let p = alloc(1); - unsafe { - ptr::write(p, value); - CBox::from_raw_unchecked(p) - } - } - - pub unsafe fn from_raw(p: *mut T) -> CBox { - check_ptr(p, 1); - CBox(p) - } - - pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { - CBox(ptr::slice_from_raw_parts_mut(p, len)) - } - - pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { - check_ptr(p, len); - CBox::slice_from_raw_parts_unchecked(p, len) - } - - fn safe_ptr(&self) -> *mut T { - if self.0.is_null() { - debug_assert!(zst::(1), "NULL ptr"); - return dangling(); - } - - self.0 - } -} - -impl CBox<[T]> { - fn safe_ptr(&self) -> *mut [T] { - if self.0.is_null() { - let len = slice_len(self.0); - debug_assert!(zst::(len), "NULL ptr"); - return ptr::slice_from_raw_parts_mut(dangling(), len); - } - - self.0 - } -} - -impl Drop for CBox { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - libc::free(self.0 as *mut libc::c_void); - } - } -} - -impl Deref for CBox { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.safe_ptr() } - } -} - -impl DerefMut for CBox { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.safe_ptr() } - } -} - -impl Deref for CBox<[T]> { - type Target = [T]; - - fn deref(&self) -> &[T] { - unsafe { &*self.safe_ptr() } - } -} - -impl DerefMut for CBox<[T]> { - fn deref_mut(&mut self) -> &mut [T] { - unsafe { &mut *self.safe_ptr() } - } -} - -//pub struct FromBytesWithNulError {} - -pub struct CStr { inner: libc::c_char } - -impl CStr { - pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { - &*(p as *const CStr) - } - - pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { - check_ptr(p, 1); - CStr::from_ptr_unchecked(p) - } - - pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { - &mut *(p as *mut CStr) - } - - pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { - check_ptr(p, 1); - CStr::from_mut_ptr_unchecked(p) - } - - pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr_unchecked(bytes.as_ptr() as *const libc::c_char) - } - - // TODO - //pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { - //} - - pub fn len(&self) -> usize { - unsafe { libc::strlen(self.as_ptr()) as usize } - } - - pub const fn as_ptr(&self) -> *const libc::c_char { - &self.inner - } - - pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { - &mut self.inner - } - - pub fn as_bytes(&self) -> &[u8] { - unsafe { slice::from_raw_parts( - self.as_ptr() as *const u8, - self.len(), - ) } - } - - pub fn as_mut_bytes(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut u8, - self.len(), - ) } - } - - pub fn to_owned(self: &CStr) -> CString { - CString::from(self) - } -} - -#[macro_export] -macro_rules! cstr { - ($s:expr) => ( - unsafe { $crate::c::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } - ) -} - -pub struct CString { inner: CBox } - -impl CString { - pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { - CString { inner: CBox::from_raw_unchecked(p) } - } - - pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { - CString { inner: CBox::from_raw(p) } - } - - pub fn into_raw(self) -> *mut libc::c_char { - self.inner.into_raw() - } -} - -impl Deref for CString { - type Target = CStr; - - fn deref(&self) -> &CStr { - unsafe { CStr::from_ptr(&*self.inner) } - } -} - -impl DerefMut for CString { - fn deref_mut(&mut self) -> &mut CStr { - unsafe { CStr::from_mut_ptr(&mut *self.inner) } - } -} - -impl From<&[u8]> for CString { - fn from(s: &[u8]) -> CString { - unsafe { - CString::from_raw_unchecked(must_succeed(posix::strndup( - s.as_ptr() as *const libc::c_char, - s.len() as libc::size_t, - ))) - } - } -} - -impl From<&str> for CString { - fn from(s: &str) -> CString { - CString::from(s.as_bytes()) - } -} - -impl From<&CStr> for CString { - fn from(s: &CStr) -> CString { - unsafe { - CString::from_raw_unchecked(must_succeed(libc::strdup(s.as_ptr()))) - } - } -} diff --git a/src/main.rs b/src/main.rs index 5f86a61..67d8baa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,29 @@ #![no_main] #![no_std] -extern crate libc; - -mod c; +use safe_libc as libc; +use libc::cstr; use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let mut stdout = unsafe { c::stdio::stdout() }; - let x = c::string::CString::from("foo"); + let mut stdout = unsafe { libc::stdio::stdout() }; + let x = libc::string::CString::from("foo"); let l = x.len(); let y = x.into_raw(); let z = unsafe { - c::string::CBox::slice_from_raw_parts(y, l) + libc::boxed::CBox::slice_from_raw_parts(y, l) }; //let y = unsafe { c::string::CBox::from_raw(x) }; - //let foo = cstr!("Foo! %p\n"); - //c::stdio::stdout().puts(foo); + let foo = cstr!("Foo!\n"); + stdout.puts(foo); let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); 0 } #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - let _ = writeln!(unsafe { c::stdio::stderr() }, "Panic: {}", info); + let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info); unsafe { libc::abort() } } From de9bc974a6b423b7f086af23e50c47a097dcd352 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 4 Apr 2020 17:13:36 +0200 Subject: [PATCH 18/29] Minor cleanup, implement Debug, Display for CBox --- Cargo.toml | 2 ++ safe_libc/src/boxed.rs | 44 +++++++++++++++++++++++++++++++++-------- safe_libc/src/stdio.rs | 2 +- safe_libc/src/string.rs | 12 +++++------ src/main.rs | 3 ++- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7770a0..b83531d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,9 @@ edition = "2018" safe_libc = { path = "safe_libc" } [profile.dev] +lto = true panic = "abort" [profile.release] +lto = true panic = "abort" diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs index 72c961c..625c5b0 100644 --- a/safe_libc/src/boxed.rs +++ b/safe_libc/src/boxed.rs @@ -1,6 +1,6 @@ use crate::util; -use core::{mem, ptr}; +use core::{fmt, mem, ptr}; use core::ops::{Deref, DerefMut}; fn alloc(len: usize) -> *mut T { @@ -11,26 +11,26 @@ fn alloc(len: usize) -> *mut T { let align = mem::align_of::(); util::must_succeed( unsafe { - libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T + libc::memalign(align as libc::size_t, size as libc::size_t) } - ) + ).cast() } #[inline] -fn dangling() -> *mut T { +const fn dangling() -> *mut T { mem::align_of::() as *mut T } #[inline] fn slice_len(p: *const [T]) -> usize { - unsafe { mem::transmute::<*const [T], [usize; 2]>(p)[1] } + unsafe { mem::transmute::<_, [usize; 2]>(p)[1] } } pub struct CBox(*mut T); impl CBox { #[inline] - pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { + pub const unsafe fn from_raw_unchecked(p: *mut T) -> CBox { CBox(p) } @@ -42,7 +42,7 @@ impl CBox { } #[inline] - pub fn as_ptr(&self) -> *const T { + pub const fn as_ptr(&self) -> *const T { self.0 } @@ -108,7 +108,7 @@ impl Drop for CBox { fn drop(&mut self) { unsafe { ptr::drop_in_place(self.0); - libc::free(self.0 as *mut libc::c_void); + libc::free(self.0.cast()); } } } @@ -144,3 +144,31 @@ impl DerefMut for CBox<[T]> { unsafe { &mut *self.safe_ptr() } } } + +impl fmt::Debug for CBox { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl fmt::Debug for CBox<[T]> where [T]: fmt::Debug { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl fmt::Display for CBox { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Display for CBox<[T]> where [T]: fmt::Display { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index 5f33f74..271f023 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -22,7 +22,7 @@ impl OStream { pub fn write(&mut self, b: &[u8]) { unsafe { libc::fwrite( - b.as_ptr() as *const libc::c_void, + b.as_ptr().cast(), 1, b.len(), self.file, diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index c85f788..8f973ad 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -12,7 +12,7 @@ pub struct CStr { inner: libc::c_char } impl CStr { #[inline] pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { - &*(p as *const CStr) + &*p.cast() } #[inline] @@ -23,7 +23,7 @@ impl CStr { #[inline] pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { - &mut *(p as *mut CStr) + &mut *p.cast() } #[inline] @@ -34,7 +34,7 @@ impl CStr { #[inline] pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr_unchecked(bytes.as_ptr() as *const libc::c_char) + CStr::from_ptr_unchecked(bytes.as_ptr().cast()) } // TODO @@ -59,7 +59,7 @@ impl CStr { #[inline] pub fn as_bytes(&self) -> &[u8] { unsafe { slice::from_raw_parts( - self.as_ptr() as *const u8, + self.as_ptr().cast(), self.len(), ) } } @@ -67,7 +67,7 @@ impl CStr { #[inline] pub fn as_mut_bytes(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut u8, + self.as_mut_ptr().cast(), self.len(), ) } } @@ -126,7 +126,7 @@ impl From<&[u8]> for CString { unsafe { CString::from_raw_unchecked( util::must_succeed(libc::strndup( - s.as_ptr() as *const libc::c_char, + s.as_ptr().cast(), s.len() as libc::size_t, )) ) diff --git a/src/main.rs b/src/main.rs index 67d8baa..944b7ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,10 +15,11 @@ pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) - let z = unsafe { libc::boxed::CBox::slice_from_raw_parts(y, l) }; - //let y = unsafe { c::string::CBox::from_raw(x) }; let foo = cstr!("Foo!\n"); stdout.puts(foo); let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); + let b = libc::boxed::CBox::new(42); + let _ = writeln!(stdout, "Bar: {}", b); 0 } From fff906a78ba545e77d087a82711b30bcc8e0d0c5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 5 Apr 2020 14:22:59 +0200 Subject: [PATCH 19/29] Introduce SafePtr trait, cleanup --- safe_libc/src/boxed.rs | 104 ++++++++++++++++------------------------- src/main.rs | 31 +++++++----- 2 files changed, 60 insertions(+), 75 deletions(-) diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs index 625c5b0..fa3e45d 100644 --- a/safe_libc/src/boxed.rs +++ b/safe_libc/src/boxed.rs @@ -26,11 +26,40 @@ fn slice_len(p: *const [T]) -> usize { unsafe { mem::transmute::<_, [usize; 2]>(p)[1] } } -pub struct CBox(*mut T); +pub trait SafePtr { + fn safe_ptr(p: *mut Self) -> *mut Self; +} -impl CBox { +impl SafePtr for T { #[inline] - pub const unsafe fn from_raw_unchecked(p: *mut T) -> CBox { + fn safe_ptr(p: *mut T) -> *mut T { + if util::zst::(1) { + return dangling(); + } + + debug_assert!(!p.is_null(), "NULL ptr"); + p + } +} + +impl SafePtr for [T] { + #[inline] + fn safe_ptr(p: *mut [T]) -> *mut [T] { + let len = slice_len(p); + if util::zst::(len) { + return ptr::slice_from_raw_parts_mut(dangling(), len); + } + + debug_assert!(!p.is_null(), "NULL ptr"); + p + } +} + +pub struct CBox(*mut T); + +impl CBox { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { CBox(p) } @@ -42,7 +71,7 @@ impl CBox { } #[inline] - pub const fn as_ptr(&self) -> *const T { + pub fn as_ptr(&self) -> *const T { self.0 } @@ -78,32 +107,9 @@ impl CBox { util::check_ptr(p, len); CBox::slice_from_raw_parts_unchecked(p, len) } - - #[inline] - fn safe_ptr(&self) -> *mut T { - if util::zst::(1) { - return dangling(); - } - - debug_assert!(!self.0.is_null(), "NULL ptr"); - self.0 - } } -impl CBox<[T]> { - #[inline] - fn safe_ptr(&self) -> *mut [T] { - if self.0.is_null() { - let len = slice_len(self.0); - debug_assert!(util::zst::(len), "NULL ptr"); - return ptr::slice_from_raw_parts_mut(dangling(), len); - } - - self.0 - } -} - -impl Drop for CBox { +impl Drop for CBox { #[inline] fn drop(&mut self) { unsafe { @@ -113,60 +119,30 @@ impl Drop for CBox { } } -impl Deref for CBox { +impl Deref for CBox { type Target = T; #[inline] fn deref(&self) -> &T { - unsafe { &*self.safe_ptr() } + unsafe { &*T::safe_ptr(self.0) } } } -impl DerefMut for CBox { +impl DerefMut for CBox { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.safe_ptr() } + unsafe { &mut *T::safe_ptr(self.0) } } } -impl Deref for CBox<[T]> { - type Target = [T]; - - #[inline] - fn deref(&self) -> &[T] { - unsafe { &*self.safe_ptr() } - } -} - -impl DerefMut for CBox<[T]> { - #[inline] - fn deref_mut(&mut self) -> &mut [T] { - unsafe { &mut *self.safe_ptr() } - } -} - -impl fmt::Debug for CBox { +impl fmt::Debug for CBox { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } -impl fmt::Debug for CBox<[T]> where [T]: fmt::Debug { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for CBox { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -impl fmt::Display for CBox<[T]> where [T]: fmt::Display { +impl fmt::Display for CBox { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&**self, f) diff --git a/src/main.rs b/src/main.rs index 944b7ab..c79f1da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,17 +9,26 @@ use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { let mut stdout = unsafe { libc::stdio::stdout() }; - let x = libc::string::CString::from("foo"); - let l = x.len(); - let y = x.into_raw(); - let z = unsafe { - libc::boxed::CBox::slice_from_raw_parts(y, l) - }; - let foo = cstr!("Foo!\n"); - stdout.puts(foo); - let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); - let b = libc::boxed::CBox::new(42); - let _ = writeln!(stdout, "Bar: {}", b); + + { + let foo = cstr!("Foo!\n"); + stdout.puts(foo); + } + + { + let x = libc::string::CString::from("foo"); + let l = x.len(); + let y = x.into_raw(); + let z = unsafe { + libc::boxed::CBox::slice_from_raw_parts(y, l) + }; + let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); + } + + { + let b = libc::boxed::CBox::new(42); + let _ = writeln!(stdout, "Bar: {}", b); + } 0 } From 5b449f4e1e43bc59109da5a37edf5ec911d3df8e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 13 Apr 2020 02:13:58 +0200 Subject: [PATCH 20/29] IO, error handling --- safe_libc/src/boxed.rs | 2 +- safe_libc/src/errno.rs | 29 +++++++++++ safe_libc/src/lib.rs | 1 + safe_libc/src/stdio.rs | 110 +++++++++++++++++++++++++++++++--------- safe_libc/src/string.rs | 23 ++++++--- src/main.rs | 7 +-- 6 files changed, 138 insertions(+), 34 deletions(-) create mode 100644 safe_libc/src/errno.rs diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs index fa3e45d..4a38b70 100644 --- a/safe_libc/src/boxed.rs +++ b/safe_libc/src/boxed.rs @@ -11,7 +11,7 @@ fn alloc(len: usize) -> *mut T { let align = mem::align_of::(); util::must_succeed( unsafe { - libc::memalign(align as libc::size_t, size as libc::size_t) + libc::memalign(align, size) } ).cast() } diff --git a/safe_libc/src/errno.rs b/safe_libc/src/errno.rs new file mode 100644 index 0000000..be944d1 --- /dev/null +++ b/safe_libc/src/errno.rs @@ -0,0 +1,29 @@ +use crate::string; + +use core::fmt; + +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct Errno(pub libc::c_int); + +#[inline] +pub fn errno() -> Errno { + unsafe { Errno(*libc::__errno_location()) } +} + +impl fmt::Display for Errno { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [0u8; 1024]; + let cstr = unsafe { + if libc::strerror_r(self.0, buf.as_mut_ptr().cast(), buf.len()) != 0 { + return Err(fmt::Error); + } + string::CStr::from_bytes_with_nul_unchecked(&buf) + }; + match cstr.to_str() { + Err(_) => Err(fmt::Error), + Ok(s) => f.write_str(s), + } + } +} diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index 0ab0e89..aec5acc 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -5,6 +5,7 @@ pub use libc::*; mod util; pub mod boxed; +pub mod errno; pub mod stdio; pub mod string; diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index 271f023..2d19725 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -1,47 +1,111 @@ -use crate as libc; -use crate::string; +use crate::{self as libc, errno, string}; use core::fmt; +use core::ops::{Deref, DerefMut}; -pub struct OStream { - file: *mut libc::FILE +pub struct Error { + pub errno: errno::Errno, +} + +pub type Result = core::result::Result; + +#[inline] +fn check_io(ok: bool) -> Result<()> { + if ok { + Ok(()) + } else { + Err(Error { + errno: errno::errno(), + }) + } +} + +pub struct BasicOStream(*mut libc::FILE); + +unsafe impl Sync for BasicOStream {} +unsafe impl Send for BasicOStream {} + +pub type Stdout = BasicOStream; +pub type Stderr = BasicOStream; + +#[inline] +pub fn stdout() -> Stdout { + BasicOStream(unsafe { libc::stdout }) } #[inline] -pub unsafe fn stdout() -> OStream { - OStream { file: libc::stdout } +pub fn stderr() -> Stderr { + BasicOStream(unsafe { libc::stderr }) } -#[inline] -pub unsafe fn stderr() -> OStream { - OStream { file: libc::stderr } -} - -impl OStream { +impl BasicOStream { #[inline] - pub fn write(&mut self, b: &[u8]) { - unsafe { + pub fn flush(&mut self) -> Result<()> { + check_io(unsafe { + libc::fflush(self.0) + } == 0) + } + + #[inline] + pub fn write(&mut self, b: &[u8]) -> Result<()> { + check_io(unsafe { libc::fwrite( b.as_ptr().cast(), 1, b.len(), - self.file, - ); - } + self.0, + ) + } == b.len()) } #[inline] - pub fn puts(&mut self, s: &string::CStr) { - unsafe { - libc::fputs(s.as_ptr(), self.file); - } + pub fn puts(&mut self, s: &string::CStr) -> Result<()> { + check_io(unsafe { + libc::fputs(s.as_ptr(), self.0) + } != libc::EOF) } } -impl fmt::Write for OStream { +impl fmt::Write for BasicOStream { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { - self.write(s.as_bytes()); + if self.write(s.as_bytes()).is_err() { + return Err(fmt::Error); + } Ok(()) } } + +pub struct OStream(BasicOStream); + +impl OStream { + #[inline] + pub unsafe fn from_raw(file: *mut libc::FILE) -> OStream { + OStream(BasicOStream(file)) + } +} + +impl Drop for OStream { + #[inline] + fn drop(&mut self) { + unsafe { + libc::fclose((self.0).0); + } + } +} + +impl Deref for OStream { + type Target = BasicOStream; + + #[inline] + fn deref(&self) -> &BasicOStream { + &self.0 + } +} + +impl DerefMut for OStream { + #[inline] + fn deref_mut(&mut self) -> &mut BasicOStream { + &mut self.0 + } +} diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index 8f973ad..53f8f87 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -1,12 +1,11 @@ -use crate as libc; -use crate::util; -use crate::boxed::CBox; +use crate::{self as libc, util, boxed::CBox}; use core::slice; use core::ops::{Deref, DerefMut}; //pub struct FromBytesWithNulError {} +#[repr(transparent)] pub struct CStr { inner: libc::c_char } impl CStr { @@ -43,7 +42,7 @@ impl CStr { #[inline] pub fn len(&self) -> usize { - unsafe { libc::strlen(self.as_ptr()) as usize } + unsafe { libc::strlen(self.as_ptr()) } } #[inline] @@ -57,7 +56,7 @@ impl CStr { } #[inline] - pub fn as_bytes(&self) -> &[u8] { + pub fn to_bytes(&self) -> &[u8] { unsafe { slice::from_raw_parts( self.as_ptr().cast(), self.len(), @@ -65,13 +64,23 @@ impl CStr { } #[inline] - pub fn as_mut_bytes(&mut self) -> &mut [u8] { + pub fn to_mut_bytes(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut( self.as_mut_ptr().cast(), self.len(), ) } } + #[inline] + pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> { + core::str::from_utf8(self.to_bytes()) + } + + #[inline] + pub fn to_mut_str(&mut self) -> Result<&mut str, core::str::Utf8Error> { + core::str::from_utf8_mut(self.to_mut_bytes()) + } + #[inline] pub fn to_owned(self: &CStr) -> CString { CString::from(self) @@ -127,7 +136,7 @@ impl From<&[u8]> for CString { CString::from_raw_unchecked( util::must_succeed(libc::strndup( s.as_ptr().cast(), - s.len() as libc::size_t, + s.len(), )) ) } diff --git a/src/main.rs b/src/main.rs index c79f1da..26882ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,11 @@ use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let mut stdout = unsafe { libc::stdio::stdout() }; + let mut stdout = libc::stdio::stdout(); { let foo = cstr!("Foo!\n"); - stdout.puts(foo); + let _ = stdout.puts(foo); } { @@ -29,11 +29,12 @@ pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) - let b = libc::boxed::CBox::new(42); let _ = writeln!(stdout, "Bar: {}", b); } + 0 } #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info); + let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info); unsafe { libc::abort() } } From fd2384b7fab14732efde99da8affd830e2334e16 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 1 May 2020 18:46:23 +0200 Subject: [PATCH 21/29] Nightly stuff --- rustfmt.toml | 1 + safe_libc/src/boxed.rs | 150 ---------------------------------------- safe_libc/src/errno.rs | 7 +- safe_libc/src/lib.rs | 7 +- safe_libc/src/stdio.rs | 17 +---- safe_libc/src/string.rs | 72 +++++++------------ safe_libc/src/util.rs | 13 ---- src/main.rs | 17 +++-- src/system_alloc.rs | 21 ++++++ 9 files changed, 73 insertions(+), 232 deletions(-) create mode 100644 rustfmt.toml delete mode 100644 safe_libc/src/boxed.rs create mode 100644 src/system_alloc.rs diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs deleted file mode 100644 index 4a38b70..0000000 --- a/safe_libc/src/boxed.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::util; - -use core::{fmt, mem, ptr}; -use core::ops::{Deref, DerefMut}; - -fn alloc(len: usize) -> *mut T { - if util::zst::(len) { - return ptr::null_mut(); - } - let size = len.checked_mul(mem::size_of::()).expect("allocation overflow"); - let align = mem::align_of::(); - util::must_succeed( - unsafe { - libc::memalign(align, size) - } - ).cast() -} - -#[inline] -const fn dangling() -> *mut T { - mem::align_of::() as *mut T -} - -#[inline] -fn slice_len(p: *const [T]) -> usize { - unsafe { mem::transmute::<_, [usize; 2]>(p)[1] } -} - -pub trait SafePtr { - fn safe_ptr(p: *mut Self) -> *mut Self; -} - -impl SafePtr for T { - #[inline] - fn safe_ptr(p: *mut T) -> *mut T { - if util::zst::(1) { - return dangling(); - } - - debug_assert!(!p.is_null(), "NULL ptr"); - p - } -} - -impl SafePtr for [T] { - #[inline] - fn safe_ptr(p: *mut [T]) -> *mut [T] { - let len = slice_len(p); - if util::zst::(len) { - return ptr::slice_from_raw_parts_mut(dangling(), len); - } - - debug_assert!(!p.is_null(), "NULL ptr"); - p - } -} - -pub struct CBox(*mut T); - -impl CBox { - #[inline] - pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { - CBox(p) - } - - #[inline] - pub fn into_raw(self) -> *mut T { - let p = self.0; - mem::forget(self); - p - } - - #[inline] - pub fn as_ptr(&self) -> *const T { - self.0 - } - - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - self.0 - } -} - -impl CBox { - #[inline] - pub fn new(value: T) -> CBox { - let p = alloc(1); - unsafe { - ptr::write(p, value); - CBox::from_raw_unchecked(p) - } - } - - #[inline] - pub unsafe fn from_raw(p: *mut T) -> CBox { - util::check_ptr(p, 1); - CBox(p) - } - - #[inline] - pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { - CBox(ptr::slice_from_raw_parts_mut(p, len)) - } - - #[inline] - pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { - util::check_ptr(p, len); - CBox::slice_from_raw_parts_unchecked(p, len) - } -} - -impl Drop for CBox { - #[inline] - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - libc::free(self.0.cast()); - } - } -} - -impl Deref for CBox { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - unsafe { &*T::safe_ptr(self.0) } - } -} - -impl DerefMut for CBox { - #[inline] - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *T::safe_ptr(self.0) } - } -} - -impl fmt::Debug for CBox { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Display for CBox { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} diff --git a/safe_libc/src/errno.rs b/safe_libc/src/errno.rs index be944d1..2a2220b 100644 --- a/safe_libc/src/errno.rs +++ b/safe_libc/src/errno.rs @@ -6,9 +6,14 @@ use core::fmt; #[repr(transparent)] pub struct Errno(pub libc::c_int); +extern "C" { + //#[ffi_const] + pub fn __errno_location() -> *mut libc::c_int; +} + #[inline] pub fn errno() -> Errno { - unsafe { Errno(*libc::__errno_location()) } + unsafe { Errno(*__errno_location()) } } impl fmt::Display for Errno { diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index aec5acc..6d9baf2 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -1,15 +1,18 @@ #![no_std] +#![feature(slice_ptr_len)] +#![feature(min_specialization)] +// #![feature(ffi_const)] + +extern crate alloc; pub use libc::*; mod util; -pub mod boxed; pub mod errno; pub mod stdio; pub mod string; - extern "C" { pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index 2d19725..be016ab 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -41,28 +41,17 @@ pub fn stderr() -> Stderr { impl BasicOStream { #[inline] pub fn flush(&mut self) -> Result<()> { - check_io(unsafe { - libc::fflush(self.0) - } == 0) + check_io(unsafe { libc::fflush(self.0) } == 0) } #[inline] pub fn write(&mut self, b: &[u8]) -> Result<()> { - check_io(unsafe { - libc::fwrite( - b.as_ptr().cast(), - 1, - b.len(), - self.0, - ) - } == b.len()) + check_io(unsafe { libc::fwrite(b.as_ptr().cast(), 1, b.len(), self.0) } == b.len()) } #[inline] pub fn puts(&mut self, s: &string::CStr) -> Result<()> { - check_io(unsafe { - libc::fputs(s.as_ptr(), self.0) - } != libc::EOF) + check_io(unsafe { libc::fputs(s.as_ptr(), self.0) } != libc::EOF) } } diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index 53f8f87..6a8308a 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -1,39 +1,29 @@ -use crate::{self as libc, util, boxed::CBox}; +use crate::{self as libc, util}; -use core::slice; -use core::ops::{Deref, DerefMut}; +use alloc::boxed::Box; +use core::{ops::{Deref, DerefMut}, slice}; //pub struct FromBytesWithNulError {} #[repr(transparent)] -pub struct CStr { inner: libc::c_char } +pub struct CStr { + inner: libc::c_char, +} impl CStr { #[inline] - pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { + pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { &*p.cast() } #[inline] - pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { - util::check_ptr(p, 1); - CStr::from_ptr_unchecked(p) - } - - #[inline] - pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { + pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { &mut *p.cast() } - #[inline] - pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { - util::check_ptr(p, 1); - CStr::from_mut_ptr_unchecked(p) - } - #[inline] pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr_unchecked(bytes.as_ptr().cast()) + CStr::from_ptr(bytes.as_ptr().cast()) } // TODO @@ -57,18 +47,12 @@ impl CStr { #[inline] pub fn to_bytes(&self) -> &[u8] { - unsafe { slice::from_raw_parts( - self.as_ptr().cast(), - self.len(), - ) } + unsafe { slice::from_raw_parts(self.as_ptr().cast(), self.len()) } } #[inline] pub fn to_mut_bytes(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut( - self.as_mut_ptr().cast(), - self.len(), - ) } + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) } } #[inline] @@ -89,28 +73,26 @@ impl CStr { #[macro_export] macro_rules! cstr { - ($s:expr) => ( + ($s:expr) => { unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } - ) + }; } -pub struct CString { inner: CBox } +pub struct CString { + inner: Box, +} impl CString { - #[inline] - pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { - CString { inner: CBox::from_raw_unchecked(p) } - } - #[inline] pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { - util::check_ptr(p, 1); - CString::from_raw_unchecked(p) + CString { + inner: Box::from_raw(p), + } } #[inline] pub fn into_raw(self) -> *mut libc::c_char { - self.inner.into_raw() + Box::into_raw(self.inner) } } @@ -133,12 +115,10 @@ impl DerefMut for CString { impl From<&[u8]> for CString { fn from(s: &[u8]) -> CString { unsafe { - CString::from_raw_unchecked( - util::must_succeed(libc::strndup( - s.as_ptr().cast(), - s.len(), - )) - ) + CString::from_raw(util::must_succeed(libc::strndup( + s.as_ptr().cast(), + s.len(), + ))) } } } @@ -153,8 +133,6 @@ impl From<&str> for CString { impl From<&CStr> for CString { #[inline] fn from(s: &CStr) -> CString { - unsafe { - CString::from_raw_unchecked(util::must_succeed(libc::strdup(s.as_ptr()))) - } + unsafe { CString::from_raw(util::must_succeed(libc::strdup(s.as_ptr()))) } } } diff --git a/safe_libc/src/util.rs b/safe_libc/src/util.rs index b8a4ed5..e91494e 100644 --- a/safe_libc/src/util.rs +++ b/safe_libc/src/util.rs @@ -1,16 +1,3 @@ -use core::mem; - -#[inline] -pub fn zst(len: usize) -> bool { - mem::size_of::() == 0 || len == 0 -} - -#[inline] -pub fn check_ptr(p: *const T, len: usize) { - debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); - assert!(zst::(len) || !p.is_null(), "NULL ptr"); -} - #[inline] pub fn must_succeed(p: *mut T) -> *mut T { assert!(!p.is_null(), "allocation failure"); diff --git a/src/main.rs b/src/main.rs index 26882ff..fe151fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ #![no_main] #![no_std] +#![feature(alloc_error_handler)] -use safe_libc as libc; +mod system_alloc; + +extern crate alloc; use libc::cstr; +use safe_libc as libc; use core::fmt::Write; @@ -19,17 +23,20 @@ pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) - let x = libc::string::CString::from("foo"); let l = x.len(); let y = x.into_raw(); - let z = unsafe { - libc::boxed::CBox::slice_from_raw_parts(y, l) - }; + let z = unsafe { alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut(y, l)) }; let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); } { - let b = libc::boxed::CBox::new(42); + let b = alloc::boxed::Box::new(42); let _ = writeln!(stdout, "Bar: {}", b); } + { + let x = alloc::boxed::Box::new(()); + let _ = writeln!(stdout, "Box: {:?}", x); + } + 0 } diff --git a/src/system_alloc.rs b/src/system_alloc.rs new file mode 100644 index 0000000..ad1afcb --- /dev/null +++ b/src/system_alloc.rs @@ -0,0 +1,21 @@ +use safe_libc as libc; + +struct System; + +unsafe impl alloc::alloc::GlobalAlloc for System { + unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 { + libc::memalign(layout.align(), layout.size()).cast() + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: alloc::alloc::Layout) { + libc::free(ptr.cast()); + } +} + +#[global_allocator] +static SYSTEM_ALLOC: System = System; + +#[alloc_error_handler] +fn alloc_error(_: core::alloc::Layout) -> ! { + panic!("allocation failure"); +} From a205bbb1d286d78efbb325dac1d465c6908e0026 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 1 May 2020 22:21:17 +0200 Subject: [PATCH 22/29] Use extern type for CStr --- safe_libc/src/boxed.rs | 78 +++++++++++++++++++++++++++++++++++++++++ safe_libc/src/lib.rs | 3 +- safe_libc/src/string.rs | 36 ++++++++++--------- src/main.rs | 5 +++ 4 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 safe_libc/src/boxed.rs diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs new file mode 100644 index 0000000..a0bbfb4 --- /dev/null +++ b/safe_libc/src/boxed.rs @@ -0,0 +1,78 @@ +use core::{marker, mem, ptr}; +use core::ops::{Deref, DerefMut}; + +pub trait Dealloc { + unsafe fn dealloc(p: *mut T); +} + +#[repr(transparent)] +pub struct BoxLike> { + inner: ptr::NonNull, + _phantom: marker::PhantomData, + _dropper: marker::PhantomData, +} + +impl> BoxLike { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut T) -> Self { + BoxLike { + inner: ptr::NonNull::new_unchecked(p), + _phantom: marker::PhantomData, + _dropper: marker::PhantomData, + } + } + + #[inline] + pub unsafe fn from_raw(p: *mut T) -> Option { + if p.is_null() { + None + } else { + Some(Self::from_raw_unchecked(p)) + } + } + + #[inline] + pub fn into_raw(self) -> *mut T { + let p = self.inner.as_ptr(); + mem::forget(self); + p + } +} + +impl> Drop for BoxLike { + #[inline] + fn drop(&mut self) { + let p = self.inner.as_ptr(); + unsafe { + ptr::drop_in_place(p); + D::dealloc(p); + } + } +} + +impl> Deref for BoxLike { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + unsafe { self.inner.as_ref() } + } +} + +impl> DerefMut for BoxLike { + #[inline] + fn deref_mut(&mut self) -> &mut T { + unsafe { self.inner.as_mut() } + } +} + +pub struct DeallocFree; + +impl Dealloc for DeallocFree { + #[inline] + unsafe fn dealloc(p: *mut T) { + libc::free(p as _); + } +} + +pub type CBox = BoxLike; diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index 6d9baf2..2e39b8d 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] +#![feature(extern_types)] #![feature(slice_ptr_len)] -#![feature(min_specialization)] // #![feature(ffi_const)] extern crate alloc; @@ -9,6 +9,7 @@ pub use libc::*; mod util; +pub mod boxed; pub mod errno; pub mod stdio; pub mod string; diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index 6a8308a..5483e08 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -1,24 +1,26 @@ -use crate::{self as libc, util}; +use crate::{self as libc, boxed::CBox, util}; -use alloc::boxed::Box; -use core::{ops::{Deref, DerefMut}, slice}; +use core::{mem, slice}; +use core::ops::{Deref, DerefMut}; //pub struct FromBytesWithNulError {} -#[repr(transparent)] -pub struct CStr { - inner: libc::c_char, +extern "C" { + type RawCStr; } +#[repr(transparent)] +pub struct CStr(RawCStr); + impl CStr { #[inline] pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { - &*p.cast() + &*(p as *const CStr) } #[inline] pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { - &mut *p.cast() + &mut *(p as *mut CStr) } #[inline] @@ -37,12 +39,12 @@ impl CStr { #[inline] pub const fn as_ptr(&self) -> *const libc::c_char { - &self.inner + (self as *const CStr).cast() } #[inline] pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { - &mut self.inner + (self as *mut CStr).cast() } #[inline] @@ -79,20 +81,22 @@ macro_rules! cstr { } pub struct CString { - inner: Box, + inner: CBox, } impl CString { #[inline] pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { CString { - inner: Box::from_raw(p), + inner: CBox::from_raw_unchecked(p as *mut CStr) } } #[inline] - pub fn into_raw(self) -> *mut libc::c_char { - Box::into_raw(self.inner) + pub fn into_raw(mut self) -> *mut libc::c_char { + let p = self.as_mut_ptr(); + mem::forget(self); + p } } @@ -101,14 +105,14 @@ impl Deref for CString { #[inline] fn deref(&self) -> &CStr { - unsafe { CStr::from_ptr(&*self.inner) } + &*self.inner } } impl DerefMut for CString { #[inline] fn deref_mut(&mut self) -> &mut CStr { - unsafe { CStr::from_mut_ptr(&mut *self.inner) } + &mut *self.inner } } diff --git a/src/main.rs b/src/main.rs index fe151fe..c6ec961 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,11 @@ pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) - let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); } + { + let x = libc::string::CString::from("bar"); + let _ = stdout.puts(&x); + } + { let b = alloc::boxed::Box::new(42); let _ = writeln!(stdout, "Bar: {}", b); From 4f5b90400e414e77f067b8831e3296ce569352ed Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 1 May 2020 23:57:16 +0200 Subject: [PATCH 23/29] util: use handle_alloc_error() --- safe_libc/src/util.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/safe_libc/src/util.rs b/safe_libc/src/util.rs index e91494e..d37954c 100644 --- a/safe_libc/src/util.rs +++ b/safe_libc/src/util.rs @@ -1,5 +1,9 @@ +use alloc::alloc; + #[inline] pub fn must_succeed(p: *mut T) -> *mut T { - assert!(!p.is_null(), "allocation failure"); + if p.is_null() { + alloc::handle_alloc_error(alloc::Layout::new::()) + } p } From acafb83a63b4f99b3340b84d8e1ea3ba267bd80a Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 1 May 2020 23:58:57 +0200 Subject: [PATCH 24/29] Update libc --- Cargo.lock | 4 ++-- safe_libc/Cargo.lock | 4 ++-- safe_libc/Cargo.toml | 2 +- safe_libc/src/lib.rs | 2 -- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45e6ba1..81b8dd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" [[package]] name = "neco" diff --git a/safe_libc/Cargo.lock b/safe_libc/Cargo.lock index e659af2..b20b067 100644 --- a/safe_libc/Cargo.lock +++ b/safe_libc/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" [[package]] name = "safe_libc" diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml index f776d09..3741877 100644 --- a/safe_libc/Cargo.toml +++ b/safe_libc/Cargo.toml @@ -7,4 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = { version = "0.2.68", default-features = false } +libc = { version = "0.2.69", default-features = false } diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index 2e39b8d..9356406 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -15,8 +15,6 @@ pub mod stdio; pub mod string; extern "C" { - pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; - // pub static stdin: *mut libc::FILE; pub static stdout: *mut libc::FILE; pub static stderr: *mut libc::FILE; From 85d52f3db69126ad9d374f03e32c22fded058570 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 19 May 2020 00:54:23 +0200 Subject: [PATCH 25/29] Fix --all-targets build --- src/main.rs | 1 + src/system_alloc.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index c6ec961..4283396 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,7 @@ pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) - 0 } +#[cfg(not(test))] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info); diff --git a/src/system_alloc.rs b/src/system_alloc.rs index ad1afcb..7c0bac9 100644 --- a/src/system_alloc.rs +++ b/src/system_alloc.rs @@ -15,6 +15,7 @@ unsafe impl alloc::alloc::GlobalAlloc for System { #[global_allocator] static SYSTEM_ALLOC: System = System; +#[cfg(not(test))] #[alloc_error_handler] fn alloc_error(_: core::alloc::Layout) -> ! { panic!("allocation failure"); From 806ba1d0a866da7a60ca4e94b7209e85250e396c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 24 May 2020 15:43:57 +0200 Subject: [PATCH 26/29] Update libc --- Cargo.lock | 4 ++-- safe_libc/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81b8dd6..e32d3ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "neco" diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml index 3741877..03d9b94 100644 --- a/safe_libc/Cargo.toml +++ b/safe_libc/Cargo.toml @@ -7,4 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = { version = "0.2.69", default-features = false } +libc = { version = "0.2.70", default-features = false } From daf595fe5648250c2947947ba5d9cdf4ab24b403 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 24 May 2020 15:52:35 +0200 Subject: [PATCH 27/29] stdio: nicer error handling --- safe_libc/src/stdio.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index be016ab..2942405 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -7,6 +7,12 @@ pub struct Error { pub errno: errno::Errno, } +impl From for fmt::Error { + fn from(_err: Error) -> fmt::Error { + fmt::Error + } +} + pub type Result = core::result::Result; #[inline] @@ -58,9 +64,7 @@ impl BasicOStream { impl fmt::Write for BasicOStream { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { - if self.write(s.as_bytes()).is_err() { - return Err(fmt::Error); - } + self.write(s.as_bytes())?; Ok(()) } } From 554275d78187657f2ff7099f8cffe66aa48289fd Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 30 May 2020 10:22:12 +0200 Subject: [PATCH 28/29] Update libc, remove redundant Cargo.lock --- Cargo.lock | 4 ++-- safe_libc/Cargo.lock | 14 -------------- safe_libc/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 safe_libc/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index e32d3ab..8675aaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "libc" -version = "0.2.70" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" [[package]] name = "neco" diff --git a/safe_libc/Cargo.lock b/safe_libc/Cargo.lock deleted file mode 100644 index b20b067..0000000 --- a/safe_libc/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "libc" -version = "0.2.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" - -[[package]] -name = "safe_libc" -version = "0.1.0" -dependencies = [ - "libc", -] diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml index 03d9b94..34f8f79 100644 --- a/safe_libc/Cargo.toml +++ b/safe_libc/Cargo.toml @@ -7,4 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = { version = "0.2.70", default-features = false } +libc = { version = "0.2.71", default-features = false } From 58260af4d8afd2079a50f2444e8bf721044d7a35 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 30 May 2020 10:22:35 +0200 Subject: [PATCH 29/29] Enable #[ffi_const] --- safe_libc/src/errno.rs | 2 +- safe_libc/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/safe_libc/src/errno.rs b/safe_libc/src/errno.rs index 2a2220b..fdb1246 100644 --- a/safe_libc/src/errno.rs +++ b/safe_libc/src/errno.rs @@ -7,7 +7,7 @@ use core::fmt; pub struct Errno(pub libc::c_int); extern "C" { - //#[ffi_const] + #[ffi_const] pub fn __errno_location() -> *mut libc::c_int; } diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index 9356406..cbc4071 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(extern_types)] #![feature(slice_ptr_len)] -// #![feature(ffi_const)] +#![feature(ffi_const)] extern crate alloc;