diff options
Diffstat (limited to 'util.c')
-rw-r--r-- | util.c | 76 |
1 files changed, 76 insertions, 0 deletions
@@ -211,3 +211,79 @@ int r_mkdir(char *path) } return 0; } + +void construct_argv(char **argv, unsigned int len, ...) +{ + unsigned int i; + va_list args; + + va_start(args, len); + for (i = 0; i < len; ++i) + argv[i] = va_arg(args, char *); + va_end(args); + if (argv[len-1] != NULL) + error(EXIT_FAILURE, 0, "argv not NULL terminated"); +} + +spawn_t spawn(const char *cmd, char *const argv[], unsigned int flags) +{ + pid_t pid; + spawn_t status = { -1, -1, -1 }; + int pfd_read[2] = { -1, -1 }; + int pfd_write[2] = { -1, -1 }; + const bool r = flags & X_READ; + const bool w = flags & X_WRITE; + + if (cmd == NULL || argv == NULL || flags == 0) + return status; + + if (r && pipe(pfd_read) < 0) { + error(0, errno, "pipe: %s", cmd); + return status; + } + + if (w && pipe(pfd_write) < 0) { + if (r) { + close(pfd_read[0]); + close(pfd_read[1]); + } + error(0, errno, "pipe: %s", cmd); + return status; + } + + if ((pid = fork()) == 0) { + bool err = (r && dup2(pfd_read[1], 1) < 0) || (w && dup2(pfd_write[0], 0) < 0); + if (r) { + close(pfd_read[0]); + close(pfd_read[1]); + } + if (w) { + close(pfd_write[0]); + close(pfd_write[1]); + } + + if (err) + error(EXIT_FAILURE, errno, "dup2: %s", cmd); + execv(cmd, argv); + error(EXIT_FAILURE, errno, "exec: %s", cmd); + } + + if (r) + close(pfd_read[1]); + if (w) + close(pfd_write[0]); + + if (pid < 0) { + if (r) + close(pfd_read[0]); + if (w) + close(pfd_write[1]); + error(0, errno, "fork: %s", cmd); + return status; + } + + status.pid = pid; + status.readfd = pfd_read[0]; + status.writefd = pfd_write[1]; + return status; +} |