+int
+xread (int sock, void *v_buf, size_t len)
+{
+ int r;
+ char *buf = v_buf;
+
+ while (len > 0) {
+ r = read (sock, buf, len);
+ if (r == -1) {
+ perror ("read");
+ return -1;
+ }
+ if (r == 0) {
+ fprintf (stderr, "read: unexpected end of file on fd %d\n", sock);
+ return -1;
+ }
+ buf += r;
+ len -= r;
+ }
+
+ return 0;
+}
+
+int
+add_string (char ***argv, int *size, int *alloc, const char *str)
+{
+ char **new_argv;
+ char *new_str;
+
+ if (*size >= *alloc) {
+ *alloc += 64;
+ new_argv = realloc (*argv, *alloc * sizeof (char *));
+ if (new_argv == NULL) {
+ reply_with_perror ("realloc");
+ free_strings (*argv);
+ return -1;
+ }
+ *argv = new_argv;
+ }
+
+ if (str) {
+ new_str = strdup (str);
+ if (new_str == NULL) {
+ reply_with_perror ("strdup");
+ free_strings (*argv);
+ }
+ } else
+ new_str = NULL;
+
+ (*argv)[*size] = new_str;
+
+ (*size)++;
+ return 0;
+}
+
+int
+count_strings (char *const *argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ ;
+ return argc;
+}
+
+static int
+compare (const void *vp1, const void *vp2)
+{
+ char * const *p1 = (char * const *) vp1;
+ char * const *p2 = (char * const *) vp2;
+ return strcmp (*p1, *p2);
+}
+
+void
+sort_strings (char **argv, int len)
+{
+ qsort (argv, len, sizeof (char *), compare);
+}
+
+void
+free_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ free (argv[argc]);
+ free (argv);
+}
+
+void
+free_stringslen (char **argv, int len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i)
+ free (argv[i]);
+ free (argv);
+}
+
+/* Easy ways to run external commands. For full documentation, see
+ * 'commandrvf' below.
+ */
+int
+commandf (char **stdoutput, char **stderror, int flags, const char *name, ...)
+{
+ va_list args;
+ const char **argv;
+ char *s;
+ int i, r;
+
+ /* Collect the command line arguments into an array. */
+ i = 2;
+ argv = malloc (sizeof (char *) * i);
+ if (argv == NULL) {
+ perror ("malloc");
+ return -1;
+ }
+ argv[0] = (char *) name;
+ argv[1] = NULL;
+
+ va_start (args, name);
+
+ while ((s = va_arg (args, char *)) != NULL) {
+ const char **p = realloc (argv, sizeof (char *) * (++i));
+ if (p == NULL) {
+ perror ("realloc");
+ free (argv);
+ va_end (args);
+ return -1;
+ }
+ argv = p;
+ argv[i-2] = s;
+ argv[i-1] = NULL;
+ }
+
+ va_end (args);
+
+ r = commandvf (stdoutput, stderror, flags, (const char * const*) argv);
+
+ /* NB: Mustn't free the strings which are on the stack. */
+ free (argv);
+
+ return r;
+}
+
+/* Same as 'command', but we allow the status code from the
+ * subcommand to be non-zero, and return that status code.
+ * We still return -1 if there was some other error.
+ */
+int
+commandrf (char **stdoutput, char **stderror, int flags, const char *name, ...)
+{
+ va_list args;
+ const char **argv;
+ char *s;
+ int i, r;
+
+ /* Collect the command line arguments into an array. */
+ i = 2;
+ argv = malloc (sizeof (char *) * i);
+ if (argv == NULL) {
+ perror ("malloc");
+ return -1;
+ }
+ argv[0] = (char *) name;
+ argv[1] = NULL;
+
+ va_start (args, name);
+
+ while ((s = va_arg (args, char *)) != NULL) {
+ const char **p = realloc (argv, sizeof (char *) * (++i));
+ if (p == NULL) {
+ perror ("realloc");
+ free (argv);
+ va_end (args);
+ return -1;
+ }
+ argv = p;
+ argv[i-2] = s;
+ argv[i-1] = NULL;
+ }
+
+ va_end (args);
+
+ r = commandrvf (stdoutput, stderror, flags, argv);
+
+ /* NB: Mustn't free the strings which are on the stack. */
+ free (argv);
+
+ return r;
+}
+
+/* Same as 'command', but passing an argv. */
+int
+commandvf (char **stdoutput, char **stderror, int flags,
+ char const *const *argv)
+{
+ int r;
+
+ r = commandrvf (stdoutput, stderror, flags, (void *) argv);
+ if (r == 0)
+ return 0;
+ else
+ return -1;
+}
+
+/* This is a more sane version of 'system(3)' for running external
+ * commands. It uses fork/execvp, so we don't need to worry about
+ * quoting of parameters, and it allows us to capture any error
+ * messages in a buffer.
+ *
+ * If stdoutput is not NULL, then *stdoutput will return the stdout
+ * of the command.
+ *
+ * If stderror is not NULL, then *stderror will return the stderr
+ * of the command. If there is a final \n character, it is removed
+ * so you can use the error string directly in a call to
+ * reply_with_error.
+ *
+ * Flags:
+ *
+ * COMMAND_FLAG_FOLD_STDOUT_ON_STDERR: For broken external commands
+ * that send error messages to stdout (hello, parted) but that don't
+ * have any useful stdout information, use this flag to capture the
+ * error messages in the *stderror buffer. If using this flag,
+ * you should pass stdoutput as NULL because nothing could ever be
+ * captured in that buffer.
+ */
+int
+commandrvf (char **stdoutput, char **stderror, int flags,
+ char const* const *argv)
+{
+ int so_size = 0, se_size = 0;
+ int so_fd[2], se_fd[2];
+ pid_t pid;
+ 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");
+ }
+
+ if (pipe (so_fd) == -1 || pipe (se_fd) == -1) {
+ perror ("pipe");
+ return -1;
+ }
+
+ pid = fork ();
+ if (pid == -1) {
+ perror ("fork");
+ close (so_fd[0]);
+ close (so_fd[1]);
+ close (se_fd[0]);
+ close (se_fd[1]);
+ return -1;
+ }
+
+ if (pid == 0) { /* Child process. */
+ close (0);
+ open ("/dev/null", O_RDONLY); /* Set stdin to /dev/null (ignore failure) */
+ 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 (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) {
+ rset2 = rset;
+ r = select (MAX (so_fd[0], se_fd[0]) + 1, &rset2, NULL, NULL, NULL);
+ if (r == -1) {
+ perror ("select");
+ quit:
+ if (stdoutput) free (*stdoutput);
+ if (stderror) free (*stderror);
+ close (so_fd[0]);
+ close (se_fd[0]);
+ waitpid (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';
+ }
+ }
+
+ /* 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 HACKING file 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. */
+ return -1;
+}
+
+/* Check program exists and is executable on $PATH. Actually, we
+ * just assume PATH contains the default entries (see main() above).
+ */
+int
+prog_exists (const char *prog)
+{
+ static const char * const dirs[] =
+ { "/sbin", "/usr/sbin", "/bin", "/usr/bin" };
+ size_t i;
+ char buf[1024];
+
+ for (i = 0; i < sizeof dirs / sizeof dirs[0]; ++i) {
+ snprintf (buf, sizeof buf, "%s/%s", dirs[i], prog);
+ if (access (buf, X_OK) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/* LVM and other commands aren't synchronous, especially when udev is
+ * involved. eg. You can create or remove some device, but the /dev
+ * device node won't appear until some time later. This means that
+ * you get an error if you run one command followed by another.
+ *
+ * Use 'udevadm settle' after certain commands, but don't be too
+ * fussed if it fails.
+ *
+ * 'udevsettle' was the old name for this command (RHEL 5). This was
+ * deprecated in favour of 'udevadm settle'. The old 'udevsettle'
+ * command was left as a symlink. Then in Fedora 13 the old symlink
+ * remained but it stopped working (RHBZ#548121), so we have to be
+ * careful not to assume that we can use 'udevsettle' if it exists.
+ */
+void
+udev_settle (void)