Use common subprocSystem for executing commands
authorRobert Swiecki <robert@swiecki.net>
Wed, 12 Oct 2016 00:01:12 +0000 (02:01 +0200)
committerRobert Swiecki <robert@swiecki.net>
Wed, 12 Oct 2016 00:01:12 +0000 (02:01 +0200)
net.c
subproc.c
subproc.h
user.c

diff --git a/net.c b/net.c
index f93de91af9e84f1f2bbb59235cefad6e25d9dd36..cddca0f41f91d86fb569db97100594bee3daed2a 100644 (file)
--- a/net.c
+++ b/net.c
@@ -42,6 +42,7 @@
 #include <unistd.h>
 
 #include "log.h"
+#include "subproc.h"
 
 #define IFACE_NAME "vs"
 
@@ -114,51 +115,15 @@ bool netInitNsFromParent(struct nsjconf_t *nsjconf, int pid)
        return true;
 }
 #else                          // defined(NSJAIL_NL3_WITH_MACVLAN)
-static bool netSystemSbinIp(struct nsjconf_t *nsjconf, char *const *argv)
+static bool netSystemSbinIp(struct nsjconf_t *nsjconf, const char *const *argv)
 {
-       if (nsjconf->clone_newnet == false) {
-               LOG_W
-                   ("CLONE_NEWNET not enabled. All changes would affect the global networking namespace");
-               return false;
-       }
-
-       int pid = fork();
-       if (pid == -1) {
-               PLOG_E("fork()");
-               return false;
-       }
-       if (pid == 0) {
-               execve("/sbin/ip", argv, environ);
-               PLOG_E("execve('/sbin/ip'");
-               _exit(1);
-       }
-
-       for (;;) {
-               int status;
-               while (wait4(pid, &status, __WALL, NULL) != pid) ;
-               if (WIFEXITED(status)) {
-                       if (WEXITSTATUS(status) == 0) {
-                               return true;
-                       }
-                       LOG_W("'/sbin/ip' returned with exit status: %d", WEXITSTATUS(status));
-                       return false;
-               }
-               if (WIFSIGNALED(status)) {
-                       LOG_W("'/sbin/ip' killed with signal: %d", WTERMSIG(status));
-                       return false;
-               }
-               if (WIFSTOPPED(status)) {
-                       continue;
-               }
-               if (WIFCONTINUED(status)) {
-                       continue;
-               }
-               LOG_W("Unknown exit status for '/sbin/ip' (pid=%d): %d", pid, status);
-               kill(pid, SIGKILL);
+       if (subprocSystem(argv, environ) == 0) {
+               return true;
        }
+       return false;
 }
 
-bool netInitNsFromParent(struct nsjconf_t *nsjconf, int pid)
+bool netInitNsFromParent(struct nsjconf_t * nsjconf, int pid)
 {
        if (nsjconf->clone_newnet == false) {
                return true;
@@ -171,7 +136,8 @@ bool netInitNsFromParent(struct nsjconf_t *nsjconf, int pid)
        snprintf(pid_str, sizeof(pid_str), "%d", pid);
 
        char *const argv_add[] =
-           { "ip", "link", "add", "link", (char *)nsjconf->iface, "name", IFACE_NAME, "netns",
+           { "/sbin/ip", "link", "add", "link", (char *)nsjconf->iface, "name", IFACE_NAME,
+               "netns",
                pid_str, "type", "macvlan", "mode", "bridge", NULL
        };
        if (netSystemSbinIp(nsjconf, argv_add) == false) {
index 047600a064e2d0ba81300275feba89e10ac82cc7..b93d9684ab925f912864b45675c151b8fa7a1baa 100644 (file)
--- a/subproc.c
+++ b/subproc.c
@@ -352,3 +352,68 @@ void subprocRunChild(struct nsjconf_t *nsjconf, int fd_in, int fd_out, int fd_er
        netConnToText(fd_in, true /* remote */ , cs_addr, sizeof(cs_addr), NULL);
        LOG_I("PID: %d about to execute '%s' for %s", pid, nsjconf->argv[0], cs_addr);
 }
+
+int subprocSystem(const char **argv, char **env)
+{
+       bool exec_failed = false;
+
+       int sv[2];
+       if (pipe2(sv, O_CLOEXEC) == -1) {
+               PLOG_W("pipe2(sv, O_CLOEXEC");
+               return -1;
+       }
+
+       pid_t pid = fork();
+       if (pid == -1) {
+               PLOG_W("fork()");
+               close(sv[0]);
+               close(sv[1]);
+               return -1;
+       }
+
+       if (pid == 0) {
+               close(sv[0]);
+               execve(argv[0], (char *const *)argv, (char *const *)env);
+               PLOG_W("execve('%s')", argv[0]);
+               utilWriteToFd(sv[1], "A", 1);
+               exit(0);
+       }
+
+       close(sv[1]);
+       char buf[1];
+       if (utilReadFromFd(sv[0], buf, sizeof(buf)) > 0) {
+               exec_failed = true;
+               LOG_W("Couldn't execute '%s'", argv[0]);
+       }
+       close(sv[0]);
+
+       for (;;) {
+               int status;
+               int ret = wait4(pid, &status, __WALL, NULL);
+               if (ret == -1 && errno == EINTR) {
+                       continue;
+               }
+               if (ret == -1) {
+                       PLOG_W("wait4(pid=%d)", pid);
+                       return -1;
+               }
+               if (WIFEXITED(status)) {
+                       int exit_code = WEXITSTATUS(status);
+                       LOG_D("PID %d exited with exit code: %d", pid, exit_code);
+                       if (exec_failed == true) {
+                               return -1;
+                       } else if (exit_code == 0) {
+                               return 0;
+                       } else {
+                               return 1;
+                       }
+               }
+               if (WIFSIGNALED(status)) {
+                       int exit_signal = WTERMSIG(status);
+                       LOG_W("PID %d killed by a signal: %d (%s)", pid, exit_signal,
+                             strsignal(exit_signal));
+                       return 2;
+               }
+               LOG_W("Unknown exit status: %d", status);
+       }
+}
index ce25ca4735433f3e50c7283e52135a76e313c371..fa2d61a89cef613ddf8a5df2087b1a4fce07b1a1 100644 (file)
--- a/subproc.h
+++ b/subproc.h
@@ -28,6 +28,7 @@ void subprocRunChild(struct nsjconf_t *nsjconf, int fd_in, int fd_out, int fd_er
 int subprocCount(struct nsjconf_t *nsjconf);
 void subprocDisplay(struct nsjconf_t *nsjconf);
 void subprocKillAll(struct nsjconf_t *nsjconf);
+int subprocSystem(const char **argv, char **env);
 
 /* Returns the exit code of the first failing subprocess, or 0 if none fail */
 int subprocReap(struct nsjconf_t *nsjconf);
diff --git a/user.c b/user.c
index 56c5fb558ae83a0a34d2b83eeb686d97bafac82e..950fea77f7a45da1e09fba4b7b12757d1c891ecd 100644 (file)
--- a/user.c
+++ b/user.c
@@ -32,6 +32,7 @@
 #include <unistd.h>
 
 #include "log.h"
+#include "subproc.h"
 #include "util.h"
 
 static bool userSetGroups(pid_t pid)
@@ -87,79 +88,70 @@ static bool userGidMapSelf(struct nsjconf_t *nsjconf, pid_t pid)
        return true;
 }
 
-// use /usr/bin/newgidmap for writing the uid and gid map
+/* Use /usr/bin/newgidmap for writing the gid map */
 static bool userGidMapExternal(struct nsjconf_t *nsjconf, pid_t pid)
 {
-       char cmd_buf[1024];
-       char *cmd_ptr = cmd_buf;
-       size_t len = sizeof(cmd_buf);
-       int write_size;
-
-       write_size = snprintf(cmd_ptr, len, "/usr/bin/newgidmap %lu %lu %lu 1",
-                             (unsigned long)pid,
-                             (unsigned long)nsjconf->inside_gid,
-                             (unsigned long)nsjconf->outside_gid);
-       if (write_size <= 0 || (size_t) write_size > len) {
-               LOG_E("snprintf writing the new{u,g}idmap command failed");
-               return false;
-       }
-       cmd_ptr += write_size;
-       len -= write_size;
+       char pid_str[16];
+       char ins_gid_str[16];
+       char out_gid_str[16];
+
+       snprintf(pid_str, sizeof(pid_str), "%lu", (unsigned long)pid);
+       snprintf(ins_gid_str, sizeof(ins_gid_str), "%lu", (unsigned long)nsjconf->inside_gid);
+       snprintf(out_gid_str, sizeof(out_gid_str), "%lu", (unsigned long)nsjconf->outside_gid);
+
+       const char *argv[1024] = { "/usr/bin/newgidmap", pid_str, ins_gid_str, out_gid_str, "1" };
+       size_t argv_idx = 5;
 
        struct mapping_t *p;
        TAILQ_FOREACH(p, &nsjconf->gid_mappings, pointers) {
-               write_size = snprintf(cmd_ptr, len, " %s %s %s",
-                                     p->inside_id, p->outside_id, p->count);
-               if (write_size <= 0 || (size_t) write_size > len) {
-                       LOG_E("snprintf writing the new{u,g}idmap command failed");
+               if ((argv_idx + 4) >= ARRAYSIZE(argv)) {
+                       LOG_W("Number of arguments to '/usr/bin/newgidmap' too big");
                        return false;
                }
-               cmd_ptr += write_size;
-               len -= write_size;
+
+               argv[argv_idx++] = p->inside_id;
+               argv[argv_idx++] = p->outside_id;
+               argv[argv_idx++] = p->count;
        }
+       argv[argv_idx++] = NULL;
 
-       if (system(cmd_buf) != 0) {
-               LOG_E("system('%s') failed", cmd_buf);
-               while (1) ;
+       if (subprocSystem(argv, environ) != 0) {
+               LOG_E("'/usr/bin/newgidmap' failed");
                return false;
        }
 
        return true;
 }
 
-// use /usr/bin/newuidmap for writing the uid and gid map
+/* Use /usr/bin/newuidmap for writing the uid map */
 static bool userUidMapExternal(struct nsjconf_t *nsjconf, pid_t pid)
 {
-       char cmd_buf[1024];
-       char *cmd_ptr = cmd_buf;
-       size_t len = sizeof(cmd_buf);
-       int write_size;
-
-       write_size = snprintf(cmd_ptr, len, "/usr/bin/newuidmap %lu %lu %lu 1",
-                             (unsigned long)pid,
-                             (unsigned long)nsjconf->inside_uid,
-                             (unsigned long)nsjconf->outside_uid);
-       if (write_size <= 0 || (size_t) write_size > len) {
-               LOG_E("snprintf writing the new{u,g}idmap command failed");
-               return false;
-       }
-       cmd_ptr += write_size;
-       len -= write_size;
+       char pid_str[16];
+       char ins_uid_str[16];
+       char out_uid_str[16];
+
+       snprintf(pid_str, sizeof(pid_str), "%lu", (unsigned long)pid);
+       snprintf(ins_uid_str, sizeof(ins_uid_str), "%lu", (unsigned long)nsjconf->inside_uid);
+       snprintf(out_uid_str, sizeof(out_uid_str), "%lu", (unsigned long)nsjconf->outside_uid);
+
+       const char *argv[1024] = { "/usr/bin/newuidmap", pid_str, ins_uid_str, out_uid_str, "1" };
+       size_t argv_idx = 5;
 
        struct mapping_t *p;
        TAILQ_FOREACH(p, &nsjconf->uid_mappings, pointers) {
-               write_size = snprintf(cmd_ptr, len, " %s %s %s",
-                                     p->inside_id, p->outside_id, p->count);
-               if (write_size <= 0 || (size_t) write_size > len) {
-                       LOG_E("snprintf writing the new{u,g}idmap command failed");
+               if ((argv_idx + 4) >= ARRAYSIZE(argv)) {
+                       LOG_W("Number of arguments to '/usr/bin/newuidmap' too big");
                        return false;
                }
-               cmd_ptr += write_size;
-               len -= write_size;
+
+               argv[argv_idx++] = p->inside_id;
+               argv[argv_idx++] = p->outside_id;
+               argv[argv_idx++] = p->count;
        }
+       argv[argv_idx++] = NULL;
 
-       if (system(cmd_buf) != 0) {
-               LOG_E("system('%s') failed", cmd_buf);
+       if (subprocSystem(argv, environ) != 0) {
+               LOG_E("'/usr/bin/newuidmap' failed");
                return false;
        }