#include "util.h" #include #include #include #include #include #include #include #include #include "/usr/src/sbin/mount/mntopts.h" #include 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 .: bridge _ link0: %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; } int Create_pipe(u32 *ID, fd Control, char const *NodeA, char const *NodeB, char const *HookA, char const *HookB) { *ID = 0; { struct ngm_mkpeer D; strcpy(D.type, "pipe"); strncpy(D.ourhook, HookA, sizeof(D.ourhook)); strcpy(D.peerhook, "lower"); if(NgSendMsg(Control, NodeA, NGM_GENERIC_COOKIE, NGM_MKPEER, &D, sizeof(D)) == -1) { fprintf(stderr, "ngctl mkpeer %s pipe %s lower: %s\n", NodeA, HookA, strerror(errno)); goto error; } } char Path[NG_PATHSIZ]; { snprintf(Path, sizeof(Path), "%s%s", NodeA, HookA); if(NgSendMsg(Control, Path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) == -1) { fprintf(stderr, "ngctl info %s : %s\n", Path, 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); } if(ng_connect(Control, NodeB, Path, HookB, "upper") == -1) goto error; return 0; error: if(*ID != 0) DestroyNetgraphNode(*ID, Control); return -1; } int ng_connect(fd Control, char const *NodeA, char const *NodeB, char const *HookA, char const *HookB) { struct ngm_connect D; char Path0[NG_PATHSIZ]; strncpy(Path0, NodeA, sizeof(Path0)); strncpy(D.path, NodeB, sizeof(D.path)); strncpy(D.ourhook, HookA, sizeof(D.ourhook)); strncpy(D.peerhook, HookB, sizeof(D.peerhook)); if(NgSendMsg(Control, Path0, NGM_GENERIC_COOKIE, NGM_CONNECT, &D, sizeof(D)) == -1) { fprintf(stderr, "ngctl connect %s %s %s %s: %s\n", Path0, D.path, D.ourhook, D.peerhook, strerror(errno)); return -1; } return 0; } 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; }