+ int so_size = 0, se_size = 0;
+ int so_fd[2], se_fd[2];
+ int flag_copy_stdin = flags & COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN;
+ int stdin_fd[2] = { -1, -1 };
+ pid_t pid, stdin_pid = -1;
+ int r, quit, i;
+ fd_set rset, rset2;
+ char buf[256];
+ char *p;
+
+ if (stdoutput) *stdoutput = NULL;
+ if (stderror) *stderror = NULL;
+
+ if (verbose) {
+ printf ("%s", argv[0]);
+ for (i = 1; argv[i] != NULL; ++i)
+ printf (" %s", argv[i]);
+ printf ("\n");
+ }
+
+ /* Note: abort is used in a few places along the error paths early
+ * in this function. This is because (a) cleaning up correctly is
+ * very complex at these places and (b) abort is used when a
+ * resource problems is indicated which would be due to much more
+ * serious issues - eg. memory or file descriptor leaks. We
+ * wouldn't expect fork(2) or pipe(2) to fail in normal
+ * circumstances.
+ */
+
+ if (pipe (so_fd) == -1 || pipe (se_fd) == -1) {
+ error (0, errno, "pipe");
+ abort ();
+ }
+
+ if (flag_copy_stdin) {
+ if (pipe (stdin_fd) == -1) {
+ error (0, errno, "pipe");
+ abort ();
+ }
+ }
+
+ pid = fork ();
+ if (pid == -1) {
+ error (0, errno, "fork");
+ abort ();
+ }
+
+ if (pid == 0) { /* Child process running the command. */
+ signal (SIGALRM, SIG_DFL);
+ signal (SIGPIPE, SIG_DFL);
+ close (0);
+ if (flag_copy_stdin) {
+ dup2 (stdin_fd[0], 0);
+ close (stdin_fd[0]);
+ close (stdin_fd[1]);
+ } else {
+ /* Set stdin to /dev/null (ignore failure) */
+ ignore_value (open ("/dev/null", O_RDONLY));
+ }
+ close (so_fd[0]);
+ close (se_fd[0]);
+ if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR))
+ dup2 (so_fd[1], 1);
+ else
+ dup2 (se_fd[1], 1);
+ dup2 (se_fd[1], 2);
+ close (so_fd[1]);
+ close (se_fd[1]);
+
+ execvp (argv[0], (void *) argv);
+ perror (argv[0]);
+ _exit (EXIT_FAILURE);
+ }
+
+ if (flag_copy_stdin) {
+ int fd = flags & COMMAND_FLAG_FD_MASK;
+
+ stdin_pid = fork ();
+ if (stdin_pid == -1) {
+ error (0, errno, "fork");
+ abort ();
+ }
+
+ if (stdin_pid == 0) { /* Child process copying stdin. */
+ close (so_fd[0]);
+ close (so_fd[1]);
+ close (se_fd[0]);
+ close (se_fd[1]);
+
+ close (1);
+ dup2 (stdin_fd[1], 1);
+ close (stdin_fd[0]);
+ close (stdin_fd[1]);
+
+ if (chroot (sysroot) == -1) {
+ perror ("chroot");
+ _exit (EXIT_FAILURE);
+ }
+
+ ssize_t n;
+ char buffer[BUFSIZ];
+ while ((n = read (fd, buffer, sizeof buffer)) > 0) {
+ if (xwrite (1, buffer, n) == -1)
+ /* EPIPE error indicates the command process has exited
+ * early. If the command process fails that will be caught
+ * by the daemon, and if not, then it's not an error.
+ */
+ _exit (errno == EPIPE ? EXIT_SUCCESS : EXIT_FAILURE);
+ }
+
+ if (n == -1) {
+ perror ("read");
+ _exit (EXIT_FAILURE);
+ }
+
+ if (close (fd) == -1) {
+ perror ("close");
+ _exit (EXIT_FAILURE);
+ }
+
+ _exit (EXIT_SUCCESS);
+ }
+
+ close (fd);
+ close (stdin_fd[0]);
+ close (stdin_fd[1]);
+ }
+
+ /* Parent process. */
+ close (so_fd[1]);
+ close (se_fd[1]);
+
+ FD_ZERO (&rset);
+ FD_SET (so_fd[0], &rset);
+ FD_SET (se_fd[0], &rset);
+
+ quit = 0;
+ while (quit < 2) {
+ again:
+ rset2 = rset;
+ r = select (MAX (so_fd[0], se_fd[0]) + 1, &rset2, NULL, NULL, NULL);
+ if (r == -1) {
+ if (errno == EINTR)
+ goto again;
+
+ perror ("select");
+ quit:
+ if (stdoutput) {
+ free (*stdoutput);
+ *stdoutput = NULL;
+ }
+ if (stderror) {
+ free (*stderror);
+ /* Need to return non-NULL *stderror here since most callers
+ * will try to print and then free the err string.
+ * Unfortunately recovery from strdup failure here is not
+ * possible.
+ */
+ *stderror = strdup ("error running external command, "
+ "see debug output for details");
+ }
+ close (so_fd[0]);
+ close (se_fd[0]);
+ waitpid (pid, NULL, 0);
+ if (stdin_pid >= 0) waitpid (stdin_pid, NULL, 0);
+ return -1;
+ }
+
+ if (FD_ISSET (so_fd[0], &rset2)) { /* something on stdout */
+ r = read (so_fd[0], buf, sizeof buf);
+ if (r == -1) {
+ perror ("read");
+ goto quit;
+ }
+ if (r == 0) { FD_CLR (so_fd[0], &rset); quit++; }
+
+ if (r > 0 && stdoutput) {
+ so_size += r;
+ p = realloc (*stdoutput, so_size);
+ if (p == NULL) {
+ perror ("realloc");
+ goto quit;
+ }
+ *stdoutput = p;
+ memcpy (*stdoutput + so_size - r, buf, r);
+ }
+ }
+
+ if (FD_ISSET (se_fd[0], &rset2)) { /* something on stderr */
+ r = read (se_fd[0], buf, sizeof buf);
+ if (r == -1) {
+ perror ("read");
+ goto quit;
+ }
+ if (r == 0) { FD_CLR (se_fd[0], &rset); quit++; }
+
+ if (r > 0) {
+ if (verbose)
+ ignore_value (write (2, buf, r));
+
+ if (stderror) {
+ se_size += r;
+ p = realloc (*stderror, se_size);
+ if (p == NULL) {
+ perror ("realloc");
+ goto quit;
+ }
+ *stderror = p;
+ memcpy (*stderror + se_size - r, buf, r);
+ }
+ }
+ }
+ }
+
+ close (so_fd[0]);
+ close (se_fd[0]);
+
+ /* Make sure the output buffers are \0-terminated. Also remove any
+ * trailing \n characters from the error buffer (not from stdout).
+ */
+ if (stdoutput) {
+ void *q = realloc (*stdoutput, so_size+1);
+ if (q == NULL) {
+ perror ("realloc");
+ free (*stdoutput);
+ }
+ *stdoutput = q;
+ if (*stdoutput)
+ (*stdoutput)[so_size] = '\0';
+ }
+ if (stderror) {
+ void *q = realloc (*stderror, se_size+1);
+ if (q == NULL) {
+ perror ("realloc");
+ free (*stderror);
+ }
+ *stderror = q;
+ if (*stderror) {
+ (*stderror)[se_size] = '\0';
+ se_size--;
+ while (se_size >= 0 && (*stderror)[se_size] == '\n')
+ (*stderror)[se_size--] = '\0';
+ }
+ }
+
+ if (flag_copy_stdin) {
+ /* Check copy process didn't fail. */
+ if (waitpid (stdin_pid, &r, 0) != stdin_pid) {
+ perror ("waitpid");
+ kill (pid, 9);
+ waitpid (pid, NULL, 0);
+ return -1;
+ }
+
+ if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
+ fprintf (stderr, "failed copying from input file, see earlier messages\n");
+ kill (pid, 9);
+ waitpid (pid, NULL, 0);
+ return -1;
+ }
+ }
+
+ /* Get the exit status of the command. */
+ if (waitpid (pid, &r, 0) != pid) {
+ perror ("waitpid");
+ return -1;
+ }
+
+ if (WIFEXITED (r)) {
+ return WEXITSTATUS (r);
+ } else
+ return -1;
+}
+
+/* Split an output string into a NULL-terminated list of lines.
+ * Typically this is used where we have run an external command
+ * which has printed out a list of things, and we want to return
+ * an actual list.
+ *
+ * The corner cases here are quite tricky. Note in particular:
+ *
+ * "" -> []
+ * "\n" -> [""]
+ * "a\nb" -> ["a"; "b"]
+ * "a\nb\n" -> ["a"; "b"]
+ * "a\nb\n\n" -> ["a"; "b"; ""]
+ *
+ * The original string is written over and destroyed by this
+ * function (which is usually OK because it's the 'out' string
+ * from command()). You can free the original string, because
+ * add_string() strdups the strings.
+ */
+char **
+split_lines (char *str)
+{
+ char **lines = NULL;
+ int size = 0, alloc = 0;
+ char *p, *pend;
+
+ if (STREQ (str, ""))
+ goto empty_list;
+
+ p = str;
+ while (p) {
+ /* Empty last line? */
+ if (p[0] == '\0')
+ break;
+
+ pend = strchr (p, '\n');
+ if (pend) {
+ *pend = '\0';
+ pend++;
+ }
+
+ if (add_string (&lines, &size, &alloc, p) == -1) {
+ return NULL;
+ }
+
+ p = pend;
+ }
+
+ empty_list:
+ if (add_string (&lines, &size, &alloc, NULL) == -1)
+ return NULL;
+
+ return lines;
+}
+
+/* Skip leading and trailing whitespace, updating the original string
+ * in-place.
+ */
+void
+trim (char *str)
+{
+ size_t len = strlen (str);
+
+ while (len > 0 && c_isspace (str[len-1])) {
+ str[len-1] = '\0';
+ len--;
+ }
+
+ const char *p = str;
+ while (*p && c_isspace (*p)) {
+ p++;
+ len--;
+ }
+
+ memmove (str, p, len+1);
+}
+
+/* printf helper function so we can use %Q ("quoted") and %R to print
+ * shell-quoted strings. See guestfs(3)/EXTENDING LIBGUESTFS for more
+ * details.
+ */
+static int
+print_shell_quote (FILE *stream,
+ const struct printf_info *info ATTRIBUTE_UNUSED,
+ const void *const *args)
+{
+#define SAFE(c) (c_isalnum((c)) || \
+ (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
+ int i, len;
+ const char *str = *((const char **) (args[0]));
+
+ for (i = len = 0; str[i]; ++i) {
+ if (!SAFE(str[i])) {
+ putc ('\\', stream);
+ len ++;
+ }
+ putc (str[i], stream);
+ len ++;
+ }
+
+ return len;
+}
+
+static int
+print_sysroot_shell_quote (FILE *stream,
+ const struct printf_info *info,
+ const void *const *args)
+{
+ fputs (sysroot, stream);
+ return sysroot_len + print_shell_quote (stream, info, args);
+}
+
+#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
+static int
+print_arginfo (const struct printf_info *info ATTRIBUTE_UNUSED,
+ size_t n, int *argtypes, int *size)
+{
+ if (n > 0) {
+ argtypes[0] = PA_STRING;
+ size[0] = sizeof (const char *);
+ }
+ return 1;
+}
+#else
+#ifdef HAVE_REGISTER_PRINTF_FUNCTION
+static int
+print_arginfo (const struct printf_info *info, size_t n, int *argtypes)
+{
+ if (n > 0)
+ argtypes[0] = PA_STRING;
+ return 1;
+}
+#else
+#error "HAVE_REGISTER_PRINTF_{SPECIFIER|FUNCTION} not defined"
+#endif
+#endif
+
+/* Perform device name translation. Don't call this directly -
+ * use the RESOLVE_DEVICE macro.
+ *
+ * See guestfs(3) for the algorithm.
+ *
+ * We have to open the device and test for ENXIO, because
+ * the device nodes themselves will exist in the appliance.
+ */
+int
+device_name_translation (char *device)
+{
+ int fd;
+
+ fd = open (device, O_RDONLY);
+ if (fd >= 0) {
+ close_ok:
+ close (fd);
+ return 0;
+ }
+
+ if (errno != ENXIO && errno != ENOENT)
+ return -1;
+
+ /* If the name begins with "/dev/sd" then try the alternatives. */
+ if (STRNEQLEN (device, "/dev/sd", 7))
+ return -1;
+
+ device[5] = 'h'; /* /dev/hd (old IDE driver) */
+ fd = open (device, O_RDONLY);
+ if (fd >= 0)
+ goto close_ok;
+
+ device[5] = 'v'; /* /dev/vd (for virtio devices) */
+ fd = open (device, O_RDONLY);
+ if (fd >= 0)
+ goto close_ok;
+
+ device[5] = 's'; /* Restore original device name. */