From f1e579bc427df418af96da61498fe0cdb4cecb69 Mon Sep 17 00:00:00 2001 From: dautor Date: Sun, 17 Nov 2024 20:53:51 +0100 Subject: Add COPYRIGHT and rename module directories --- .gitignore | 1 - COPYRIGHT | 20 ++ build.sh | 5 +- src/module.bridge/main.c | 166 +++++++++++++++ src/module.bridge/state.c | 58 +++++ src/module.bridge/state.h | 20 ++ src/module.eiface/main.c | 155 ++++++++++++++ src/module.eiface/state.c | 63 ++++++ src/module.eiface/state.h | 21 ++ src/module.jail/main.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++ src/module.jail/state.c | 396 ++++++++++++++++++++++++++++++++++ src/module.jail/state.h | 93 ++++++++ src/module.skel/main.c | 116 ++++++++++ src/module.skel/state.c | 49 +++++ src/module.skel/state.h | 19 ++ src/sf.bridge/main.c | 166 --------------- src/sf.bridge/state.c | 58 ----- src/sf.bridge/state.h | 20 -- src/sf.eiface/main.c | 155 -------------- src/sf.eiface/state.c | 63 ------ src/sf.eiface/state.h | 21 -- src/sf.jail/main.c | 525 ---------------------------------------------- src/sf.jail/state.c | 396 ---------------------------------- src/sf.jail/state.h | 93 -------- src/sf.skel/main.c | 116 ---------- src/sf.skel/state.c | 49 ----- src/sf.skel/state.h | 19 -- 27 files changed, 1704 insertions(+), 1684 deletions(-) create mode 100644 COPYRIGHT create mode 100644 src/module.bridge/main.c create mode 100644 src/module.bridge/state.c create mode 100644 src/module.bridge/state.h create mode 100644 src/module.eiface/main.c create mode 100644 src/module.eiface/state.c create mode 100644 src/module.eiface/state.h create mode 100644 src/module.jail/main.c create mode 100644 src/module.jail/state.c create mode 100644 src/module.jail/state.h create mode 100644 src/module.skel/main.c create mode 100644 src/module.skel/state.c create mode 100644 src/module.skel/state.h delete mode 100644 src/sf.bridge/main.c delete mode 100644 src/sf.bridge/state.c delete mode 100644 src/sf.bridge/state.h delete mode 100644 src/sf.eiface/main.c delete mode 100644 src/sf.eiface/state.c delete mode 100644 src/sf.eiface/state.h delete mode 100644 src/sf.jail/main.c delete mode 100644 src/sf.jail/state.c delete mode 100644 src/sf.jail/state.h delete mode 100644 src/sf.skel/main.c delete mode 100644 src/sf.skel/state.c delete mode 100644 src/sf.skel/state.h diff --git a/.gitignore b/.gitignore index a04fdd5..90f85fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ getmntopts.o sf.* -!src/sf.* diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..106f4c8 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,20 @@ +Copyright (c) 2024 Karlo Milicevic + +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 AUTHOR 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 AUTHOR 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. diff --git a/build.sh b/build.sh index e60f258..ae62e50 100755 --- a/build.sh +++ b/build.sh @@ -13,8 +13,9 @@ LDFLAGS="$LDFLAGS -L/usr/local/lib -lucl -lnetgraph -ljail -lutil getmntopts.o" $CC -c /usr/src/sbin/mount/getmntopts.c -o getmntopts.o -for i in $(ls -d src/sf.*); do - $CC $CFLAGS $SAN src/*.c src/module/module.c $i/*.c $LDFLAGS -o $(basename $i) +for i in $(ls -d src/module.*); do + name="$(basename $i)" + $CC $CFLAGS $SAN src/*.c src/module/module.c $i/*.c $LDFLAGS -o "sf.${name##*.}" done $CC $CFLAGS $SAN src/*.c src/base/*.c $LDFLAGS $LIBS -o sf.base diff --git a/src/module.bridge/main.c b/src/module.bridge/main.c new file mode 100644 index 0000000..fbfb500 --- /dev/null +++ b/src/module.bridge/main.c @@ -0,0 +1,166 @@ +#include "../module/module.h" +#include "../util.h" +#include "state.h" + +#include + +char const *Usage = "usage: %1$s start [name] [configuration]\n" + " %1$s stop [name] [configuration] [data]\n" + " %1$s get-endpoint [name] [configuration] [data] [interface]\n" + "unsupported commands: cmd mod\n"; + +static configuration Configuration; +static data Data; +static bool ConfigurationLoaded; +static bool DataLoaded; + +// HELPERS + +static void +Free_all(void) +{ + if(ConfigurationLoaded == true) Free_configuration(&Configuration); + if(DataLoaded == true) Free_data(&Data); +} + +static void +Load_configuration(ucl_object_t *root) +{ + char *Error = Parse_configuration(&Configuration, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + ConfigurationLoaded = true; +} + +static void +Load_data(ucl_object_t *root) +{ + char *Error = Parse_data(&Data, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: data error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + DataLoaded = true; + // TODO: check that data matches configuration +} + +// COMMANDS + +int +start(char const *Name, ucl_object_t *configuration) +{ + UNUSED(Name); + Load_configuration(configuration); + // Implement + Data.ID = 0; + fd Control = -1; + if(NgMkSockNode(NULL, &Control, NULL) == -1) + { + fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); + goto error; + } + if(Create_bridge(&Data.ID, Control) == -1) goto error; + close(Control); + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + Save_data(&S, &Data); + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) +{ + UNUSED(Name); + Load_configuration(configuration); + Load_data(data); + fd Control = -1; + if(NgMkSockNode(NULL, &Control, NULL) == -1) + { + fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); + goto error; + } + if(DestroyNetgraphNode(Data.ID, Control) == -1) goto error; + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) +{ + UNUSED(Name); + Load_configuration(configuration); + Load_data(data); + size_t linkNum; + if(strncmp(Interface, "link", __builtin_strlen("link")) == 0) + { + char const *N = Interface + __builtin_strlen("link"); + linkNum = strtoul(N, NULL, 10); + if(linkNum == 0 && errno == EINVAL) + { + fprintf(stderr, "invalid interface %s\n", Interface); + goto error; + } + } else if(strncmp(Interface, "uplink", __builtin_strlen("uplink")) == 0) + { + char const *N = Interface + __builtin_strlen("uplink"); + linkNum = strtoul(N, NULL, 10); + if(linkNum == 0) + { + fprintf(stderr, "unknown interface %s\n", Interface); + goto error; + } + } else + { + fprintf(stderr, "unknown interface %s\n", Interface); + } + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + JPrintObjectBegin(&S); + JPrintMember(&S, "address"); + char Address[NG_PATHSIZ]; + snprintf(Address, sizeof(Address), "[%x]:", Data.ID); + JPrint_string(&S, Address); + JPrintMember(&S, "hook"); + JPrint_string(&S, Interface); + JPrintObjectEnd(&S); + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + UNUSED(Name, ArgCount, Arg); + Load_configuration(configuration); + Load_data(data); + usage(); +} + +int +mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + UNUSED(Name, ArgCount, Arg); + Load_configuration(configuration); + Load_data(data); + usage(); +} diff --git a/src/module.bridge/state.c b/src/module.bridge/state.c new file mode 100644 index 0000000..c7780f3 --- /dev/null +++ b/src/module.bridge/state.c @@ -0,0 +1,58 @@ +#include "state.h" + +// FREE + +void +Free_configuration(configuration *E) +{ + UNUSED(E); +} + +void +Free_data(data *E) +{ + UNUSED(E); +} + +// PARSE + +char * +Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + UNUSED(E); + return NULL; +error: + return Error; +} + +char * +Parse_data(data *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + ucl_object_t const *id = ucl_object_lookup(root, "id"); + UCL_CHECK(id, INT); + s64 ID = ucl_object_toint(id); + if(ID < 0 || ID > UINT32_MAX) + { + asprintf(&Error, "%s id invalid (%ld)", Position, ID); + goto error; + } + E->ID = (u32)ID; + return NULL; +error: + return Error; +} + +// SAVE + +void +Save_data(jprint_state *S, data const *E) +{ + JPrintObjectBegin(S); + JPrintMember(S, "id"); + JPrint_ssize_t(S, E->ID); + JPrintObjectEnd(S); +} diff --git a/src/module.bridge/state.h b/src/module.bridge/state.h new file mode 100644 index 0000000..7ef6243 --- /dev/null +++ b/src/module.bridge/state.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../jprint.h" + +struct +{ +} typedef configuration; + +struct +{ + u32 ID; +} typedef data; + +void Free_configuration(configuration *); +void Free_data(data *); + +char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); +char *Parse_data(data *, ucl_object_t const *, char const *Position); + +void Save_data(jprint_state *, data const *); diff --git a/src/module.eiface/main.c b/src/module.eiface/main.c new file mode 100644 index 0000000..4fafa81 --- /dev/null +++ b/src/module.eiface/main.c @@ -0,0 +1,155 @@ +#include "../module/module.h" +#include "../util.h" +#include "state.h" + +#include + +char const *Usage = "usage: %1$s start [name] [configuration]\n" + " %1$s stop [name] [configuration] [data]\n" + " %1$s get-endpoint [name] [configuration] [data] [interface]\n" + "unsupported commands: cmd mod\n"; + +static configuration Configuration; +static data Data; +static bool ConfigurationLoaded; +static bool DataLoaded; + +// HELPERS + +static void +Free_all(void) +{ + if(ConfigurationLoaded == true) Free_configuration(&Configuration); + if(DataLoaded == true) Free_data(&Data); +} + +static void +Load_configuration(ucl_object_t *root) +{ + char *Error = Parse_configuration(&Configuration, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + ConfigurationLoaded = true; +} + +static void +Load_data(ucl_object_t *root) +{ + char *Error = Parse_data(&Data, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: data error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + DataLoaded = true; + // TODO: check that data matches configuration +} + +// COMMANDS + +int +start(char const *Name, ucl_object_t *configuration) +{ + UNUSED(Name); + Load_configuration(configuration); + Data.ID = 0; + fd Control = -1; + if(NgMkSockNode(NULL, &Control, NULL) == -1) + { + fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); + goto error; + } + if(Create_eiface(&Data.ID, Control) == -1) goto error; + char *InterfaceName = getifname(Data.ID, Control); + if(InterfaceName == NULL) goto error; + int Result = command("ifconfig", "ifconfig", InterfaceName, "name", Name, NULL); + free(InterfaceName); + if(Result == -1) goto error; + close(Control); + if(Configuration.inet != NULL) command("ifconfig", "ifconfig", Name, "inet", Configuration.inet, NULL); + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + Save_data(&S, &Data); + Free_all(); + return 0; +error: + if(Data.ID != 0) DestroyNetgraphNode(Data.ID, Control); + if(Control != -1) close(Control); + Free_all(); + return -1; +} + +int +stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) +{ + UNUSED(Name); + Load_configuration(configuration); + Load_data(data); + fd Control = -1; + if(NgMkSockNode(NULL, &Control, NULL) == -1) + { + fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); + goto error; + } + if(DestroyNetgraphNode(Data.ID, Control) == -1) goto error; + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) +{ + UNUSED(Name); + Load_configuration(configuration); + Load_data(data); + if(*Interface != '\0') + { + fprintf(stderr, "%s provides only empty interface name\n", Arg0); + goto error; + } + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + JPrintObjectBegin(&S); + JPrintMember(&S, "address"); + char Address[NG_PATHSIZ]; + snprintf(Address, sizeof(Address), "[%x]:", Data.ID); + JPrint_string(&S, Address); + JPrintMember(&S, "hook"); + JPrint_string(&S, "ether"); + JPrintObjectEnd(&S); + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + UNUSED(Name, ArgCount, Arg); + Load_configuration(configuration); + Load_data(data); + usage(); +} + +int +mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + UNUSED(Name, ArgCount, Arg); + Load_configuration(configuration); + Load_data(data); + usage(); +} diff --git a/src/module.eiface/state.c b/src/module.eiface/state.c new file mode 100644 index 0000000..7d85056 --- /dev/null +++ b/src/module.eiface/state.c @@ -0,0 +1,63 @@ +#include "state.h" + +// FREE + +void +Free_configuration(configuration *E) +{ + if(E->inet != NULL) free(E->inet); +} + +void +Free_data(data *E) +{ + UNUSED(E); +} + +// PARSE + +char * +Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + ucl_object_t const *inet = ucl_object_lookup(root, "inet"); + if(inet != NULL) + { + UCL_CHECK(inet, STRING); + E->inet = strdup(ucl_object_tostring(inet)); + } + return NULL; +error: + return Error; +} + +char * +Parse_data(data *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + ucl_object_t const *id = ucl_object_lookup(root, "id"); + UCL_CHECK(id, INT); + s64 ID = ucl_object_toint(id); + if(ID < 0 || ID > UINT32_MAX) + { + asprintf(&Error, "%s id invalid (%ld)", Position, ID); + goto error; + } + E->ID = (u32)ID; + return NULL; +error: + return Error; +} + +// SAVE + +void +Save_data(jprint_state *S, data const *E) +{ + JPrintObjectBegin(S); + JPrintMember(S, "id"); + JPrint_ssize_t(S, E->ID); + JPrintObjectEnd(S); +} diff --git a/src/module.eiface/state.h b/src/module.eiface/state.h new file mode 100644 index 0000000..0431fdf --- /dev/null +++ b/src/module.eiface/state.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../jprint.h" + +struct +{ + char *inet; +} typedef configuration; + +struct +{ + u32 ID; +} typedef data; + +void Free_configuration(configuration *); +void Free_data(data *); + +char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); +char *Parse_data(data *, ucl_object_t const *, char const *Position); + +void Save_data(jprint_state *, data const *); diff --git a/src/module.jail/main.c b/src/module.jail/main.c new file mode 100644 index 0000000..d71511b --- /dev/null +++ b/src/module.jail/main.c @@ -0,0 +1,525 @@ +#include "../module/module.h" +#include "../util.h" +#include "state.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +char const *Usage = "usage: %1$s start [name] [configuration]\n" + " %1$s stop [name] [configuration] [data]\n" + " %1$s get-endpoint [name] [configuration] [data] [interface]\n" + " %1$s cmd [name] [configuration] [data] sh\n" + " %1$s cmd [name] [configuration] [data] wireshark [interface]\n" + "unsupported commands: mod\n"; + +static configuration Configuration; +static data Data; +static bool ConfigurationLoaded; +static bool DataLoaded; + +// HELPERS + +static void +Free_all(void) +{ + if(ConfigurationLoaded == true) Free_configuration(&Configuration); + if(DataLoaded == true) Free_data(&Data); +} + +static void +Load_configuration(ucl_object_t *root) +{ + char *Error = Parse_configuration(&Configuration, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + ConfigurationLoaded = true; +} + +static void +Load_data(ucl_object_t *root) +{ + char *Error = Parse_data(&Data, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: data error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + DataLoaded = true; + // TODO: check that data matches configuration +} + +static int +start_interface(interface *E, interface_configuration *C, fd Control, char const *JailName) +{ + E->Type = C->Type; + switch(C->Type) + { + case interface_type_eiface: + { + if(Create_eiface(&E->ID, Control) == -1) return -1; + char *InterfaceName = getifname(E->ID, Control); + if(InterfaceName == NULL) + { + DestroyNetgraphNode(E->ID, Control); + return -1; + } + // NOTE: This first renames the interface and only then vnets it. + int Result = + command("ifconfig", "ifconfig", InterfaceName, "name", C->Name, "vnet", JailName, NULL); + free(InterfaceName); + return Result; + } + case interface_type_steal: + { + return command("ifconfig", "ifconfig", C->steal.Interface, "name", C->Name, "vnet", JailName, NULL); + } + case interface_type_COUNT: + default: __builtin_unreachable(); + } +} + +static int +stop_interface(interface *E, interface_configuration *C, fd Control, char const *JailName) +{ + switch(C->Type) + { + case interface_type_eiface: return DestroyNetgraphNode(E->ID, Control); + case interface_type_steal: + { + return command("ifconfig", + "ifconfig", + C->Name, + "-vnet", + JailName, + "name", + C->steal.Interface, + NULL); + } + case interface_type_COUNT: + default: __builtin_unreachable(); + } +} + +static int +stop_interfaces(interfaces *E, interfaces_configuration *C, fd Control, char const *JailName) +{ + for(size_t i = 0; i < E->Count; ++i) + { + if(stop_interface(E->_ + i, C->_ + i, Control, JailName) == -1) + { + fprintf(stderr, "failed to stop interface '%s'\n", C->_[i].Name); + return -1; + } + } + return 0; +} + +static int +start_interfaces(interfaces *E, interfaces_configuration *C, fd Control, char const *Name) +{ + Data.Interfaces._ = calloc(sizeof(interface), Configuration.Interfaces.Count); + for(E->Count = 0; E->Count < C->Count; ++E->Count) + { + interface *E_ = E->_ + E->Count; + interface_configuration *C_ = C->_ + E->Count; + if(start_interface(E_, C_, Control, Name) == -1) goto error; + } + return 0; +error: + stop_interfaces(E, C, Control, Name); + Free_interfaces(E); + return -1; +} + +static int +start_jail(jid_t *JID, char const *Name, char const *Path) +{ + *JID = jail_setv(JAIL_CREATE, "name", Name, "vnet", "new", "persist", "true", "path", Path, NULL); + if(*JID == -1) + { + fprintf(stderr, "jail creation failed: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +static int +stop_jail(jid_t JID) +{ + if(jail_remove(JID) == -1) + { + fprintf(stderr, "jail removal failed: %s\n", strerror(errno)); + Free_all(); + return -1; + } + return 0; +} + +NAMED_ENUM(mapping_type, unionfs_below, unionfs, devfs, nullfs); + +static int +mapping_mount(mapping *M, char const *TargetPrefix, mapping_type Type) +{ + char Target[MAXPATHLEN]; + snprintf(Target, sizeof(Target), "%s/%s", TargetPrefix, M->Target); + if(mkdir(Target, 755) == -1) + { + if(errno != EEXIST) + { + fprintf(stderr, "mkdir %s: %s\n", Target, strerror(errno)); + return -1; + } + } + switch(Type) + { + case mapping_type_unionfs_below: return mount_unionfs(Target, M->Source, true); + case mapping_type_unionfs: return mount_unionfs(Target, M->Source, false); + case mapping_type_devfs: return mount_devfs(Target); + case mapping_type_nullfs: return mount_nullfs(Target, M->Source); + case mapping_type_COUNT: + default: __builtin_unreachable(); + } +} + +static int +mapping_umount(char const *TargetPrefix, char const *Target_) +{ + char Target[MAXPATHLEN]; + snprintf(Target, sizeof(Target), "%s/%s", TargetPrefix, Target_); + // NOTE: unmount fails because some processes somehow still hold a reference to the jail FS + while(unmount(Target, MNT_FORCE) == -1) + { + fprintf(stderr, "unmount %s failed: %s\n", Target, strerror(errno)); + if(errno != EBUSY) return -1; + sleep(1); + } + return 0; +} + +static int +rm(char const *Path) +{ + pid_t PID = fork(); + if(PID == -1) return -1; + if(PID == 0) + { + close(STDIN_FILENO); + dup2(STDERR_FILENO, STDOUT_FILENO); + close_all(); + fprintf(stderr, "rm -r %s\n", Path); + execlp("rm", "rm", "-r", Path, NULL); + exit(-1); + } + int Exit; + waitpid(PID, &Exit, 0); + if(!WIFEXITED(Exit) || (WEXITSTATUS(Exit) != 0)) return -1; + return 0; +} + +static int +ApplyDevfsRuleset(char const *Prefix, s64 Ruleset) +{ + if(Ruleset == 0) return 0; + devfs_rsnum RS = (devfs_rsnum)Ruleset; + if(RS != Ruleset) + { + fprintf(stderr, "Ruleset invalid: %ld\n", Ruleset); + return -1; + } + char Path[MAXPATHLEN]; + snprintf(Path, sizeof(Path), "%s/dev", Prefix); + fd DevFD = open(Path, O_RDONLY); + if(DevFD == -1) + { + fprintf(stderr, "open '%s' failed: %s\n", Path, strerror(errno)); + return -1; + } + if(ioctl(DevFD, DEVFSIO_SAPPLY, &RS) == -1) + { + fprintf(stderr, "ruleset %hu apply failed: %s\n", RS, strerror(errno)); + close(DevFD); + return -1; + } + close(DevFD); + return 0; +} + +static int +start_fs(filesystem_configuration *E, char const *Path) +{ + bool CreatedPath = false; + size_t LayerAt = 0; + size_t VolumeAt = 0; + bool Mounted_devfs = false; + bool Mounted_temporary = false; + if(mkdir(Path, 0755) == -1) + { + fprintf(stderr, "mkdir %s: %s\n", Path, strerror(errno)); + goto error; + } + CreatedPath = true; + for(LayerAt = 0; LayerAt < E->LayerCount; ++LayerAt) + { + if(mapping_mount(&(mapping){ .Source = E->Layer[LayerAt], .Target = "/" }, + Path, + mapping_type_unionfs_below) == -1) + goto error; + } + if(E->Temporary != NULL) + { + if(mkdir(E->Temporary, 755) == -1) + { + if(errno != EEXIST) + { + fprintf(stderr, "mkdir %s: %s\n", E->Temporary, strerror(errno)); + goto error; + } + } + if(mapping_mount(&(mapping){ .Source = E->Temporary, .Target = "/" }, Path, mapping_type_unionfs) == -1) + goto error; + } + Mounted_temporary = true; + if(mapping_mount(&(mapping){ .Target = "/dev" }, Path, mapping_type_devfs) == -1) goto error; + if(ApplyDevfsRuleset(Path, E->DevfsRuleset) == -1) goto error; + for(VolumeAt = 0; VolumeAt < E->VolumeCount; ++VolumeAt) + { + if(mapping_mount(E->Volume + VolumeAt, Path, mapping_type_nullfs) == -1) goto error; + } + return 0; +error: + for(size_t i = 0; i < VolumeAt; ++i) mapping_umount(Path, E->Volume[E->VolumeCount - 1 - i].Target); + if(Mounted_devfs) mapping_umount(Path, "/dev"); + if(Mounted_temporary) mapping_umount(Path, "/"); + for(size_t i = 0; i < LayerAt; ++i) mapping_umount(Path, "/"); + if(CreatedPath) rm(Path); + return -1; +} + +static int +stop_fs(filesystem_configuration *E, char const *Path) +{ + for(size_t i = 0; i < E->VolumeCount; ++i) mapping_umount(Path, E->Volume[E->VolumeCount - 1 - i].Target); + mapping_umount(Path, "/dev"); + if(E->Temporary != NULL) mapping_umount(Path, "/"); + for(size_t i = 0; i < E->LayerCount; ++i) mapping_umount(Path, "/"); + if(rm(Path) == -1) + { + fprintf(stderr, "rm -r %s: %s\n", Path, strerror(errno)); + goto error; + } + return 0; +error: + return -1; +} + +static bool +ValidJailName(char const *Name) +{ + for(char const *At = Name; *At; ++At) + { + if(isnumber(*At) == false) return true; + } + return true; +} + +// COMMANDS + +static char const *FilesystemPrefix = "/tmp/sf"; + +int +start(char const *Name, ucl_object_t *configuration) +{ + if(ValidJailName(Name) == false) return -1; + Load_configuration(configuration); + fd Control = -1; + Data.JID = -1; + char Path[PATH_MAX]; + snprintf(Path, sizeof(Path), "%s/%s", FilesystemPrefix, Name); + if(mkdir(FilesystemPrefix, 0755) == -1) + { + if(errno != EEXIST) + { + fprintf(stderr, "mkdir %s: %s\n", FilesystemPrefix, strerror(errno)); + goto error; + } + } + if(start_fs(&Configuration.Filesystem, Path) == -1) goto error; + if(start_jail(&Data.JID, Name, Path) == -1) goto error; + if(NgMkSockNode(NULL, &Control, NULL) == -1) + { + fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); + goto error; + } + if(start_interfaces(&Data.Interfaces, &Configuration.Interfaces, Control, Name) == -1) + { + fprintf(stderr, "failed to start interfaces\n"); + goto error; + } + DataLoaded = true; + close(Control); + exec_string_array(&Configuration.Init, false, Data.JID); + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + Save_data(&S, &Data); + Free_all(); + return 0; +error: + if(Control != -1) close(Control); + if(Data.JID != -1) stop_jail(Data.JID); + Free_all(); + return -1; +} + +int +stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) +{ + if(ValidJailName(Name) == false) return -1; + Load_configuration(configuration); + Load_data(data); + exec_string_array(&Configuration.Shutdown, true, Data.JID); + fd Control = -1; + if(NgMkSockNode(NULL, &Control, NULL) == -1) + { + fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); + goto error; + } + if(stop_interfaces(&Data.Interfaces, &Configuration.Interfaces, Control, Name) == -1) goto error; + if(stop_jail(Data.JID) == -1) goto error; + char Path[PATH_MAX]; + snprintf(Path, sizeof(Path), "%s/%s", FilesystemPrefix, Name); + if(stop_fs(&Configuration.Filesystem, Path) == -1) goto error; + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) +{ + if(ValidJailName(Name) == false) return -1; + Load_configuration(configuration); + Load_data(data); + for(size_t i = 0; i < Configuration.Interfaces.Count; ++i) + { + interface_configuration *C = Configuration.Interfaces._ + i; + if(strcmp(C->Name, Interface) == 0) + { + interface *E = Data.Interfaces._ + i; + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + JPrintObjectBegin(&S); + JPrintMember(&S, "address"); + char Address[NG_PATHSIZ]; + snprintf(Address, sizeof(Address), "[%x]:", E->ID); + JPrint_string(&S, Address); + JPrintMember(&S, "hook"); + JPrint_string(&S, "ether"); + JPrintObjectEnd(&S); + Free_all(); + return 0; + } + } + Free_all(); + return -1; +} + +int +cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + if(ValidJailName(Name) == false) return -1; + Load_configuration(configuration); + Load_data(data); + if(ArgCount < 1) usage(); + char const *Command = Arg[0]; + --ArgCount; + ++Arg; + if(strcmp(Command, "sh") == 0) + { + size_t ExtraArguments = 2; + char **Args = alloca(sizeof(char *) * (ArgCount + ExtraArguments + 1)); + Args[0] = "jexec"; + Args[1] = strdup(Name); + memcpy(Args + ExtraArguments, Arg, sizeof(char *) * ArgCount); + Args[ArgCount + ExtraArguments] = NULL; + execvp("jexec", Args); + fprintf(stderr, "execvp failed: %s\n", strerror(errno)); + exit(-1); + } else if(strcmp(Command, "wireshark") == 0) + { + if(ArgCount < 1) usage(); + char const *Interface = Arg[0]; + --ArgCount; + ++Arg; + fd Pipe[2]; + if(pipe(Pipe) == -1) + { + fprintf(stderr, "pipe: %s\n", strerror(errno)); + return -1; + } + pid_t PID = fork(); + if(PID == -1) + { + fprintf(stderr, "fork: %s\n", strerror(errno)); + return -1; + } else if(PID == 0) + { + dup2(Pipe[1], STDOUT_FILENO); + close(Pipe[0]); + close(Pipe[1]); + close_all(); + if(jail_attach(Data.JID)) + { + fprintf(stderr, "jail_attach(%d): %s\n", Data.JID, strerror(errno)); + exit(-1); + } + execlp("tcpdump", "tcpdump", "-s", "-0", "-U", "-w", "-", "-i", Interface, NULL); + fprintf(stderr, "execlp failed: %s\n", strerror(errno)); + exit(-1); + } else + { + dup2(Pipe[0], STDIN_FILENO); + close(Pipe[0]); + close(Pipe[1]); + close_all(); + char Title[64]; + snprintf(Title, sizeof(Title), "gui.window_title:%s:%s", Name, Interface); + execlp("wireshark", "wireshark", "-o", Title, "-k", "-i", "-", NULL); + fprintf(stderr, "execlp failed: %s\n", strerror(errno)); + exit(-1); + } + } else + { + fprintf(stderr, "unknown command %s\n", Command); + usage(); + } +} + +int +mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + if(ValidJailName(Name) == false) return -1; + Load_configuration(configuration); + Load_data(data); + UNUSED(ArgCount, Arg); + usage(); +} diff --git a/src/module.jail/state.c b/src/module.jail/state.c new file mode 100644 index 0000000..893961e --- /dev/null +++ b/src/module.jail/state.c @@ -0,0 +1,396 @@ +#include "state.h" + +// FREE + +void +Free_interface_configuration(interface_configuration *E) +{ + free(E->Name); + switch(E->Type) + { + case interface_type_eiface: break; + case interface_type_steal: free(E->steal.Interface); break; + case interface_type_COUNT: + default: __builtin_unreachable(); + } +} + +void +Free_interfaces_configuration(interfaces_configuration *E) +{ + for(size_t i = 0; i < E->Count; ++i) Free_interface_configuration(E->_ + i); + free(E->_); +} + +void +Free_mapping(mapping *E) +{ + free(E->Source); + free(E->Target); +} + +void +Free_filesystem_configuration(filesystem_configuration *E) +{ + for(size_t i = 0; i < E->LayerCount; ++i) free(E->Layer[i]); + if(E->Layer != NULL) free(E->Layer); + if(E->Temporary != NULL) free(E->Temporary); + for(size_t i = 0; i < E->VolumeCount; ++i) Free_mapping(E->Volume + i); + if(E->Volume != NULL) free(E->Volume); +} + +void +Free_configuration(configuration *E) +{ + Free_filesystem_configuration(&E->Filesystem); + Free_interfaces_configuration(&E->Interfaces); + Free_string_array(&E->Init); + Free_string_array(&E->Shutdown); +} + +void +Free_interface(interface *E) +{ + UNUSED(E); +} + +void +Free_interfaces(interfaces *E) +{ + for(size_t i = 0; i < E->Count; ++i) Free_interface(E->_ + i); + free(E->_); +} + +void +Free_data(data *E) +{ + Free_interfaces(&E->Interfaces); +} + +// PARSE + +char * +Parse_interface_type(interface_type *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(STRING); + char const *Type = ucl_object_tostring(root); + for(interface_type i = 0; i < interface_type_COUNT; ++i) + { + if(strcmp(interface_type_Names[i], Type) == 0) + { + *E = i; + return NULL; + } + } + asprintf(&Error, "%s invalid interface type '%s'", Position, Type); + goto error; +error: + return Error; +} + +char * +Parse_interface_configuration(interface_configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + { + char *NewPosition; + asprintf(&NewPosition, "%s.type", Position); + Error = Parse_interface_type(&E->Type, ucl_object_lookup(root, "type"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + switch(E->Type) + { + case interface_type_eiface: break; + case interface_type_steal: + { + ucl_object_t const *interface = ucl_object_lookup(root, "interface"); + UCL_CHECK(interface, STRING); + E->steal.Interface = strdup(ucl_object_tostring(interface)); + break; + } + case interface_type_COUNT: + default: __builtin_unreachable(); + } + return NULL; +error: + return Error; +} + +char * +Parse_interfaces_configuration(interfaces_configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + E->Count = root->len; + E->_ = calloc(sizeof(interface_configuration), E->Count); + { + ucl_object_iter_t it = NULL; + for(E->Count = 0; E->Count < root->len; ++E->Count) + { + ucl_object_t const *v = ucl_iterate_object(root, &it, true); + if(v == NULL) break; + char const *k = ucl_object_key(v); + if(k == NULL) continue; + interface_configuration *I = E->_ + E->Count; + char *NewPosition; + asprintf(&NewPosition, "%s['%s']", Position, k); + Error = Parse_interface_configuration(I, v, NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + I->Name = strdup(k); + } + } + return NULL; +error: + Free_interfaces_configuration(E); + return Error; +} + +char * +Parse_mapping(mapping *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + ucl_object_t const *source = ucl_object_lookup(root, "source"); + UCL_CHECK(source, STRING); + ucl_object_t const *target = ucl_object_lookup(root, "target"); + UCL_CHECK(target, STRING); + E->Source = strdup(ucl_object_tostring(source)); + E->Target = strdup(ucl_object_tostring(target)); + return NULL; +error: + return Error; +} + +char * +Parse_filesystem_configuration(filesystem_configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + ucl_object_t const *layers = ucl_object_lookup(root, "layers"); + UCL_CHECK_OPTIONAL(layers, ARRAY); + ucl_object_t const *temporary = ucl_object_lookup(root, "temporary"); + UCL_CHECK_OPTIONAL(temporary, STRING); + ucl_object_t const *volumes = ucl_object_lookup(root, "volumes"); + UCL_CHECK_OPTIONAL(volumes, ARRAY); + ucl_object_t const *devfs = ucl_object_lookup(root, "devfs"); + UCL_CHECK_OPTIONAL(devfs, INT); + if(layers != NULL) + { + E->Layer = calloc(sizeof(char *), layers->len); + ucl_object_iter_t it = NULL; + for(E->LayerCount = 0; E->LayerCount < layers->len; ++E->LayerCount) + { + ucl_object_t const *v = ucl_iterate_object(layers, &it, true); + if(v == NULL) break; + char const *k = ucl_object_key(v); + if(k != NULL) continue; + if(v->type != UCL_STRING) + { + asprintf(&Error, "%s.layers[%zu] is not " UCL_CHECK_HELPER(STRING), Position, E->LayerCount); + goto error; + } + E->Layer[E->LayerCount] = strdup(ucl_object_tostring(v)); + } + } + if(temporary != NULL) E->Temporary = strdup(ucl_object_tostring(temporary)); + if(volumes != NULL) + { + E->Volume = calloc(sizeof(mapping), volumes->len); + ucl_object_iter_t it = NULL; + for(E->VolumeCount = 0; E->VolumeCount < volumes->len; ++E->VolumeCount) + { + ucl_object_t const *v = ucl_iterate_object(volumes, &it, true); + if(v == NULL) break; + char const *k = ucl_object_key(v); + if(k != NULL) continue; + mapping *I = E->Volume + E->VolumeCount; + char *NewPosition; + asprintf(&NewPosition, "%s.volumes[%zu]", Position, E->VolumeCount); + Error = Parse_mapping(I, v, NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + } + E->DevfsRuleset = ucl_object_toint(devfs); + return NULL; +error: + Free_filesystem_configuration(E); + return Error; +} + +char * +Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + { + char *NewPosition; + asprintf(&NewPosition, "%s.interfaces", Position); + Error = + Parse_interfaces_configuration(&E->Interfaces, ucl_object_lookup(root, "interfaces"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + { + char *NewPosition; + asprintf(&NewPosition, "%s.filesystem", Position); + Error = + Parse_filesystem_configuration(&E->Filesystem, ucl_object_lookup(root, "filesystem"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + { + char *NewPosition; + asprintf(&NewPosition, "%s.init", Position); + Error = Parse_string_array(&E->Init, ucl_object_lookup(root, "init"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + { + char *NewPosition; + asprintf(&NewPosition, "%s.shutdown", Position); + Error = Parse_string_array(&E->Shutdown, ucl_object_lookup(root, "shutdown"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + return NULL; +error: + return Error; +} + +char * +Parse_interface(interface *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + { + char *NewPosition; + asprintf(&NewPosition, "%s.type", Position); + Error = Parse_interface_type(&E->Type, ucl_object_lookup(root, "type"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + switch(E->Type) + { + case interface_type_eiface: + { + ucl_object_t const *id = ucl_object_lookup(root, "id"); + UCL_CHECK(id, INT); + s64 ID = ucl_object_toint(id); + if(ID < 0 || ID > INT32_MAX) + { + asprintf(&Error, "%s id invalid (%ld)", Position, ID); + goto error; + } + E->ID = (u32)ID; + break; + } + case interface_type_steal: break; + case interface_type_COUNT: + default: __builtin_unreachable(); + } + return NULL; +error: + return Error; +} + +char * +Parse_interfaces(interfaces *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(ARRAY); + E->Count = root->len; + E->_ = calloc(sizeof(interface_configuration), E->Count); + { + ucl_object_iter_t it = NULL; + for(E->Count = 0; E->Count < root->len; ++E->Count) + { + ucl_object_t const *v = ucl_iterate_object(root, &it, true); + if(v == NULL) break; + char const *k = ucl_object_key(v); + if(k != NULL) continue; + interface *I = E->_ + E->Count; + char *NewPosition; + asprintf(&NewPosition, "%s['%zu']", Position, E->Count); + Error = Parse_interface(I, v, NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + } + return NULL; +error: + Free_interfaces(E); + return Error; +} + +char * +Parse_data(data *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + ucl_object_t const *jid = ucl_object_lookup(root, "jid"); + UCL_CHECK(jid, INT); + int64_t JID = ucl_object_toint(jid); + if(JID < 0 || JID > INT32_MAX) + { + asprintf(&Error, "%s jid invalid (%ld)", Position, JID); + goto error; + } + E->JID = (jid_t)JID; + { + char *NewPosition; + asprintf(&NewPosition, "%s.interfaces", Position); + Error = Parse_interfaces(&E->Interfaces, ucl_object_lookup(root, "interfaces"), NewPosition); + free(NewPosition); + if(Error != NULL) goto error; + } + return NULL; +error: + return Error; +} + +// SAVE + +void +Save_interface(jprint_state *S, interface const *E) +{ + JPrintObjectBegin(S); + JPrintMember(S, "type"); + JPrint_string(S, interface_type_Names[E->Type]); + switch(E->Type) + { + case interface_type_eiface: + { + JPrintMember(S, "id"); + JPrint_ssize_t(S, E->ID); + break; + } + case interface_type_steal: break; + case interface_type_COUNT: + default: __builtin_unreachable(); + } + JPrintObjectEnd(S); +} + +void +Save_interfaces(jprint_state *S, interfaces const *E) +{ + JPrintArrayBegin(S); + for(size_t i = 0; i < E->Count; ++i) Save_interface(S, E->_ + i); + JPrintArrayEnd(S); +} + +void +Save_data(jprint_state *S, data const *E) +{ + JPrintObjectBegin(S); + JPrintMember(S, "jid"); + JPrint_ssize_t(S, E->JID); + JPrintMember(S, "interfaces"); + Save_interfaces(S, &E->Interfaces); + JPrintObjectEnd(S); +} diff --git a/src/module.jail/state.h b/src/module.jail/state.h new file mode 100644 index 0000000..d22d374 --- /dev/null +++ b/src/module.jail/state.h @@ -0,0 +1,93 @@ +#pragma once + +#include "../util.h" + +NAMED_ENUM(interface_type, eiface, steal); + +struct +{ + char *Name; + interface_type Type; + union + { + struct + { + } eiface; + struct + { + char *Interface; + } steal; + }; +} typedef interface_configuration; + +struct +{ + size_t Count; + interface_configuration *_; +} typedef interfaces_configuration; + +struct +{ + char *Source; + char *Target; +} typedef mapping; + +struct +{ + size_t LayerCount; + char **Layer; + char *Temporary; + size_t VolumeCount; + mapping *Volume; + s64 DevfsRuleset; +} typedef filesystem_configuration; + +struct +{ + interfaces_configuration Interfaces; + filesystem_configuration Filesystem; + string_array Init; + string_array Shutdown; +} typedef configuration; + +struct +{ + u32 ID; + interface_type Type; +} typedef interface; + +struct +{ + size_t Count; + interface *_; +} typedef interfaces; + +struct +{ + interfaces Interfaces; + jid_t JID; +} typedef data; + +void Free_interface_configuration(interface_configuration *); +void Free_interfaces_configuration(interfaces_configuration *); +void Free_mapping(mapping *); +void Free_filesystem_configuration(filesystem_configuration *); +void Free_configuration(configuration *); +void Free_interface(interface *); +void Free_interfaces(interfaces *); +void Free_data(data *); + +char *Parse_interface_type(interface_type *, ucl_object_t const *, char const *Position); +char *Parse_interface_configuration(interface_configuration *, ucl_object_t const *, char const *Position); +char *Parse_interfaces_configuration(interfaces_configuration *, ucl_object_t const *, char const *Position); +char *Parse_mapping(mapping *, ucl_object_t const *, char const *Position); +char *Parse_filesystem_configuration(filesystem_configuration *, ucl_object_t const *, char const *Position); +char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); +char *Parse_interface(interface *, ucl_object_t const *, char const *Position); +char *Parse_interfaces(interfaces *, ucl_object_t const *, char const *Position); +char *Parse_data(data *, ucl_object_t const *, char const *Position); + +void Save_interface_type(jprint_state *, interface_type const *); +void Save_interface(jprint_state *, interface const *); +void Save_interfaces(jprint_state *, interfaces const *); +void Save_data(jprint_state *, data const *); diff --git a/src/module.skel/main.c b/src/module.skel/main.c new file mode 100644 index 0000000..5460454 --- /dev/null +++ b/src/module.skel/main.c @@ -0,0 +1,116 @@ +#include "../module/module.h" +#include "state.h" + +char const *Usage = "usage: %1$s start [name] [configuration]\n" + " %1$s stop [name] [configuration] [data]\n" + " %1$s get-endpoint [name] [configuration] [data] [interface]\n" + "unsupported commands: cmd mod\n"; + +static configuration Configuration; +static data Data; +static bool ConfigurationLoaded; +static bool DataLoaded; + +// HELPERS + +static void +Free_all(void) +{ + if(ConfigurationLoaded == true) Free_configuration(&Configuration); + if(DataLoaded == true) Free_data(&Data); +} + +static void +Load_configuration(ucl_object_t *root) +{ + char *Error = Parse_configuration(&Configuration, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + ConfigurationLoaded = true; +} + +static void +Load_data(ucl_object_t *root) +{ + char *Error = Parse_data(&Data, root, ""); + if(Error != NULL) + { + fprintf(stderr, "%s: data error: %s\n", Arg0, Error); + free(Error); + Free_all(); + exit(-1); + } + DataLoaded = true; + // TODO: check that data matches configuration +} + +// COMMANDS + +int +start(char const *Name, ucl_object_t *configuration) +{ + UNUSED(Name); + Load_configuration(configuration); + // Implement + if((true)) goto error; + jprint_state S; + bzero(&S, sizeof(S)); + S.F = stdout; + Save_data(&S, &Data); + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) +{ + UNUSED(Name); + Load_configuration(configuration); + Load_data(data); + // Implement + if((true)) goto error; + Free_all(); + return 0; +error: + Free_all(); + return -1; +} + +int +get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) +{ + UNUSED(Name, Interface); + Load_configuration(configuration); + Load_data(data); + // Implement + Free_all(); + return -1; +} + +int +cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + UNUSED(Name, ArgCount, Arg); + Load_configuration(configuration); + Load_data(data); + // Implement + usage(); +} + +int +mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) +{ + UNUSED(Name, ArgCount, Arg); + Load_configuration(configuration); + Load_data(data); + // Implement + usage(); +} diff --git a/src/module.skel/state.c b/src/module.skel/state.c new file mode 100644 index 0000000..3a1d297 --- /dev/null +++ b/src/module.skel/state.c @@ -0,0 +1,49 @@ +#include "state.h" + +// FREE + +void +Free_configuration(configuration *E) +{ + UNUSED(E); +} + +void +Free_data(data *E) +{ + UNUSED(E); +} + +// PARSE + +char * +Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + UNUSED(E); + return NULL; +error: + return Error; +} + +char * +Parse_data(data *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(OBJECT); + UNUSED(E); + return NULL; +error: + return Error; +} + +// SAVE + +void +Save_data(jprint_state *S, data const *E) +{ + JPrintObjectBegin(S); + UNUSED(E); + JPrintObjectEnd(S); +} diff --git a/src/module.skel/state.h b/src/module.skel/state.h new file mode 100644 index 0000000..9f52a2b --- /dev/null +++ b/src/module.skel/state.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../jprint.h" + +struct +{ +} typedef configuration; + +struct +{ +} typedef data; + +void Free_configuration(configuration *); +void Free_data(data *); + +char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); +char *Parse_data(data *, ucl_object_t const *, char const *Position); + +void Save_data(jprint_state *, data const *); diff --git a/src/sf.bridge/main.c b/src/sf.bridge/main.c deleted file mode 100644 index fbfb500..0000000 --- a/src/sf.bridge/main.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "../module/module.h" -#include "../util.h" -#include "state.h" - -#include - -char const *Usage = "usage: %1$s start [name] [configuration]\n" - " %1$s stop [name] [configuration] [data]\n" - " %1$s get-endpoint [name] [configuration] [data] [interface]\n" - "unsupported commands: cmd mod\n"; - -static configuration Configuration; -static data Data; -static bool ConfigurationLoaded; -static bool DataLoaded; - -// HELPERS - -static void -Free_all(void) -{ - if(ConfigurationLoaded == true) Free_configuration(&Configuration); - if(DataLoaded == true) Free_data(&Data); -} - -static void -Load_configuration(ucl_object_t *root) -{ - char *Error = Parse_configuration(&Configuration, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - ConfigurationLoaded = true; -} - -static void -Load_data(ucl_object_t *root) -{ - char *Error = Parse_data(&Data, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: data error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - DataLoaded = true; - // TODO: check that data matches configuration -} - -// COMMANDS - -int -start(char const *Name, ucl_object_t *configuration) -{ - UNUSED(Name); - Load_configuration(configuration); - // Implement - Data.ID = 0; - fd Control = -1; - if(NgMkSockNode(NULL, &Control, NULL) == -1) - { - fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); - goto error; - } - if(Create_bridge(&Data.ID, Control) == -1) goto error; - close(Control); - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - Save_data(&S, &Data); - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) -{ - UNUSED(Name); - Load_configuration(configuration); - Load_data(data); - fd Control = -1; - if(NgMkSockNode(NULL, &Control, NULL) == -1) - { - fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); - goto error; - } - if(DestroyNetgraphNode(Data.ID, Control) == -1) goto error; - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) -{ - UNUSED(Name); - Load_configuration(configuration); - Load_data(data); - size_t linkNum; - if(strncmp(Interface, "link", __builtin_strlen("link")) == 0) - { - char const *N = Interface + __builtin_strlen("link"); - linkNum = strtoul(N, NULL, 10); - if(linkNum == 0 && errno == EINVAL) - { - fprintf(stderr, "invalid interface %s\n", Interface); - goto error; - } - } else if(strncmp(Interface, "uplink", __builtin_strlen("uplink")) == 0) - { - char const *N = Interface + __builtin_strlen("uplink"); - linkNum = strtoul(N, NULL, 10); - if(linkNum == 0) - { - fprintf(stderr, "unknown interface %s\n", Interface); - goto error; - } - } else - { - fprintf(stderr, "unknown interface %s\n", Interface); - } - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - JPrintObjectBegin(&S); - JPrintMember(&S, "address"); - char Address[NG_PATHSIZ]; - snprintf(Address, sizeof(Address), "[%x]:", Data.ID); - JPrint_string(&S, Address); - JPrintMember(&S, "hook"); - JPrint_string(&S, Interface); - JPrintObjectEnd(&S); - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - UNUSED(Name, ArgCount, Arg); - Load_configuration(configuration); - Load_data(data); - usage(); -} - -int -mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - UNUSED(Name, ArgCount, Arg); - Load_configuration(configuration); - Load_data(data); - usage(); -} diff --git a/src/sf.bridge/state.c b/src/sf.bridge/state.c deleted file mode 100644 index c7780f3..0000000 --- a/src/sf.bridge/state.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "state.h" - -// FREE - -void -Free_configuration(configuration *E) -{ - UNUSED(E); -} - -void -Free_data(data *E) -{ - UNUSED(E); -} - -// PARSE - -char * -Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - UNUSED(E); - return NULL; -error: - return Error; -} - -char * -Parse_data(data *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - ucl_object_t const *id = ucl_object_lookup(root, "id"); - UCL_CHECK(id, INT); - s64 ID = ucl_object_toint(id); - if(ID < 0 || ID > UINT32_MAX) - { - asprintf(&Error, "%s id invalid (%ld)", Position, ID); - goto error; - } - E->ID = (u32)ID; - return NULL; -error: - return Error; -} - -// SAVE - -void -Save_data(jprint_state *S, data const *E) -{ - JPrintObjectBegin(S); - JPrintMember(S, "id"); - JPrint_ssize_t(S, E->ID); - JPrintObjectEnd(S); -} diff --git a/src/sf.bridge/state.h b/src/sf.bridge/state.h deleted file mode 100644 index 7ef6243..0000000 --- a/src/sf.bridge/state.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "../jprint.h" - -struct -{ -} typedef configuration; - -struct -{ - u32 ID; -} typedef data; - -void Free_configuration(configuration *); -void Free_data(data *); - -char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); -char *Parse_data(data *, ucl_object_t const *, char const *Position); - -void Save_data(jprint_state *, data const *); diff --git a/src/sf.eiface/main.c b/src/sf.eiface/main.c deleted file mode 100644 index 4fafa81..0000000 --- a/src/sf.eiface/main.c +++ /dev/null @@ -1,155 +0,0 @@ -#include "../module/module.h" -#include "../util.h" -#include "state.h" - -#include - -char const *Usage = "usage: %1$s start [name] [configuration]\n" - " %1$s stop [name] [configuration] [data]\n" - " %1$s get-endpoint [name] [configuration] [data] [interface]\n" - "unsupported commands: cmd mod\n"; - -static configuration Configuration; -static data Data; -static bool ConfigurationLoaded; -static bool DataLoaded; - -// HELPERS - -static void -Free_all(void) -{ - if(ConfigurationLoaded == true) Free_configuration(&Configuration); - if(DataLoaded == true) Free_data(&Data); -} - -static void -Load_configuration(ucl_object_t *root) -{ - char *Error = Parse_configuration(&Configuration, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - ConfigurationLoaded = true; -} - -static void -Load_data(ucl_object_t *root) -{ - char *Error = Parse_data(&Data, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: data error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - DataLoaded = true; - // TODO: check that data matches configuration -} - -// COMMANDS - -int -start(char const *Name, ucl_object_t *configuration) -{ - UNUSED(Name); - Load_configuration(configuration); - Data.ID = 0; - fd Control = -1; - if(NgMkSockNode(NULL, &Control, NULL) == -1) - { - fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); - goto error; - } - if(Create_eiface(&Data.ID, Control) == -1) goto error; - char *InterfaceName = getifname(Data.ID, Control); - if(InterfaceName == NULL) goto error; - int Result = command("ifconfig", "ifconfig", InterfaceName, "name", Name, NULL); - free(InterfaceName); - if(Result == -1) goto error; - close(Control); - if(Configuration.inet != NULL) command("ifconfig", "ifconfig", Name, "inet", Configuration.inet, NULL); - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - Save_data(&S, &Data); - Free_all(); - return 0; -error: - if(Data.ID != 0) DestroyNetgraphNode(Data.ID, Control); - if(Control != -1) close(Control); - Free_all(); - return -1; -} - -int -stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) -{ - UNUSED(Name); - Load_configuration(configuration); - Load_data(data); - fd Control = -1; - if(NgMkSockNode(NULL, &Control, NULL) == -1) - { - fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); - goto error; - } - if(DestroyNetgraphNode(Data.ID, Control) == -1) goto error; - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) -{ - UNUSED(Name); - Load_configuration(configuration); - Load_data(data); - if(*Interface != '\0') - { - fprintf(stderr, "%s provides only empty interface name\n", Arg0); - goto error; - } - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - JPrintObjectBegin(&S); - JPrintMember(&S, "address"); - char Address[NG_PATHSIZ]; - snprintf(Address, sizeof(Address), "[%x]:", Data.ID); - JPrint_string(&S, Address); - JPrintMember(&S, "hook"); - JPrint_string(&S, "ether"); - JPrintObjectEnd(&S); - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - UNUSED(Name, ArgCount, Arg); - Load_configuration(configuration); - Load_data(data); - usage(); -} - -int -mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - UNUSED(Name, ArgCount, Arg); - Load_configuration(configuration); - Load_data(data); - usage(); -} diff --git a/src/sf.eiface/state.c b/src/sf.eiface/state.c deleted file mode 100644 index 7d85056..0000000 --- a/src/sf.eiface/state.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "state.h" - -// FREE - -void -Free_configuration(configuration *E) -{ - if(E->inet != NULL) free(E->inet); -} - -void -Free_data(data *E) -{ - UNUSED(E); -} - -// PARSE - -char * -Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - ucl_object_t const *inet = ucl_object_lookup(root, "inet"); - if(inet != NULL) - { - UCL_CHECK(inet, STRING); - E->inet = strdup(ucl_object_tostring(inet)); - } - return NULL; -error: - return Error; -} - -char * -Parse_data(data *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - ucl_object_t const *id = ucl_object_lookup(root, "id"); - UCL_CHECK(id, INT); - s64 ID = ucl_object_toint(id); - if(ID < 0 || ID > UINT32_MAX) - { - asprintf(&Error, "%s id invalid (%ld)", Position, ID); - goto error; - } - E->ID = (u32)ID; - return NULL; -error: - return Error; -} - -// SAVE - -void -Save_data(jprint_state *S, data const *E) -{ - JPrintObjectBegin(S); - JPrintMember(S, "id"); - JPrint_ssize_t(S, E->ID); - JPrintObjectEnd(S); -} diff --git a/src/sf.eiface/state.h b/src/sf.eiface/state.h deleted file mode 100644 index 0431fdf..0000000 --- a/src/sf.eiface/state.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "../jprint.h" - -struct -{ - char *inet; -} typedef configuration; - -struct -{ - u32 ID; -} typedef data; - -void Free_configuration(configuration *); -void Free_data(data *); - -char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); -char *Parse_data(data *, ucl_object_t const *, char const *Position); - -void Save_data(jprint_state *, data const *); diff --git a/src/sf.jail/main.c b/src/sf.jail/main.c deleted file mode 100644 index d71511b..0000000 --- a/src/sf.jail/main.c +++ /dev/null @@ -1,525 +0,0 @@ -#include "../module/module.h" -#include "../util.h" -#include "state.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -char const *Usage = "usage: %1$s start [name] [configuration]\n" - " %1$s stop [name] [configuration] [data]\n" - " %1$s get-endpoint [name] [configuration] [data] [interface]\n" - " %1$s cmd [name] [configuration] [data] sh\n" - " %1$s cmd [name] [configuration] [data] wireshark [interface]\n" - "unsupported commands: mod\n"; - -static configuration Configuration; -static data Data; -static bool ConfigurationLoaded; -static bool DataLoaded; - -// HELPERS - -static void -Free_all(void) -{ - if(ConfigurationLoaded == true) Free_configuration(&Configuration); - if(DataLoaded == true) Free_data(&Data); -} - -static void -Load_configuration(ucl_object_t *root) -{ - char *Error = Parse_configuration(&Configuration, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - ConfigurationLoaded = true; -} - -static void -Load_data(ucl_object_t *root) -{ - char *Error = Parse_data(&Data, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: data error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - DataLoaded = true; - // TODO: check that data matches configuration -} - -static int -start_interface(interface *E, interface_configuration *C, fd Control, char const *JailName) -{ - E->Type = C->Type; - switch(C->Type) - { - case interface_type_eiface: - { - if(Create_eiface(&E->ID, Control) == -1) return -1; - char *InterfaceName = getifname(E->ID, Control); - if(InterfaceName == NULL) - { - DestroyNetgraphNode(E->ID, Control); - return -1; - } - // NOTE: This first renames the interface and only then vnets it. - int Result = - command("ifconfig", "ifconfig", InterfaceName, "name", C->Name, "vnet", JailName, NULL); - free(InterfaceName); - return Result; - } - case interface_type_steal: - { - return command("ifconfig", "ifconfig", C->steal.Interface, "name", C->Name, "vnet", JailName, NULL); - } - case interface_type_COUNT: - default: __builtin_unreachable(); - } -} - -static int -stop_interface(interface *E, interface_configuration *C, fd Control, char const *JailName) -{ - switch(C->Type) - { - case interface_type_eiface: return DestroyNetgraphNode(E->ID, Control); - case interface_type_steal: - { - return command("ifconfig", - "ifconfig", - C->Name, - "-vnet", - JailName, - "name", - C->steal.Interface, - NULL); - } - case interface_type_COUNT: - default: __builtin_unreachable(); - } -} - -static int -stop_interfaces(interfaces *E, interfaces_configuration *C, fd Control, char const *JailName) -{ - for(size_t i = 0; i < E->Count; ++i) - { - if(stop_interface(E->_ + i, C->_ + i, Control, JailName) == -1) - { - fprintf(stderr, "failed to stop interface '%s'\n", C->_[i].Name); - return -1; - } - } - return 0; -} - -static int -start_interfaces(interfaces *E, interfaces_configuration *C, fd Control, char const *Name) -{ - Data.Interfaces._ = calloc(sizeof(interface), Configuration.Interfaces.Count); - for(E->Count = 0; E->Count < C->Count; ++E->Count) - { - interface *E_ = E->_ + E->Count; - interface_configuration *C_ = C->_ + E->Count; - if(start_interface(E_, C_, Control, Name) == -1) goto error; - } - return 0; -error: - stop_interfaces(E, C, Control, Name); - Free_interfaces(E); - return -1; -} - -static int -start_jail(jid_t *JID, char const *Name, char const *Path) -{ - *JID = jail_setv(JAIL_CREATE, "name", Name, "vnet", "new", "persist", "true", "path", Path, NULL); - if(*JID == -1) - { - fprintf(stderr, "jail creation failed: %s\n", strerror(errno)); - return -1; - } - return 0; -} - -static int -stop_jail(jid_t JID) -{ - if(jail_remove(JID) == -1) - { - fprintf(stderr, "jail removal failed: %s\n", strerror(errno)); - Free_all(); - return -1; - } - return 0; -} - -NAMED_ENUM(mapping_type, unionfs_below, unionfs, devfs, nullfs); - -static int -mapping_mount(mapping *M, char const *TargetPrefix, mapping_type Type) -{ - char Target[MAXPATHLEN]; - snprintf(Target, sizeof(Target), "%s/%s", TargetPrefix, M->Target); - if(mkdir(Target, 755) == -1) - { - if(errno != EEXIST) - { - fprintf(stderr, "mkdir %s: %s\n", Target, strerror(errno)); - return -1; - } - } - switch(Type) - { - case mapping_type_unionfs_below: return mount_unionfs(Target, M->Source, true); - case mapping_type_unionfs: return mount_unionfs(Target, M->Source, false); - case mapping_type_devfs: return mount_devfs(Target); - case mapping_type_nullfs: return mount_nullfs(Target, M->Source); - case mapping_type_COUNT: - default: __builtin_unreachable(); - } -} - -static int -mapping_umount(char const *TargetPrefix, char const *Target_) -{ - char Target[MAXPATHLEN]; - snprintf(Target, sizeof(Target), "%s/%s", TargetPrefix, Target_); - // NOTE: unmount fails because some processes somehow still hold a reference to the jail FS - while(unmount(Target, MNT_FORCE) == -1) - { - fprintf(stderr, "unmount %s failed: %s\n", Target, strerror(errno)); - if(errno != EBUSY) return -1; - sleep(1); - } - return 0; -} - -static int -rm(char const *Path) -{ - pid_t PID = fork(); - if(PID == -1) return -1; - if(PID == 0) - { - close(STDIN_FILENO); - dup2(STDERR_FILENO, STDOUT_FILENO); - close_all(); - fprintf(stderr, "rm -r %s\n", Path); - execlp("rm", "rm", "-r", Path, NULL); - exit(-1); - } - int Exit; - waitpid(PID, &Exit, 0); - if(!WIFEXITED(Exit) || (WEXITSTATUS(Exit) != 0)) return -1; - return 0; -} - -static int -ApplyDevfsRuleset(char const *Prefix, s64 Ruleset) -{ - if(Ruleset == 0) return 0; - devfs_rsnum RS = (devfs_rsnum)Ruleset; - if(RS != Ruleset) - { - fprintf(stderr, "Ruleset invalid: %ld\n", Ruleset); - return -1; - } - char Path[MAXPATHLEN]; - snprintf(Path, sizeof(Path), "%s/dev", Prefix); - fd DevFD = open(Path, O_RDONLY); - if(DevFD == -1) - { - fprintf(stderr, "open '%s' failed: %s\n", Path, strerror(errno)); - return -1; - } - if(ioctl(DevFD, DEVFSIO_SAPPLY, &RS) == -1) - { - fprintf(stderr, "ruleset %hu apply failed: %s\n", RS, strerror(errno)); - close(DevFD); - return -1; - } - close(DevFD); - return 0; -} - -static int -start_fs(filesystem_configuration *E, char const *Path) -{ - bool CreatedPath = false; - size_t LayerAt = 0; - size_t VolumeAt = 0; - bool Mounted_devfs = false; - bool Mounted_temporary = false; - if(mkdir(Path, 0755) == -1) - { - fprintf(stderr, "mkdir %s: %s\n", Path, strerror(errno)); - goto error; - } - CreatedPath = true; - for(LayerAt = 0; LayerAt < E->LayerCount; ++LayerAt) - { - if(mapping_mount(&(mapping){ .Source = E->Layer[LayerAt], .Target = "/" }, - Path, - mapping_type_unionfs_below) == -1) - goto error; - } - if(E->Temporary != NULL) - { - if(mkdir(E->Temporary, 755) == -1) - { - if(errno != EEXIST) - { - fprintf(stderr, "mkdir %s: %s\n", E->Temporary, strerror(errno)); - goto error; - } - } - if(mapping_mount(&(mapping){ .Source = E->Temporary, .Target = "/" }, Path, mapping_type_unionfs) == -1) - goto error; - } - Mounted_temporary = true; - if(mapping_mount(&(mapping){ .Target = "/dev" }, Path, mapping_type_devfs) == -1) goto error; - if(ApplyDevfsRuleset(Path, E->DevfsRuleset) == -1) goto error; - for(VolumeAt = 0; VolumeAt < E->VolumeCount; ++VolumeAt) - { - if(mapping_mount(E->Volume + VolumeAt, Path, mapping_type_nullfs) == -1) goto error; - } - return 0; -error: - for(size_t i = 0; i < VolumeAt; ++i) mapping_umount(Path, E->Volume[E->VolumeCount - 1 - i].Target); - if(Mounted_devfs) mapping_umount(Path, "/dev"); - if(Mounted_temporary) mapping_umount(Path, "/"); - for(size_t i = 0; i < LayerAt; ++i) mapping_umount(Path, "/"); - if(CreatedPath) rm(Path); - return -1; -} - -static int -stop_fs(filesystem_configuration *E, char const *Path) -{ - for(size_t i = 0; i < E->VolumeCount; ++i) mapping_umount(Path, E->Volume[E->VolumeCount - 1 - i].Target); - mapping_umount(Path, "/dev"); - if(E->Temporary != NULL) mapping_umount(Path, "/"); - for(size_t i = 0; i < E->LayerCount; ++i) mapping_umount(Path, "/"); - if(rm(Path) == -1) - { - fprintf(stderr, "rm -r %s: %s\n", Path, strerror(errno)); - goto error; - } - return 0; -error: - return -1; -} - -static bool -ValidJailName(char const *Name) -{ - for(char const *At = Name; *At; ++At) - { - if(isnumber(*At) == false) return true; - } - return true; -} - -// COMMANDS - -static char const *FilesystemPrefix = "/tmp/sf"; - -int -start(char const *Name, ucl_object_t *configuration) -{ - if(ValidJailName(Name) == false) return -1; - Load_configuration(configuration); - fd Control = -1; - Data.JID = -1; - char Path[PATH_MAX]; - snprintf(Path, sizeof(Path), "%s/%s", FilesystemPrefix, Name); - if(mkdir(FilesystemPrefix, 0755) == -1) - { - if(errno != EEXIST) - { - fprintf(stderr, "mkdir %s: %s\n", FilesystemPrefix, strerror(errno)); - goto error; - } - } - if(start_fs(&Configuration.Filesystem, Path) == -1) goto error; - if(start_jail(&Data.JID, Name, Path) == -1) goto error; - if(NgMkSockNode(NULL, &Control, NULL) == -1) - { - fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); - goto error; - } - if(start_interfaces(&Data.Interfaces, &Configuration.Interfaces, Control, Name) == -1) - { - fprintf(stderr, "failed to start interfaces\n"); - goto error; - } - DataLoaded = true; - close(Control); - exec_string_array(&Configuration.Init, false, Data.JID); - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - Save_data(&S, &Data); - Free_all(); - return 0; -error: - if(Control != -1) close(Control); - if(Data.JID != -1) stop_jail(Data.JID); - Free_all(); - return -1; -} - -int -stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) -{ - if(ValidJailName(Name) == false) return -1; - Load_configuration(configuration); - Load_data(data); - exec_string_array(&Configuration.Shutdown, true, Data.JID); - fd Control = -1; - if(NgMkSockNode(NULL, &Control, NULL) == -1) - { - fprintf(stderr, "Failed to create netgraph socket: %s\n", strerror(errno)); - goto error; - } - if(stop_interfaces(&Data.Interfaces, &Configuration.Interfaces, Control, Name) == -1) goto error; - if(stop_jail(Data.JID) == -1) goto error; - char Path[PATH_MAX]; - snprintf(Path, sizeof(Path), "%s/%s", FilesystemPrefix, Name); - if(stop_fs(&Configuration.Filesystem, Path) == -1) goto error; - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) -{ - if(ValidJailName(Name) == false) return -1; - Load_configuration(configuration); - Load_data(data); - for(size_t i = 0; i < Configuration.Interfaces.Count; ++i) - { - interface_configuration *C = Configuration.Interfaces._ + i; - if(strcmp(C->Name, Interface) == 0) - { - interface *E = Data.Interfaces._ + i; - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - JPrintObjectBegin(&S); - JPrintMember(&S, "address"); - char Address[NG_PATHSIZ]; - snprintf(Address, sizeof(Address), "[%x]:", E->ID); - JPrint_string(&S, Address); - JPrintMember(&S, "hook"); - JPrint_string(&S, "ether"); - JPrintObjectEnd(&S); - Free_all(); - return 0; - } - } - Free_all(); - return -1; -} - -int -cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - if(ValidJailName(Name) == false) return -1; - Load_configuration(configuration); - Load_data(data); - if(ArgCount < 1) usage(); - char const *Command = Arg[0]; - --ArgCount; - ++Arg; - if(strcmp(Command, "sh") == 0) - { - size_t ExtraArguments = 2; - char **Args = alloca(sizeof(char *) * (ArgCount + ExtraArguments + 1)); - Args[0] = "jexec"; - Args[1] = strdup(Name); - memcpy(Args + ExtraArguments, Arg, sizeof(char *) * ArgCount); - Args[ArgCount + ExtraArguments] = NULL; - execvp("jexec", Args); - fprintf(stderr, "execvp failed: %s\n", strerror(errno)); - exit(-1); - } else if(strcmp(Command, "wireshark") == 0) - { - if(ArgCount < 1) usage(); - char const *Interface = Arg[0]; - --ArgCount; - ++Arg; - fd Pipe[2]; - if(pipe(Pipe) == -1) - { - fprintf(stderr, "pipe: %s\n", strerror(errno)); - return -1; - } - pid_t PID = fork(); - if(PID == -1) - { - fprintf(stderr, "fork: %s\n", strerror(errno)); - return -1; - } else if(PID == 0) - { - dup2(Pipe[1], STDOUT_FILENO); - close(Pipe[0]); - close(Pipe[1]); - close_all(); - if(jail_attach(Data.JID)) - { - fprintf(stderr, "jail_attach(%d): %s\n", Data.JID, strerror(errno)); - exit(-1); - } - execlp("tcpdump", "tcpdump", "-s", "-0", "-U", "-w", "-", "-i", Interface, NULL); - fprintf(stderr, "execlp failed: %s\n", strerror(errno)); - exit(-1); - } else - { - dup2(Pipe[0], STDIN_FILENO); - close(Pipe[0]); - close(Pipe[1]); - close_all(); - char Title[64]; - snprintf(Title, sizeof(Title), "gui.window_title:%s:%s", Name, Interface); - execlp("wireshark", "wireshark", "-o", Title, "-k", "-i", "-", NULL); - fprintf(stderr, "execlp failed: %s\n", strerror(errno)); - exit(-1); - } - } else - { - fprintf(stderr, "unknown command %s\n", Command); - usage(); - } -} - -int -mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - if(ValidJailName(Name) == false) return -1; - Load_configuration(configuration); - Load_data(data); - UNUSED(ArgCount, Arg); - usage(); -} diff --git a/src/sf.jail/state.c b/src/sf.jail/state.c deleted file mode 100644 index 893961e..0000000 --- a/src/sf.jail/state.c +++ /dev/null @@ -1,396 +0,0 @@ -#include "state.h" - -// FREE - -void -Free_interface_configuration(interface_configuration *E) -{ - free(E->Name); - switch(E->Type) - { - case interface_type_eiface: break; - case interface_type_steal: free(E->steal.Interface); break; - case interface_type_COUNT: - default: __builtin_unreachable(); - } -} - -void -Free_interfaces_configuration(interfaces_configuration *E) -{ - for(size_t i = 0; i < E->Count; ++i) Free_interface_configuration(E->_ + i); - free(E->_); -} - -void -Free_mapping(mapping *E) -{ - free(E->Source); - free(E->Target); -} - -void -Free_filesystem_configuration(filesystem_configuration *E) -{ - for(size_t i = 0; i < E->LayerCount; ++i) free(E->Layer[i]); - if(E->Layer != NULL) free(E->Layer); - if(E->Temporary != NULL) free(E->Temporary); - for(size_t i = 0; i < E->VolumeCount; ++i) Free_mapping(E->Volume + i); - if(E->Volume != NULL) free(E->Volume); -} - -void -Free_configuration(configuration *E) -{ - Free_filesystem_configuration(&E->Filesystem); - Free_interfaces_configuration(&E->Interfaces); - Free_string_array(&E->Init); - Free_string_array(&E->Shutdown); -} - -void -Free_interface(interface *E) -{ - UNUSED(E); -} - -void -Free_interfaces(interfaces *E) -{ - for(size_t i = 0; i < E->Count; ++i) Free_interface(E->_ + i); - free(E->_); -} - -void -Free_data(data *E) -{ - Free_interfaces(&E->Interfaces); -} - -// PARSE - -char * -Parse_interface_type(interface_type *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(STRING); - char const *Type = ucl_object_tostring(root); - for(interface_type i = 0; i < interface_type_COUNT; ++i) - { - if(strcmp(interface_type_Names[i], Type) == 0) - { - *E = i; - return NULL; - } - } - asprintf(&Error, "%s invalid interface type '%s'", Position, Type); - goto error; -error: - return Error; -} - -char * -Parse_interface_configuration(interface_configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - { - char *NewPosition; - asprintf(&NewPosition, "%s.type", Position); - Error = Parse_interface_type(&E->Type, ucl_object_lookup(root, "type"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - switch(E->Type) - { - case interface_type_eiface: break; - case interface_type_steal: - { - ucl_object_t const *interface = ucl_object_lookup(root, "interface"); - UCL_CHECK(interface, STRING); - E->steal.Interface = strdup(ucl_object_tostring(interface)); - break; - } - case interface_type_COUNT: - default: __builtin_unreachable(); - } - return NULL; -error: - return Error; -} - -char * -Parse_interfaces_configuration(interfaces_configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - E->Count = root->len; - E->_ = calloc(sizeof(interface_configuration), E->Count); - { - ucl_object_iter_t it = NULL; - for(E->Count = 0; E->Count < root->len; ++E->Count) - { - ucl_object_t const *v = ucl_iterate_object(root, &it, true); - if(v == NULL) break; - char const *k = ucl_object_key(v); - if(k == NULL) continue; - interface_configuration *I = E->_ + E->Count; - char *NewPosition; - asprintf(&NewPosition, "%s['%s']", Position, k); - Error = Parse_interface_configuration(I, v, NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - I->Name = strdup(k); - } - } - return NULL; -error: - Free_interfaces_configuration(E); - return Error; -} - -char * -Parse_mapping(mapping *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - ucl_object_t const *source = ucl_object_lookup(root, "source"); - UCL_CHECK(source, STRING); - ucl_object_t const *target = ucl_object_lookup(root, "target"); - UCL_CHECK(target, STRING); - E->Source = strdup(ucl_object_tostring(source)); - E->Target = strdup(ucl_object_tostring(target)); - return NULL; -error: - return Error; -} - -char * -Parse_filesystem_configuration(filesystem_configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - ucl_object_t const *layers = ucl_object_lookup(root, "layers"); - UCL_CHECK_OPTIONAL(layers, ARRAY); - ucl_object_t const *temporary = ucl_object_lookup(root, "temporary"); - UCL_CHECK_OPTIONAL(temporary, STRING); - ucl_object_t const *volumes = ucl_object_lookup(root, "volumes"); - UCL_CHECK_OPTIONAL(volumes, ARRAY); - ucl_object_t const *devfs = ucl_object_lookup(root, "devfs"); - UCL_CHECK_OPTIONAL(devfs, INT); - if(layers != NULL) - { - E->Layer = calloc(sizeof(char *), layers->len); - ucl_object_iter_t it = NULL; - for(E->LayerCount = 0; E->LayerCount < layers->len; ++E->LayerCount) - { - ucl_object_t const *v = ucl_iterate_object(layers, &it, true); - if(v == NULL) break; - char const *k = ucl_object_key(v); - if(k != NULL) continue; - if(v->type != UCL_STRING) - { - asprintf(&Error, "%s.layers[%zu] is not " UCL_CHECK_HELPER(STRING), Position, E->LayerCount); - goto error; - } - E->Layer[E->LayerCount] = strdup(ucl_object_tostring(v)); - } - } - if(temporary != NULL) E->Temporary = strdup(ucl_object_tostring(temporary)); - if(volumes != NULL) - { - E->Volume = calloc(sizeof(mapping), volumes->len); - ucl_object_iter_t it = NULL; - for(E->VolumeCount = 0; E->VolumeCount < volumes->len; ++E->VolumeCount) - { - ucl_object_t const *v = ucl_iterate_object(volumes, &it, true); - if(v == NULL) break; - char const *k = ucl_object_key(v); - if(k != NULL) continue; - mapping *I = E->Volume + E->VolumeCount; - char *NewPosition; - asprintf(&NewPosition, "%s.volumes[%zu]", Position, E->VolumeCount); - Error = Parse_mapping(I, v, NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - } - E->DevfsRuleset = ucl_object_toint(devfs); - return NULL; -error: - Free_filesystem_configuration(E); - return Error; -} - -char * -Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - { - char *NewPosition; - asprintf(&NewPosition, "%s.interfaces", Position); - Error = - Parse_interfaces_configuration(&E->Interfaces, ucl_object_lookup(root, "interfaces"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - { - char *NewPosition; - asprintf(&NewPosition, "%s.filesystem", Position); - Error = - Parse_filesystem_configuration(&E->Filesystem, ucl_object_lookup(root, "filesystem"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - { - char *NewPosition; - asprintf(&NewPosition, "%s.init", Position); - Error = Parse_string_array(&E->Init, ucl_object_lookup(root, "init"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - { - char *NewPosition; - asprintf(&NewPosition, "%s.shutdown", Position); - Error = Parse_string_array(&E->Shutdown, ucl_object_lookup(root, "shutdown"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - return NULL; -error: - return Error; -} - -char * -Parse_interface(interface *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - { - char *NewPosition; - asprintf(&NewPosition, "%s.type", Position); - Error = Parse_interface_type(&E->Type, ucl_object_lookup(root, "type"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - switch(E->Type) - { - case interface_type_eiface: - { - ucl_object_t const *id = ucl_object_lookup(root, "id"); - UCL_CHECK(id, INT); - s64 ID = ucl_object_toint(id); - if(ID < 0 || ID > INT32_MAX) - { - asprintf(&Error, "%s id invalid (%ld)", Position, ID); - goto error; - } - E->ID = (u32)ID; - break; - } - case interface_type_steal: break; - case interface_type_COUNT: - default: __builtin_unreachable(); - } - return NULL; -error: - return Error; -} - -char * -Parse_interfaces(interfaces *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(ARRAY); - E->Count = root->len; - E->_ = calloc(sizeof(interface_configuration), E->Count); - { - ucl_object_iter_t it = NULL; - for(E->Count = 0; E->Count < root->len; ++E->Count) - { - ucl_object_t const *v = ucl_iterate_object(root, &it, true); - if(v == NULL) break; - char const *k = ucl_object_key(v); - if(k != NULL) continue; - interface *I = E->_ + E->Count; - char *NewPosition; - asprintf(&NewPosition, "%s['%zu']", Position, E->Count); - Error = Parse_interface(I, v, NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - } - return NULL; -error: - Free_interfaces(E); - return Error; -} - -char * -Parse_data(data *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - ucl_object_t const *jid = ucl_object_lookup(root, "jid"); - UCL_CHECK(jid, INT); - int64_t JID = ucl_object_toint(jid); - if(JID < 0 || JID > INT32_MAX) - { - asprintf(&Error, "%s jid invalid (%ld)", Position, JID); - goto error; - } - E->JID = (jid_t)JID; - { - char *NewPosition; - asprintf(&NewPosition, "%s.interfaces", Position); - Error = Parse_interfaces(&E->Interfaces, ucl_object_lookup(root, "interfaces"), NewPosition); - free(NewPosition); - if(Error != NULL) goto error; - } - return NULL; -error: - return Error; -} - -// SAVE - -void -Save_interface(jprint_state *S, interface const *E) -{ - JPrintObjectBegin(S); - JPrintMember(S, "type"); - JPrint_string(S, interface_type_Names[E->Type]); - switch(E->Type) - { - case interface_type_eiface: - { - JPrintMember(S, "id"); - JPrint_ssize_t(S, E->ID); - break; - } - case interface_type_steal: break; - case interface_type_COUNT: - default: __builtin_unreachable(); - } - JPrintObjectEnd(S); -} - -void -Save_interfaces(jprint_state *S, interfaces const *E) -{ - JPrintArrayBegin(S); - for(size_t i = 0; i < E->Count; ++i) Save_interface(S, E->_ + i); - JPrintArrayEnd(S); -} - -void -Save_data(jprint_state *S, data const *E) -{ - JPrintObjectBegin(S); - JPrintMember(S, "jid"); - JPrint_ssize_t(S, E->JID); - JPrintMember(S, "interfaces"); - Save_interfaces(S, &E->Interfaces); - JPrintObjectEnd(S); -} diff --git a/src/sf.jail/state.h b/src/sf.jail/state.h deleted file mode 100644 index d22d374..0000000 --- a/src/sf.jail/state.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "../util.h" - -NAMED_ENUM(interface_type, eiface, steal); - -struct -{ - char *Name; - interface_type Type; - union - { - struct - { - } eiface; - struct - { - char *Interface; - } steal; - }; -} typedef interface_configuration; - -struct -{ - size_t Count; - interface_configuration *_; -} typedef interfaces_configuration; - -struct -{ - char *Source; - char *Target; -} typedef mapping; - -struct -{ - size_t LayerCount; - char **Layer; - char *Temporary; - size_t VolumeCount; - mapping *Volume; - s64 DevfsRuleset; -} typedef filesystem_configuration; - -struct -{ - interfaces_configuration Interfaces; - filesystem_configuration Filesystem; - string_array Init; - string_array Shutdown; -} typedef configuration; - -struct -{ - u32 ID; - interface_type Type; -} typedef interface; - -struct -{ - size_t Count; - interface *_; -} typedef interfaces; - -struct -{ - interfaces Interfaces; - jid_t JID; -} typedef data; - -void Free_interface_configuration(interface_configuration *); -void Free_interfaces_configuration(interfaces_configuration *); -void Free_mapping(mapping *); -void Free_filesystem_configuration(filesystem_configuration *); -void Free_configuration(configuration *); -void Free_interface(interface *); -void Free_interfaces(interfaces *); -void Free_data(data *); - -char *Parse_interface_type(interface_type *, ucl_object_t const *, char const *Position); -char *Parse_interface_configuration(interface_configuration *, ucl_object_t const *, char const *Position); -char *Parse_interfaces_configuration(interfaces_configuration *, ucl_object_t const *, char const *Position); -char *Parse_mapping(mapping *, ucl_object_t const *, char const *Position); -char *Parse_filesystem_configuration(filesystem_configuration *, ucl_object_t const *, char const *Position); -char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); -char *Parse_interface(interface *, ucl_object_t const *, char const *Position); -char *Parse_interfaces(interfaces *, ucl_object_t const *, char const *Position); -char *Parse_data(data *, ucl_object_t const *, char const *Position); - -void Save_interface_type(jprint_state *, interface_type const *); -void Save_interface(jprint_state *, interface const *); -void Save_interfaces(jprint_state *, interfaces const *); -void Save_data(jprint_state *, data const *); diff --git a/src/sf.skel/main.c b/src/sf.skel/main.c deleted file mode 100644 index 5460454..0000000 --- a/src/sf.skel/main.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "../module/module.h" -#include "state.h" - -char const *Usage = "usage: %1$s start [name] [configuration]\n" - " %1$s stop [name] [configuration] [data]\n" - " %1$s get-endpoint [name] [configuration] [data] [interface]\n" - "unsupported commands: cmd mod\n"; - -static configuration Configuration; -static data Data; -static bool ConfigurationLoaded; -static bool DataLoaded; - -// HELPERS - -static void -Free_all(void) -{ - if(ConfigurationLoaded == true) Free_configuration(&Configuration); - if(DataLoaded == true) Free_data(&Data); -} - -static void -Load_configuration(ucl_object_t *root) -{ - char *Error = Parse_configuration(&Configuration, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: configuration error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - ConfigurationLoaded = true; -} - -static void -Load_data(ucl_object_t *root) -{ - char *Error = Parse_data(&Data, root, ""); - if(Error != NULL) - { - fprintf(stderr, "%s: data error: %s\n", Arg0, Error); - free(Error); - Free_all(); - exit(-1); - } - DataLoaded = true; - // TODO: check that data matches configuration -} - -// COMMANDS - -int -start(char const *Name, ucl_object_t *configuration) -{ - UNUSED(Name); - Load_configuration(configuration); - // Implement - if((true)) goto error; - jprint_state S; - bzero(&S, sizeof(S)); - S.F = stdout; - Save_data(&S, &Data); - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -stop(char const *Name, ucl_object_t *configuration, ucl_object_t *data) -{ - UNUSED(Name); - Load_configuration(configuration); - Load_data(data); - // Implement - if((true)) goto error; - Free_all(); - return 0; -error: - Free_all(); - return -1; -} - -int -get_endpoint(char const *Name, ucl_object_t *configuration, ucl_object_t *data, char const *Interface) -{ - UNUSED(Name, Interface); - Load_configuration(configuration); - Load_data(data); - // Implement - Free_all(); - return -1; -} - -int -cmd(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - UNUSED(Name, ArgCount, Arg); - Load_configuration(configuration); - Load_data(data); - // Implement - usage(); -} - -int -mod(char const *Name, ucl_object_t *configuration, ucl_object_t *data, size_t ArgCount, char **Arg) -{ - UNUSED(Name, ArgCount, Arg); - Load_configuration(configuration); - Load_data(data); - // Implement - usage(); -} diff --git a/src/sf.skel/state.c b/src/sf.skel/state.c deleted file mode 100644 index 3a1d297..0000000 --- a/src/sf.skel/state.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "state.h" - -// FREE - -void -Free_configuration(configuration *E) -{ - UNUSED(E); -} - -void -Free_data(data *E) -{ - UNUSED(E); -} - -// PARSE - -char * -Parse_configuration(configuration *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - UNUSED(E); - return NULL; -error: - return Error; -} - -char * -Parse_data(data *E, ucl_object_t const *root, char const *Position) -{ - char *Error; - UCL_CHECK_ROOT(OBJECT); - UNUSED(E); - return NULL; -error: - return Error; -} - -// SAVE - -void -Save_data(jprint_state *S, data const *E) -{ - JPrintObjectBegin(S); - UNUSED(E); - JPrintObjectEnd(S); -} diff --git a/src/sf.skel/state.h b/src/sf.skel/state.h deleted file mode 100644 index 9f52a2b..0000000 --- a/src/sf.skel/state.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "../jprint.h" - -struct -{ -} typedef configuration; - -struct -{ -} typedef data; - -void Free_configuration(configuration *); -void Free_data(data *); - -char *Parse_configuration(configuration *, ucl_object_t const *, char const *Position); -char *Parse_data(data *, ucl_object_t const *, char const *Position); - -void Save_data(jprint_state *, data const *); -- cgit v1.2.3