diff options
author | Joshua M. Clulow <jmc@joyent.com> | 2015-06-17 00:21:47 -0700 |
---|---|---|
committer | Joshua M. Clulow <jmc@joyent.com> | 2015-06-17 07:35:52 +0000 |
commit | 4fad45a1339a6a719ca443ab44fe94824603533a (patch) | |
tree | 2e319e36b95c1e7ee6afa6592834efaa3d146430 | |
parent | 99d40c2bd9543276ffe5c2c993fd7e6f0ba87f01 (diff) | |
download | illumos-joyent-release-20150611.tar.gz |
OS-4428 lxbrand lxinit should support static routes from NAPIrelease-20150611
-rw-r--r-- | usr/src/lib/brand/lx/lx_init/Makefile | 32 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_init/lxinit.c | 153 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_init/pipe_stream.c | 326 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_init/pipe_stream.h | 48 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_init/run_command.c | 281 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_init/run_command.h | 32 |
6 files changed, 856 insertions, 16 deletions
diff --git a/usr/src/lib/brand/lx/lx_init/Makefile b/usr/src/lib/brand/lx/lx_init/Makefile index 81087058f7..1b1276a8cb 100644 --- a/usr/src/lib/brand/lx/lx_init/Makefile +++ b/usr/src/lib/brand/lx/lx_init/Makefile @@ -23,8 +23,14 @@ # PROG = lxinit -PROGS = $(PROG) -OBJS = lxinit + +PROG_OBJS = lxinit.o pipe_stream.o run_command.o +LIST_OBJS = list.o + +OBJS = $(PROG_OBJS) \ + $(LIST_OBJS) +SRCS = $(PROG_OBJS:%.o=%.c) \ + $(LIST_OBJS:%.o=$(SRC)/common/list/%.c) all: $(PROG) @@ -33,21 +39,33 @@ include $(SRC)/cmd/Makefile.cmd # override the install directory ROOTBIN = $(ROOTBRANDDIR) -CLOBBERFILES = $(OBJS) $(ROOTPROGS) +CLOBBERFILES = $(OBJS) $(ROOTPROG) UTSBASE = $(SRC)/uts CFLAGS += $(CCVERBOSE) CPPFLAGS += -D_REENTRANT -I$(UTSBASE)/common/brand/lx -LDLIBS += -lzonecfg -lipadm -lsocket -linetutil -lnsl +LDLIBS += -lzonecfg -lipadm -lsocket -linetutil -lnsl -lcmdutils .KEEP_STATE: -install: all $(ROOTPROGS) +install: all $(ROOTPROG) clean: - $(RM) $(PROG) $(OBJS) + $(RM) $(PROG) $(OBJS) + +lint: lint_PROG lint_SRCS + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) -lint: lint_PROG +%.o: $(SRC)/common/list/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/lx/lx_init/lxinit.c b/usr/src/lib/brand/lx/lx_init/lxinit.c index 64e2f3ebae..1838ef3d37 100644 --- a/usr/src/lib/brand/lx/lx_init/lxinit.c +++ b/usr/src/lib/brand/lx/lx_init/lxinit.c @@ -53,6 +53,7 @@ #include <unistd.h> #include <libintl.h> #include <locale.h> +#include <libcmdutils.h> #include <arpa/inet.h> #include <net/route.h> @@ -61,6 +62,8 @@ #include <libinetutil.h> #include <sys/lx_brand.h> +#include "run_command.h" + static void lxi_err(char *msg, ...) __NORETURN; static void lxi_err(char *msg, ...); @@ -367,7 +370,8 @@ lxi_iface_ipv6(const char *iface) } static int -lxi_iface_gateway(const char *iface, const char *gwaddr) +lxi_iface_gateway(const char *iface, const char *dst, int dstpfx, + const char *gwaddr) { int idx, len, sockfd; char rtbuf[RTMBUFSZ]; @@ -385,22 +389,38 @@ lxi_iface_gateway(const char *iface, const char *gwaddr) rtm->rtm_type = RTM_ADD; rtm->rtm_version = RTM_VERSION; - /* The destination and netmask components have already been zeroed */ + + /* + * The destination and netmask components have already been zeroed, + * which represents the default gateway. If we were passed a more + * specific destination network, use that instead. + */ dst_sin->sin_family = AF_INET; netmask_sin->sin_family = AF_INET; + if (dst != NULL) { + struct sockaddr *mask = (struct sockaddr *)netmask_sin; + + if ((inet_pton(AF_INET, dst, &(dst_sin->sin_addr))) != 1 || + plen2mask(dstpfx, AF_INET, mask) != 0) { + lxi_warn("bad destination network %s/%d: %s", dst, + dstpfx, strerror(errno)); + return (-1); + } + } - dst_sin->sin_family = AF_INET; if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) { lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno)); return (-1); } - if ((idx = if_nametoindex(iface)) == 0) { - lxi_warn("unable to get interface index for %s: %s\n", - iface, strerror(errno)); - return (-1); + if (iface != NULL) { + if ((idx = if_nametoindex(iface)) == 0) { + lxi_warn("unable to get interface index for %s: %s\n", + iface, strerror(errno)); + return (-1); + } + rtm->rtm_index = idx; } - rtm->rtm_index = idx; if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno)); @@ -458,13 +478,126 @@ lxi_net_setup(zone_dochandle_t handle) if (zone_find_attr(attrs, "primary", &primary) == 0 && strncmp(primary, "true", MAXNAMELEN) == 0 && zone_find_attr(attrs, "gateway", &gateway) == 0) { - lxi_iface_gateway(iface, gateway); + lxi_iface_gateway(iface, NULL, 0, gateway); } } (void) zonecfg_endnwifent(handle); } static void +lxi_net_static_route(const char *line) +{ + /* + * Each static route line is a string of the form: + * + * "10.77.77.2|10.1.1.0/24|false" + * + * i.e. gateway address, destination network, and whether this is + * a "link local" route or a next hop route. + */ + custr_t *cu = NULL; + char *gw = NULL, *dst = NULL; + int pfx = -1; + int i; + + if (custr_alloc(&cu) != 0) { + lxi_err("custr_alloc failure"); + } + + for (i = 0; line[i] != '\0'; i++) { + if (gw == NULL) { + if (line[i] == '|') { + if ((gw = strdup(custr_cstr(cu))) == NULL) { + lxi_err("strdup failure"); + } + custr_reset(cu); + } else { + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + continue; + } + + if (dst == NULL) { + if (line[i] == '/') { + if ((dst = strdup(custr_cstr(cu))) == NULL) { + lxi_err("strdup failure"); + } + custr_reset(cu); + } else { + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + continue; + } + + if (pfx == -1) { + if (line[i] == '|') { + pfx = atoi(custr_cstr(cu)); + custr_reset(cu); + } else { + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + continue; + } + + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + + /* + * We currently only support "next hop" routes, so ensure that + * "linklocal" is false: + */ + if (strcmp(custr_cstr(cu), "false") != 0) { + lxi_warn("invalid static route: %s", line); + } + + if (lxi_iface_gateway(NULL, dst, pfx, gw) != 0) { + lxi_err("failed to add route: %s/%d -> %s", dst, pfx, gw); + } + + custr_free(cu); + free(gw); + free(dst); +} + +static void +lxi_net_static_routes(void) +{ + const char *cmd = "/native/usr/lib/brand/lx/routeinfo"; + char *const argv[] = { "routeinfo", NULL }; + char *const envp[] = { NULL }; + int code; + struct stat st; + char errbuf[512]; + + if (stat(cmd, &st) != 0 || !S_ISREG(st.st_mode)) { + /* + * This binary is (potentially) shipped from another + * consolidation. If it does not exist, then the platform does + * not currently support static routes for LX-branded zones. + */ + return; + } + + /* + * Run the command, firing the callback for each line that it + * outputs. When this function returns, static route processing + * is complete. + */ + if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf), + lxi_net_static_route, &code) != 0 || code != 0) { + lxi_err("failed to run \"%s\": %s", cmd, errbuf); + } +} + +static void lxi_config_close(zone_dochandle_t handle) { zonecfg_fini_handle(handle); @@ -511,6 +644,8 @@ main(int argc, char *argv[]) lxi_net_setup(handle); lxi_config_close(handle); + lxi_net_static_routes(); + lxi_net_ipadm_close(); lxi_log_close(); diff --git a/usr/src/lib/brand/lx/lx_init/pipe_stream.c b/usr/src/lib/brand/lx/lx_init/pipe_stream.c new file mode 100644 index 0000000000..8f06a07906 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/pipe_stream.c @@ -0,0 +1,326 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> +#include <port.h> +#include <poll.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/debug.h> +#include <sys/list.h> + +#include "pipe_stream.h" + +struct pipe_stream { + pipe_stream_loop_t *pis_loop; + + boolean_t pis_finished; + boolean_t pis_associated; + + void *pis_arg0; + void *pis_arg1; + + int pis_fd_write; + int pis_fd_read; + list_node_t pis_linkage; +}; + +struct pipe_stream_loop { + int psl_port; + + uint8_t *psl_buf; + size_t psl_buf_cap; + size_t psl_buf_occ; + + list_t psl_pipes; + + pipe_stream_data_cb *psl_cb_data; + pipe_stream_eof_cb *psl_cb_eof; + pipe_stream_error_cb *psl_cb_error; +}; + + +int +pipe_stream_loop_fini(pipe_stream_loop_t *psl) +{ + if (psl == NULL) { + return (0); + } + + VERIFY0(close(psl->psl_port)); + + while (!list_is_empty(&psl->psl_pipes)) { + pipe_stream_fini(list_head(&psl->psl_pipes)); + } + + list_destroy(&psl->psl_pipes); + free(psl); + + return (0); +} + +int +pipe_stream_loop_init(pipe_stream_loop_t **pslp, size_t bufsize, + pipe_stream_data_cb *data_cb, pipe_stream_eof_cb *eof_cb, + pipe_stream_error_cb *error_cb) +{ + pipe_stream_loop_t *psl; + + if ((psl = calloc(1, sizeof (*psl))) == NULL) { + return (-1); + } + + psl->psl_buf_cap = bufsize; + psl->psl_buf_occ = 0; + if ((psl->psl_buf = calloc(1, bufsize)) == NULL) { + free(psl); + return (-1); + } + + if ((psl->psl_port = port_create()) == -1) { + free(psl->psl_buf); + free(psl); + return (-1); + } + + psl->psl_cb_data = data_cb; + psl->psl_cb_eof = eof_cb; + psl->psl_cb_error = error_cb; + + list_create(&psl->psl_pipes, sizeof (pipe_stream_t), + offsetof(pipe_stream_t, pis_linkage)); + + *pslp = psl; + return (0); +} + +boolean_t +pipe_stream_loop_should_run(pipe_stream_loop_t *psl) +{ + pipe_stream_t *pis; + + for (pis = list_head(&psl->psl_pipes); pis != NULL; + pis = list_next(&psl->psl_pipes, pis)) { + if (!pis->pis_finished) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +int +pipe_stream_loop_run(pipe_stream_loop_t *psl) +{ + pipe_stream_t *pis; + port_event_t pev; + ssize_t sz; + + for (pis = list_head(&psl->psl_pipes); pis != NULL; + pis = list_next(&psl->psl_pipes, pis)) { + if (pis->pis_finished || pis->pis_associated) { + /* + * Skip streams that are already finished, as well as + * those that have already been associated with the + * port. + */ + continue; + } + + if (port_associate(psl->psl_port, PORT_SOURCE_FD, + pis->pis_fd_read, POLLIN, pis) != 0) { + return (-1); + } + } + +again: + if (port_get(psl->psl_port, &pev, NULL) != 0) { + switch (errno) { + case ETIME: + /* + * Timeout expired; return to caller. + */ + return (0); + + case EINTR: + /* + * Interrupted by signal. Try again. + */ + goto again; + + default: + return (-1); + } + } + + VERIFY(pev.portev_source == PORT_SOURCE_FD); + pis = (pipe_stream_t *)pev.portev_user; + VERIFY((int)pev.portev_object == pis->pis_fd_read); + pis->pis_associated = B_FALSE; + +read_again: + if ((sz = read(pis->pis_fd_read, psl->psl_buf, + psl->psl_buf_cap)) == -1) { + if (errno == EINTR) { + goto read_again; + } + + if (psl->psl_cb_error != NULL) { + psl->psl_cb_error(errno, pis->pis_arg0, pis->pis_arg1); + } + + VERIFY0(close(pis->pis_fd_read)); + pis->pis_fd_read = -1; + pis->pis_finished = B_TRUE; + } + psl->psl_buf_occ = sz; + + if (sz == 0) { + /* + * Stream EOF. + */ + pis->pis_finished = B_TRUE; + VERIFY0(close(pis->pis_fd_read)); + pis->pis_fd_read = -1; + if (psl->psl_cb_eof != NULL) { + psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1); + } + return (0); + } + + if (psl->psl_cb_data != NULL) { + int cbr = psl->psl_cb_data(psl->psl_buf, psl->psl_buf_occ, + pis->pis_arg0, pis->pis_arg1); + + if (cbr != 0) { + /* + * Callback failure: close file descriptor. + */ + pis->pis_finished = B_TRUE; + VERIFY0(close(pis->pis_fd_read)); + pis->pis_fd_read = -1; + if (psl->psl_cb_eof != NULL) { + psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1); + } + } + + return (0); + } + + return (0); +} + +int +pipe_stream_init(pipe_stream_loop_t *psl, pipe_stream_t **pisp, void *arg0, + void *arg1) +{ + int e = 0; + pipe_stream_t *pis; + int fds[2] = { -1, -1 }; + + if ((pis = calloc(1, sizeof (*pis))) == NULL) { + return (-1); + } + + if (pipe(fds) != 0) { + e = errno; + goto fail; + } + + pis->pis_fd_read = fds[0]; + pis->pis_fd_write = fds[1]; + + pis->pis_arg0 = arg0; + pis->pis_arg1 = arg1; + + pis->pis_finished = B_FALSE; + pis->pis_associated = B_FALSE; + + pis->pis_loop = psl; + list_insert_tail(&psl->psl_pipes, pis); + + *pisp = pis; + return (0); + +fail: + if (fds[0] != -1) { + VERIFY0(close(fds[0])); + } + if (fds[1] != -1) { + VERIFY0(close(fds[1])); + } + free(pis); + errno = e; + return (-1); +} + +int +pipe_stream_fini(pipe_stream_t *pis) +{ + if (pis == NULL) { + return (0); + } + + if (pis->pis_fd_read != -1) { + VERIFY0(close(pis->pis_fd_read)); + } + if (pis->pis_fd_write != -1) { + VERIFY0(close(pis->pis_fd_write)); + } + + list_remove(&pis->pis_loop->psl_pipes, pis); + + free(pis); + return (0); +} + +/* + * Called in the parent, after forking, to close the "write" end of the pipe. + */ +void +pipe_stream_parent_afterfork(pipe_stream_t *pis) +{ + if (pis->pis_fd_write != -1) { + (void) close(pis->pis_fd_write); + pis->pis_fd_write = -1; + } +} + +/* + * Called in the child, after forking, to close the "read" end of the + * pipe, and to dup the file descriptor into the right place. + */ +int +pipe_stream_child_afterfork(pipe_stream_t *pis, int dup_to) +{ + int e = 0; + + if (dup_to != -1) { + if (dup2(pis->pis_fd_write, dup_to) == -1) { + e = errno; + } + VERIFY0(close(pis->pis_fd_write)); + pis->pis_fd_write = dup_to; + } + + (void) close(pis->pis_fd_read); + pis->pis_fd_read = -1; + + errno = e; + return (e == 0 ? 0 : -1); +} diff --git a/usr/src/lib/brand/lx/lx_init/pipe_stream.h b/usr/src/lib/brand/lx/lx_init/pipe_stream.h new file mode 100644 index 0000000000..140ef18f8c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/pipe_stream.h @@ -0,0 +1,48 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _PIPE_STREAM_H +#define _PIPE_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int pipe_stream_data_cb(const uint8_t *, size_t, void *, void *); +typedef void pipe_stream_eof_cb(void *, void *); +typedef void pipe_stream_error_cb(int, void *, void *); + +typedef struct pipe_stream pipe_stream_t; +typedef struct pipe_stream_loop pipe_stream_loop_t; + +extern int pipe_stream_loop_fini(pipe_stream_loop_t *); +extern int pipe_stream_loop_init(pipe_stream_loop_t **, size_t, + pipe_stream_data_cb *, pipe_stream_eof_cb *, pipe_stream_error_cb *); + +extern int pipe_stream_init(pipe_stream_loop_t *, pipe_stream_t **, void *, + void *); +extern int pipe_stream_fini(pipe_stream_t *); + +extern void pipe_stream_parent_afterfork(pipe_stream_t *); +extern int pipe_stream_child_afterfork(pipe_stream_t *, int); + +extern boolean_t pipe_stream_loop_should_run(pipe_stream_loop_t *); +extern int pipe_stream_loop_run(pipe_stream_loop_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _PIPE_STREAM_H */ diff --git a/usr/src/lib/brand/lx/lx_init/run_command.c b/usr/src/lib/brand/lx/lx_init/run_command.c new file mode 100644 index 0000000000..ad00b60482 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/run_command.c @@ -0,0 +1,281 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <err.h> +#include <strings.h> +#include <unistd.h> +#include <wait.h> +#include <sys/types.h> +#include <sys/debug.h> +#include <libcmdutils.h> + +#include "run_command.h" +#include "pipe_stream.h" + +typedef struct cmd { + int cmd_pid; + int cmd_wstatus; + pipe_stream_t *cmd_pipe[2]; + custr_t *cmd_err; + custr_t *cmd_out; + run_command_line_cb *cmd_func; + boolean_t cmd_cancel; +} cmd_t; + +static int cb_data(const uint8_t *, size_t, void *, void *); +static void cb_eof(void *, void *); +static void cb_error(int, void *, void *); + +void +post_error(cmd_t *cmd, const char *estr) +{ + if (cmd->cmd_cancel) { + return; + } + cmd->cmd_cancel = B_TRUE; + + custr_reset(cmd->cmd_err); + (void) custr_append(cmd->cmd_err, estr); +} + +int +run_command(const char *path, char *const argv[], char *const envp[], + char *errbuf, size_t errlen, run_command_line_cb *func, int *status) +{ + pipe_stream_loop_t *psl = NULL; + int e = 0; + cmd_t cmd; + pid_t wpid; + + bzero(&cmd, sizeof (cmd)); + + cmd.cmd_func = func; + + /* + * Allocate string buffers for stdout line buffering and error + * messages: + */ + if (custr_alloc_buf(&cmd.cmd_err, errbuf, errlen) != 0 || + custr_alloc(&cmd.cmd_out) != 0) { + e = errno; + goto out; + } + + /* + * Initialise pipe stream event loop: + */ + if (pipe_stream_loop_init(&psl, 256, cb_data, cb_eof, cb_error) != 0) { + e = errno; + post_error(&cmd, "could not init pipe stream loop"); + goto out; + } + + /* + * Create pipe streams for stdout and stderr communication with + * child process: + */ + if (pipe_stream_init(psl, &cmd.cmd_pipe[0], &cmd, + (void *)STDOUT_FILENO) != 0 || + pipe_stream_init(psl, &cmd.cmd_pipe[1], &cmd, + (void *)STDERR_FILENO) != 0) { + e = errno; + post_error(&cmd, "could not init pipe streams"); + goto out; + } + + /* + * Fork a child process: + */ + if ((cmd.cmd_pid = fork()) == -1) { + e = errno; + post_error(&cmd, "could not fork"); + goto out; + } + + if (cmd.cmd_pid == 0) { + /* + * This is the child process. Clean up file descriptors, and + * connect stdio to the pipes we allocated: + */ + VERIFY0(close(STDIN_FILENO)); + VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[0], + STDOUT_FILENO)); + VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[1], + STDERR_FILENO)); + closefrom(3); + + execve(path, argv, envp); + err(127, "exec(%s) failed", path); + } + + /* + * Back in the parent. Close the remote end of the stdio pipes: + */ + pipe_stream_parent_afterfork(cmd.cmd_pipe[0]); + pipe_stream_parent_afterfork(cmd.cmd_pipe[1]); + + /* + * Run the pipe event loop until all streams are completely + * consumed: + */ + while (pipe_stream_loop_should_run(psl)) { + if (pipe_stream_loop_run(psl) != 0) { + e = errno; + post_error(&cmd, "pipe stream loop run failure"); + goto out; + } + } + + /* + * Collect exit status of child process: + */ + while ((wpid = waitpid(cmd.cmd_pid, &cmd.cmd_wstatus, 0)) != + cmd.cmd_pid) { + if (wpid == -1 && errno != EINTR) { + e = errno; + post_error(&cmd, "waitpid failure"); + goto out; + } + } + + /* + * If the child died on a signal, fail the whole operation: + */ + if (WIFSIGNALED(cmd.cmd_wstatus)) { + e = ENXIO; + post_error(&cmd, "child process died on signal"); + (void) custr_append_printf(cmd.cmd_err, " (pid %d signal %d)", + cmd.cmd_pid, WTERMSIG(cmd.cmd_wstatus)); + goto out; + } + + /* + * If the child did not appear to exit, fail the whole operation: + */ + if (!WIFEXITED(cmd.cmd_wstatus)) { + e = ENXIO; + post_error(&cmd, "child process did not exit"); + (void) custr_append_printf(cmd.cmd_err, " (pid %d status %x)", + cmd.cmd_pid, cmd.cmd_wstatus); + goto out; + } + + /* + * Report exit status to the caller: + */ + *status = WEXITSTATUS(cmd.cmd_wstatus); + e = 0; + +out: + VERIFY0(pipe_stream_loop_fini(psl)); + /* + * Note that freeing the static error custr_t does not touch the + * underlying storage; we use this property to return the error + * message (if one exists) to the caller. + */ + custr_free(cmd.cmd_err); + custr_free(cmd.cmd_out); + errno = e; + return (e == 0 ? 0 : -1); +} + +static int +cb_data(const uint8_t *buf, size_t sz, void *arg0, void *arg1) +{ + cmd_t *cmd = arg0; + int fd = (int)arg1; + unsigned int i; + + if (cmd->cmd_cancel) { + return (-1); + } + + switch (fd) { + case STDOUT_FILENO: + for (i = 0; i < sz; i++) { + if (buf[i] == '\0' || buf[i] == '\r') { + continue; + } + + if (buf[i] == '\n') { + cmd->cmd_func(custr_cstr(cmd->cmd_out)); + custr_reset(cmd->cmd_out); + continue; + } + + if (custr_appendc(cmd->cmd_out, buf[i]) != 0) { + /* + * Failed to allocate memory; returning + * -1 here will abort the stream. + */ + post_error(cmd, "custr_appendc failure"); + return (-1); + } + } + break; + + case STDERR_FILENO: + /* + * Collect as much stderr output as will fit in our static + * buffer. + */ + for (i = 0; i < sz; i++) { + if (buf[i] == '\0') { + continue; + } + + (void) custr_appendc(cmd->cmd_err, buf[i]); + } + break; + + default: + abort(); + } + + return (0); +} + +static void +cb_eof(void *arg0, void *arg1) +{ + cmd_t *cmd = arg0; + int fd = (int)arg1; + + if (cmd->cmd_cancel) { + return; + } + + if (fd == STDOUT_FILENO && custr_len(cmd->cmd_out) > 0) { + cmd->cmd_func(custr_cstr(cmd->cmd_out)); + custr_reset(cmd->cmd_out); + } +} + +static void +cb_error(int e, void *arg0, void *arg1) +{ + cmd_t *cmd = arg0; + int fd = (int)arg1; + + if (cmd->cmd_cancel) { + return; + } + + post_error(cmd, "stream read failure"); + (void) custr_append_printf(cmd->cmd_err, " (pid %d fd %d): %s", + cmd->cmd_pid, fd, strerror(e)); +} diff --git a/usr/src/lib/brand/lx/lx_init/run_command.h b/usr/src/lib/brand/lx/lx_init/run_command.h new file mode 100644 index 0000000000..3484b265b6 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/run_command.h @@ -0,0 +1,32 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _RUN_COMMAND_H +#define _RUN_COMMAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void run_command_line_cb(const char *); + +extern int run_command(const char *, char *const [], char *const [], char *, + size_t, run_command_line_cb *, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _RUN_COMMAND_H */ |