Allows more sharing between the daemon and the inspection program.
---
daemon/Makefile.am | 2 +
daemon/cleanups.c | 80 +++++++++++
daemon/cleanups.h | 47 +++++++
daemon/command.c | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++++
daemon/command.h | 40 ++++++
daemon/daemon.h | 46 +------
daemon/guestfsd.c | 342 -----------------------------------------------
po/POTFILES | 2 +
8 files changed, 557 insertions(+), 385 deletions(-)
create mode 100644 daemon/cleanups.c
create mode 100644 daemon/cleanups.h
create mode 100644 daemon/command.c
create mode 100644 daemon/command.h
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index c1a33e3..a4e3df7 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -96,6 +96,8 @@ guestfsd_SOURCES = \
cap.c \
checksum.c \
cmp.c \
+ command.c \
+ command.h \
compress.c \
copy.c \
cpio.c \
diff --git a/daemon/cleanups.c b/daemon/cleanups.c
new file mode 100644
index 0000000..5293ff2
--- /dev/null
+++ b/daemon/cleanups.c
@@ -0,0 +1,80 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <augeas.h>
+
+#include "cleanups.h"
+
+/* Use by the CLEANUP_* macros. Do not call these directly. */
+void
+cleanup_free (void *ptr)
+{
+ free (* (void **) ptr);
+}
+
+extern void free_strings (char **argv);
+
+void
+cleanup_free_string_list (void *ptr)
+{
+ free_strings (* (char ***) ptr);
+}
+
+void
+cleanup_unlink_free (void *ptr)
+{
+ char *filename = * (char **) ptr;
+
+ if (filename) {
+ unlink (filename);
+ free (filename);
+ }
+}
+
+void
+cleanup_close (void *ptr)
+{
+ int fd = * (int *) ptr;
+
+ if (fd >= 0)
+ close (fd);
+}
+
+void
+cleanup_aug_close (void *ptr)
+{
+ augeas *aug = * (augeas **) ptr;
+
+ if (aug != NULL)
+ aug_close (aug);
+}
+
+struct stringsbuf;
+extern void free_stringsbuf (struct stringsbuf *sb);
+
+void
+cleanup_free_stringsbuf (void *ptr)
+{
+ free_stringsbuf ((struct stringsbuf *) ptr);
+}
diff --git a/daemon/cleanups.h b/daemon/cleanups.h
new file mode 100644
index 0000000..b6ef2ff
--- /dev/null
+++ b/daemon/cleanups.h
@@ -0,0 +1,47 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GUESTFSD_CLEANUPS_H
+#define GUESTFSD_CLEANUPS_H
+
+/* Use by the CLEANUP_* macros. */
+extern void cleanup_free (void *ptr);
+extern void cleanup_free_string_list (void *ptr);
+extern void cleanup_unlink_free (void *ptr);
+extern void cleanup_close (void *ptr);
+extern void cleanup_aug_close (void *ptr);
+extern void cleanup_free_stringsbuf (void *ptr);
+
+#ifdef HAVE_ATTRIBUTE_CLEANUP
+#define CLEANUP_FREE __attribute__((cleanup(cleanup_free)))
+#define CLEANUP_FREE_STRING_LIST \
+ __attribute__((cleanup(cleanup_free_string_list)))
+#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free)))
+#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close)))
+#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close)))
+#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf)))
+#else
+#define CLEANUP_FREE
+#define CLEANUP_FREE_STRING_LIST
+#define CLEANUP_UNLINK_FREE
+#define CLEANUP_CLOSE
+#define CLEANUP_AUG_CLOSE
+#define CLEANUP_FREE_STRINGSBUF
+#endif
+
+#endif /* GUESTFSD_CLEANUPS_H */
diff --git a/daemon/command.c b/daemon/command.c
new file mode 100644
index 0000000..fd46175
--- /dev/null
+++ b/daemon/command.c
@@ -0,0 +1,383 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <error.h>
+#include <errno.h>
+
+#include "ignore-value.h"
+
+#include "command.h"
+#include "cleanups.h"
+
+extern int verbose;
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+/* For improved readability dealing with pipe arrays */
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+/* 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;
+ /* NB: Mustn't free the strings which are on the stack. */
+ CLEANUP_FREE const char **argv = NULL;
+ char *s;
+ size_t i;
+ int 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");
+ 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);
+
+ 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;
+ CLEANUP_FREE const char **argv = NULL;
+ 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");
+ va_end (args);
+ return -1;
+ }
+ argv = p;
+ argv[i-2] = s;
+ argv[i-1] = NULL;
+ }
+
+ va_end (args);
+
+ r = commandrvf (stdoutput, stderror, flags, 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.
+ *
+ * COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN: For running external
+ * commands on chrooted files correctly (see RHBZ#579608) specifying
+ * this flag causes another process to be forked which chroots into
+ * sysroot and just copies the input file to stdin of the specified
+ * command. The file descriptor is ORed with the flags, and that file
+ * descriptor is always closed by this function. See hexdump.c for an
+ * example of usage.
+ */
+int
+commandrvf (char **stdoutput, char **stderror, int flags,
+ char const* const *argv)
+{
+ size_t 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 flag_copy_fd = flags & COMMAND_FLAG_FD_MASK;
+ 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");
+ }
+
+ /* 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 ();
+ }
+
+ 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 (flag_copy_fd, STDIN_FILENO);
+ } else {
+ /* Set stdin to /dev/null (ignore failure) */
+ ignore_value (open ("/dev/null", O_RDONLY|O_CLOEXEC));
+ }
+ close (so_fd[PIPE_READ]);
+ close (se_fd[PIPE_READ]);
+ if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR))
+ dup2 (so_fd[PIPE_WRITE], STDOUT_FILENO);
+ else
+ dup2 (se_fd[PIPE_WRITE], STDOUT_FILENO);
+ dup2 (se_fd[PIPE_WRITE], STDERR_FILENO);
+ close (so_fd[PIPE_WRITE]);
+ close (se_fd[PIPE_WRITE]);
+
+ ignore_value (chdir ("/"));
+
+ execvp (argv[0], (void *) argv);
+ perror (argv[0]);
+ _exit (EXIT_FAILURE);
+ }
+
+ /* Parent process. */
+ close (so_fd[PIPE_WRITE]);
+ close (se_fd[PIPE_WRITE]);
+
+ FD_ZERO (&rset);
+ FD_SET (so_fd[PIPE_READ], &rset);
+ FD_SET (se_fd[PIPE_READ], &rset);
+
+ quit = 0;
+ while (quit < 2) {
+ again:
+ rset2 = rset;
+ r = select (MAX (so_fd[PIPE_READ], se_fd[PIPE_READ]) + 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[PIPE_READ]);
+ close (se_fd[PIPE_READ]);
+ if (flag_copy_stdin) close (flag_copy_fd);
+ waitpid (pid, NULL, 0);
+ return -1;
+ }
+
+ if (FD_ISSET (so_fd[PIPE_READ], &rset2)) { /* something on stdout */
+ r = read (so_fd[PIPE_READ], buf, sizeof buf);
+ if (r == -1) {
+ perror ("read");
+ goto quit;
+ }
+ if (r == 0) { FD_CLR (so_fd[PIPE_READ], &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[PIPE_READ], &rset2)) { /* something on stderr */
+ r = read (se_fd[PIPE_READ], buf, sizeof buf);
+ if (r == -1) {
+ perror ("read");
+ goto quit;
+ }
+ if (r == 0) { FD_CLR (se_fd[PIPE_READ], &rset); quit++; }
+
+ if (r > 0) {
+ if (verbose)
+ ignore_value (write (STDERR_FILENO, 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[PIPE_READ]);
+ close (se_fd[PIPE_READ]);
+
+ /* 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';
+ while (se_size > 0 && (*stderror)[se_size-1] == '\n') {
+ se_size--;
+ (*stderror)[se_size] = '\0';
+ }
+ }
+ }
+
+ if (flag_copy_stdin && close (flag_copy_fd) == -1) {
+ perror ("close");
+ 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;
+}
diff --git a/daemon/command.h b/daemon/command.h
new file mode 100644
index 0000000..e935ae6
--- /dev/null
+++ b/daemon/command.h
@@ -0,0 +1,40 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GUESTFSD_COMMAND_H
+#define GUESTFSD_COMMAND_H
+
+#define command(out,err,name,...) commandf((out),(err),0,(name),__VA_ARGS__)
+#define commandr(out,err,name,...) commandrf((out),(err),0,(name),__VA_ARGS__)
+#define commandv(out,err,argv) commandvf((out),(err),0,(argv))
+#define commandrv(out,err,argv) commandrvf((out),(err),0,(argv))
+
+#define COMMAND_FLAG_FD_MASK (1024-1)
+#define COMMAND_FLAG_FOLD_STDOUT_ON_STDERR 1024
+#define COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN 2048
+
+extern int commandf (char **stdoutput, char **stderror, int flags,
+ const char *name, ...) __attribute__((sentinel));
+extern int commandrf (char **stdoutput, char **stderror, int flags,
+ const char *name, ...) __attribute__((sentinel));
+extern int commandvf (char **stdoutput, char **stderror, int flags,
+ char const *const *argv);
+extern int commandrvf (char **stdoutput, char **stderror, int flags,
+ char const* const *argv);
+
+#endif /* GUESTFSD_COMMAND_H */
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 7fbb2a2..7e454da 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -32,6 +32,9 @@
#include "guestfs-internal-all.h"
+#include "cleanups.h"
+#include "command.h"
+
/* Mountables */
typedef struct {
@@ -117,27 +120,9 @@ extern char **split_lines (char *str);
extern char **empty_list (void);
-#define command(out,err,name,...) commandf((out),(err),0,(name),__VA_ARGS__)
-#define commandr(out,err,name,...) commandrf((out),(err),0,(name),__VA_ARGS__)
-#define commandv(out,err,argv) commandvf((out),(err),0,(argv))
-#define commandrv(out,err,argv) commandrvf((out),(err),0,(argv))
-
#define __external_command __attribute__((__section__(".guestfsd_ext_cmds")))
#define GUESTFSD_EXT_CMD(___ext_cmd_var, ___ext_cmd_str) static const char
___ext_cmd_var[] __external_command = #___ext_cmd_str
-#define COMMAND_FLAG_FD_MASK (1024-1)
-#define COMMAND_FLAG_FOLD_STDOUT_ON_STDERR 1024
-#define COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN 2048
-
-extern int commandf (char **stdoutput, char **stderror, int flags,
- const char *name, ...) __attribute__((sentinel));
-extern int commandrf (char **stdoutput, char **stderror, int flags,
- const char *name, ...) __attribute__((sentinel));
-extern int commandvf (char **stdoutput, char **stderror, int flags,
- char const *const *argv);
-extern int commandrvf (char **stdoutput, char **stderror, int flags,
- char const* const *argv);
-
extern int is_power_of_2 (unsigned long v);
extern void trim (char *str);
@@ -156,14 +141,6 @@ extern char *get_random_uuid (void);
extern int asprintf_nowarn (char **strp, const char *fmt, ...);
-/* Use by the CLEANUP_* macros. */
-extern void cleanup_free (void *ptr);
-extern void cleanup_free_string_list (void *ptr);
-extern void cleanup_unlink_free (void *ptr);
-extern void cleanup_close (void *ptr);
-extern void cleanup_aug_close (void *ptr);
-extern void cleanup_free_stringsbuf (void *ptr);
-
/*-- in names.c (auto-generated) --*/
extern const char *function_names[];
@@ -451,21 +428,4 @@ is_zero (const char *buffer, size_t size)
} \
} while (0)
-#ifdef HAVE_ATTRIBUTE_CLEANUP
-#define CLEANUP_FREE __attribute__((cleanup(cleanup_free)))
-#define CLEANUP_FREE_STRING_LIST \
- __attribute__((cleanup(cleanup_free_string_list)))
-#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free)))
-#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close)))
-#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close)))
-#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf)))
-#else
-#define CLEANUP_FREE
-#define CLEANUP_FREE_STRING_LIST
-#define CLEANUP_UNLINK_FREE
-#define CLEANUP_CLOSE
-#define CLEANUP_AUG_CLOSE
-#define CLEANUP_FREE_STRINGSBUF
-#endif
-
#endif /* GUESTFSD_DAEMON_H */
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index 0a29aa6..a3e8868 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -68,10 +68,6 @@ GUESTFSD_EXT_CMD(str_uuidgen, uuidgen);
# define O_CLOEXEC 0
#endif
-/* For improved readability dealing with pipe arrays */
-#define PIPE_READ 0
-#define PIPE_WRITE 1
-
/* If root device is an ext2 filesystem, this is the major and minor.
* This is so we can ignore this device from the point of view of the
* user, eg. in guestfs_list_devices and many other places.
@@ -752,344 +748,6 @@ join_strings (const char *separator, char *const *argv)
return r;
}
-/* 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;
- /* NB: Mustn't free the strings which are on the stack. */
- CLEANUP_FREE const char **argv = NULL;
- char *s;
- size_t i;
- int 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");
- 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);
-
- 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;
- CLEANUP_FREE const char **argv = NULL;
- 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");
- va_end (args);
- return -1;
- }
- argv = p;
- argv[i-2] = s;
- argv[i-1] = NULL;
- }
-
- va_end (args);
-
- r = commandrvf (stdoutput, stderror, flags, 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.
- *
- * COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN: For running external
- * commands on chrooted files correctly (see RHBZ#579608) specifying
- * this flag causes another process to be forked which chroots into
- * sysroot and just copies the input file to stdin of the specified
- * command. The file descriptor is ORed with the flags, and that file
- * descriptor is always closed by this function. See hexdump.c for an
- * example of usage.
- */
-int
-commandrvf (char **stdoutput, char **stderror, int flags,
- char const* const *argv)
-{
- size_t 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 flag_copy_fd = flags & COMMAND_FLAG_FD_MASK;
- 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");
- }
-
- /* 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 ();
- }
-
- 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 (flag_copy_fd, STDIN_FILENO);
- } else {
- /* Set stdin to /dev/null (ignore failure) */
- ignore_value (open ("/dev/null", O_RDONLY|O_CLOEXEC));
- }
- close (so_fd[PIPE_READ]);
- close (se_fd[PIPE_READ]);
- if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR))
- dup2 (so_fd[PIPE_WRITE], STDOUT_FILENO);
- else
- dup2 (se_fd[PIPE_WRITE], STDOUT_FILENO);
- dup2 (se_fd[PIPE_WRITE], STDERR_FILENO);
- close (so_fd[PIPE_WRITE]);
- close (se_fd[PIPE_WRITE]);
-
- ignore_value (chdir ("/"));
-
- execvp (argv[0], (void *) argv);
- perror (argv[0]);
- _exit (EXIT_FAILURE);
- }
-
- /* Parent process. */
- close (so_fd[PIPE_WRITE]);
- close (se_fd[PIPE_WRITE]);
-
- FD_ZERO (&rset);
- FD_SET (so_fd[PIPE_READ], &rset);
- FD_SET (se_fd[PIPE_READ], &rset);
-
- quit = 0;
- while (quit < 2) {
- again:
- rset2 = rset;
- r = select (MAX (so_fd[PIPE_READ], se_fd[PIPE_READ]) + 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[PIPE_READ]);
- close (se_fd[PIPE_READ]);
- if (flag_copy_stdin) close (flag_copy_fd);
- waitpid (pid, NULL, 0);
- return -1;
- }
-
- if (FD_ISSET (so_fd[PIPE_READ], &rset2)) { /* something on stdout */
- r = read (so_fd[PIPE_READ], buf, sizeof buf);
- if (r == -1) {
- perror ("read");
- goto quit;
- }
- if (r == 0) { FD_CLR (so_fd[PIPE_READ], &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[PIPE_READ], &rset2)) { /* something on stderr */
- r = read (se_fd[PIPE_READ], buf, sizeof buf);
- if (r == -1) {
- perror ("read");
- goto quit;
- }
- if (r == 0) { FD_CLR (se_fd[PIPE_READ], &rset); quit++; }
-
- if (r > 0) {
- if (verbose)
- ignore_value (write (STDERR_FILENO, 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[PIPE_READ]);
- close (se_fd[PIPE_READ]);
-
- /* 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';
- while (se_size > 0 && (*stderror)[se_size-1] == '\n') {
- se_size--;
- (*stderror)[se_size] = '\0';
- }
- }
- }
-
- if (flag_copy_stdin && close (flag_copy_fd) == -1) {
- perror ("close");
- 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,
* wrapped into a stringsbuf.
* Typically this is used where we have run an external command
diff --git a/po/POTFILES b/po/POTFILES
index 9719afd..aca174d 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -25,7 +25,9 @@ daemon/blockdev.c
daemon/btrfs.c
daemon/cap.c
daemon/checksum.c
+daemon/cleanups.c
daemon/cmp.c
+daemon/command.c
daemon/compress.c
daemon/copy.c
daemon/cpio.c
--
2.5.0