summaryrefslogtreecommitdiff
path: root/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.c')
-rw-r--r--src/util.c396
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;
+}