diff options
Diffstat (limited to 'src/util.c')
| -rw-r--r-- | src/util.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..f7d0aef --- /dev/null +++ b/src/util.c @@ -0,0 +1,396 @@ +#include "util.h" + +#include <net/ethernet.h> +#include <netgraph.h> +#include <netgraph/ng_message.h> +#include <netgraph/ng_eiface.h> +#include <netgraph/ng_bridge.h> +#include <sys/user.h> +#include <libutil.h> + +#include <sys/jail.h> + +#include "/usr/src/sbin/mount/mntopts.h" +#include <sys/mount.h> + +int +DestroyNetgraphNode(u32 ID, fd Control) +{ + char Path[NG_PATHSIZ]; + snprintf(Path, sizeof(Path), "[%x]:", ID); + if(NgSendMsg(Control, Path, NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0) == -1) + { + fprintf(stderr, "ngctl shutdown %s: %s\n", Path, strerror(errno)); + return -1; + } + return 0; +} + +int +Create_eiface(u32 *ID, fd Control) +{ + *ID = 0; + { + struct ngm_mkpeer D; + strcpy(D.type, "eiface"); + strcpy(D.ourhook, "_"); + strcpy(D.peerhook, "ether"); + if(NgSendMsg(Control, ".:", NGM_GENERIC_COOKIE, NGM_MKPEER, &D, sizeof(D)) == -1) + { + fprintf(stderr, "ngctl mkpeer .: eiface _ ether: %s\n", strerror(errno)); + goto error; + } + } + { + if(NgSendMsg(Control, ".:_", NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) == -1) + { + fprintf(stderr, "ngctl info .:_ : %s\n", strerror(errno)); + goto error; + } + struct ng_mesg *Response; + if(NgAllocRecvMsg(Control, &Response, NULL) == -1) + { + fprintf(stderr, "NgAllocRecvMsg: %s\n", strerror(errno)); + return -1; + } + struct nodeinfo *Info = (void *)Response->data; + *ID = Info->id; + free(Response); + } + { + struct ngm_rmhook D; + strcpy(D.ourhook, "_"); + if(NgSendMsg(Control, ".:", NGM_GENERIC_COOKIE, NGM_RMHOOK, &D, sizeof(D)) == -1) + { + fprintf(stderr, "ngctl rmhook .: _: %s\n", strerror(errno)); + goto error; + } + } + return 0; +error: + if(*ID != 0) DestroyNetgraphNode(*ID, Control); + return -1; +} + +int +Create_bridge(u32 *ID, fd Control) +{ + *ID = 0; + { + struct ngm_mkpeer D; + strcpy(D.type, "bridge"); + strcpy(D.ourhook, "_"); + strcpy(D.peerhook, "link0"); + if(NgSendMsg(Control, ".:", NGM_GENERIC_COOKIE, NGM_MKPEER, &D, sizeof(D)) == -1) + { + fprintf(stderr, "ngctl mkpeer .: eiface _ ether: %s\n", strerror(errno)); + goto error; + } + } + { + if(NgSendMsg(Control, ".:_", NGM_BRIDGE_COOKIE, NGM_BRIDGE_SET_PERSISTENT, NULL, 0) == -1) + { + fprintf(stderr, "ngctl msg .:_ setpersistent: %s\n", strerror(errno)); + goto error; + } + } + { + if(NgSendMsg(Control, ".:_", NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) == -1) + { + fprintf(stderr, "ngctl info .:_ : %s\n", strerror(errno)); + goto error; + } + struct ng_mesg *Response; + if(NgAllocRecvMsg(Control, &Response, NULL) == -1) + { + fprintf(stderr, "NgAllocRecvMsg: %s\n", strerror(errno)); + return -1; + } + struct nodeinfo *Info = (void *)Response->data; + *ID = Info->id; + free(Response); + } + { + struct ngm_rmhook D; + strcpy(D.ourhook, "_"); + if(NgSendMsg(Control, ".:", NGM_GENERIC_COOKIE, NGM_RMHOOK, &D, sizeof(D)) == -1) + { + fprintf(stderr, "ngctl rmhook .: _: %s\n", strerror(errno)); + goto error; + } + } + return 0; +error: + if(*ID != 0) DestroyNetgraphNode(*ID, Control); + return -1; +} + +char * +getifname(u32 ID, fd Control) +{ + struct ng_mesg *Response; + char Path[NG_PATHSIZ]; + snprintf(Path, sizeof(Path), "[%x]:", ID); + if(NgSendMsg(Control, Path, NGM_EIFACE_COOKIE, NGM_EIFACE_GET_IFNAME, NULL, 0) == -1) + { + fprintf(stderr, "ngctl msg %s getifname: %s\n", Path, strerror(errno)); + return NULL; + } + if(NgAllocRecvMsg(Control, &Response, NULL) == -1) + { + fprintf(stderr, "NgAllocRecvMsg: %s\n", strerror(errno)); + return NULL; + } + char *Result = strdup((void *)Response->data); + free(Response); + return Result; +} + +void +close_all(void) +{ + int Count; + struct kinfo_file *Result = kinfo_getfile(getpid(), &Count); + if(Result == NULL) return; + for(int i = 0; i < Count; ++i) + { + if(Result[i].kf_fd > STDERR_FILENO) close(Result[i].kf_fd); + } + free(Result); +} + +int +command_a(char const *Program, char **Arg) +{ + pid_t PID = -1; + PID = fork(); + if(PID == -1) + { + fprintf(stderr, "fork failed: %s\n", strerror(errno)); + goto error; + } + if(PID == 0) + { + close(STDIN_FILENO); + close(STDOUT_FILENO); + dup2(STDERR_FILENO, STDOUT_FILENO); + close_all(); + execvp(Program, Arg); + fprintf(stderr, "exec failed: %s\n", strerror(errno)); + exit(-1); + } + int Status; + waitpid(PID, &Status, 0); + if(!WIFEXITED(Status) || (WEXITSTATUS(Status) != 0)) return -1; + return 0; +error: + return -1; +} + +int +command_v(char const *Program, va_list Arg) +{ + va_list Arg_; + va_copy(Arg_, Arg); + size_t ArgCount = 0; + do { + char const *_ = va_arg(Arg_, char const *); + ++ArgCount; + if(_ == NULL) break; + } while(true); + va_end(Arg_); + char **Args = alloca(sizeof(char *) * ArgCount); + for(size_t i = 0; i < ArgCount; ++i) Args[i] = va_arg(Arg, char *); + return command_a(Program, Args); +} + +int +command(char const *Program, ...) +{ + va_list Arg; + va_start(Arg, Program); + int Result = command_v(Program, Arg); + va_end(Arg); + return Result; +} + +void +Free_string_array(string_array *E) +{ + for(size_t i = 0; i < E->Count; ++i) free(E->_[i]); + if(E->_ != NULL) free(E->_); +} + +char * +Parse_string_array(string_array *E, ucl_object_t const *root, char const *Position) +{ + char *Error; + UCL_CHECK_ROOT(ARRAY); + E->Count = root->len; + E->_ = calloc(sizeof(char *), E->Count + 1); // NOTE: we null-terminate it + 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; + if(v->type != UCL_STRING) + { + asprintf(&Error, "%s[%zu] is not a string", Position, E->Count); + goto error; + } + E->_[E->Count] = strdup(ucl_object_tostring(v)); + } + return NULL; +error: + Free_string_array(E); + return Error; +} + +void +Save_string_array(jprint_state *S, string_array const *E) +{ + JPrintArrayBegin(S); + for(size_t i = 0; i < E->Count; ++i) JPrint_string(S, E->_[i]); + JPrintArrayEnd(S); +} + +int +exec_string_array(string_array *E, bool Wait, jid_t JID) +{ + if(E->Count == 0) return 0; + pid_t PID = fork(); + if(PID == -1) return -1; + if(PID == 0) + { + close(STDIN_FILENO); + dup2(STDERR_FILENO, STDOUT_FILENO); + close_all(); + if(JID != 0) + { + if(jail_attach(JID) == -1) + { + fprintf(stderr, "failed to attach to jail %d: %s\n", JID, strerror(errno)); + exit(-1); + } + } + execvp(E->_[0], E->_); + fprintf(stderr, "failed exec '%s': %s\n", E->_[0], strerror(errno)); + exit(-1); + } + if(Wait == true) + { + int Exit; + waitpid(PID, &Exit, 0); + if(!WIFEXITED(Exit) || (WEXITSTATUS(Exit) != 0)) return -1; + } + return 0; +} + +static int +subdir(char const *p, char const *dir) +{ + size_t l = strlen(dir); + if(l <= 1) return 1; + if((strncmp(p, dir, l) == 0) && (p[l] == '/' || p[l] == '\0')) return 1; + return 0; +} + +int +mount_unionfs(char const *Target, char const *Source, bool Below) +{ + char TargetR[MAXPATHLEN]; + char SourceR[MAXPATHLEN]; + if(checkpath(Target, TargetR) != 0) + { + fprintf(stderr, "checkpath %s\n", TargetR); + return -1; + } + if(checkpath(Source, SourceR) != 0) + { + fprintf(stderr, "checkpath %s\n", SourceR); + return -1; + } + if(subdir(TargetR, SourceR) || subdir(SourceR, TargetR)) + { + fprintf(stderr, "%s (%s) and %s (%s) are not distinct paths\n", Target, TargetR, Source, SourceR); + return -1; + } + char errmsg[255]; + bzero(errmsg, sizeof(errmsg)); + struct iovec *iov = NULL; + int iovlen = 0; + build_iovec(&iov, &iovlen, "fstype", "unionfs", (size_t)-1); + if(Below == true) build_iovec(&iov, &iovlen, "below", NULL, 0); + build_iovec(&iov, &iovlen, "fspath", TargetR, (size_t)-1); + build_iovec(&iov, &iovlen, "from", SourceR, (size_t)-1); + build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); + if(nmount(iov, (u32)iovlen, 0)) + { + fprintf(stderr, "nmount unionfs %s: %s\n", SourceR, errmsg); + return -1; + } + return 0; +} + +int +mount_devfs(char const *Target) +{ + char TargetR[MAXPATHLEN]; + if(checkpath(Target, TargetR) != 0) + { + fprintf(stderr, "checkpath %s\n", TargetR); + return -1; + } + char errmsg[255]; + bzero(errmsg, sizeof(errmsg)); + struct iovec *iov = NULL; + int iovlen = 0; + build_iovec(&iov, &iovlen, "fstype", "devfs", (size_t)-1); + build_iovec(&iov, &iovlen, "fspath", TargetR, (size_t)-1); + build_iovec(&iov, &iovlen, "from", "devfs", (size_t)-1); + build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); + if(nmount(iov, (u32)iovlen, 0)) + { + fprintf(stderr, "nmount devfs: %s\n", errmsg); + return -1; + } + return 0; +} + +int +mount_nullfs(char const *Target, char const *Source) +{ + char TargetR[MAXPATHLEN]; + char SourceR[MAXPATHLEN]; + if(checkpath(Target, TargetR) != 0) + { + fprintf(stderr, "checkpath %s\n", TargetR); + return -1; + } + if(checkpath(Source, SourceR) != 0) + { + fprintf(stderr, "checkpath %s\n", SourceR); + return -1; + } + if(subdir(TargetR, SourceR) || subdir(SourceR, TargetR)) + { + fprintf(stderr, "%s (%s) and %s (%s) are not distinct paths\n", Target, TargetR, Source, SourceR); + return -1; + } + char errmsg[255]; + bzero(errmsg, sizeof(errmsg)); + struct iovec *iov = NULL; + int iovlen = 0; + build_iovec(&iov, &iovlen, "fstype", "nullfs", (size_t)-1); + build_iovec(&iov, &iovlen, "fspath", TargetR, (size_t)-1); + build_iovec(&iov, &iovlen, "from", SourceR, (size_t)-1); + build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); + if(nmount(iov, (u32)iovlen, 0)) + { + fprintf(stderr, "nmount nullfs %s: %s\n", SourceR, errmsg); + return -1; + } + return 0; +} |
