summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua M. Clulow <jmc@joyent.com>2015-06-17 00:21:47 -0700
committerJoshua M. Clulow <jmc@joyent.com>2015-06-17 07:35:52 +0000
commit4fad45a1339a6a719ca443ab44fe94824603533a (patch)
tree2e319e36b95c1e7ee6afa6592834efaa3d146430
parent99d40c2bd9543276ffe5c2c993fd7e6f0ba87f01 (diff)
downloadillumos-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/Makefile32
-rw-r--r--usr/src/lib/brand/lx/lx_init/lxinit.c153
-rw-r--r--usr/src/lib/brand/lx/lx_init/pipe_stream.c326
-rw-r--r--usr/src/lib/brand/lx/lx_init/pipe_stream.h48
-rw-r--r--usr/src/lib/brand/lx/lx_init/run_command.c281
-rw-r--r--usr/src/lib/brand/lx/lx_init/run_command.h32
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 */