X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=daemon%2Fguestfsd.c;h=4e2933881fc971e501449a05991501cedce579b8;hp=06ad702e17b1e8007f7cc24e3ec83e562dc9749d;hb=5c31f6126ba4ea3e9056c34c300f6f5e332ab997;hpb=5922d7084d6b43f0a1a15b664c7082dfeaf584d0 diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index 06ad702..4e29338 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -1,5 +1,5 @@ /* libguestfs - the guestfsd daemon - * Copyright (C) 2009 Red Hat Inc. + * Copyright (C) 2009-2010 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 @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef HAVE_PRINTF_H # include @@ -55,9 +56,14 @@ static char *read_cmdline (void); -/* Also in guestfs.c */ -#define GUESTFWD_ADDR "10.0.2.4" -#define GUESTFWD_PORT "6666" +/* This is the default address we connect to for very old libraries + * which didn't specify the address and port number explicitly on the + * kernel command line. It's now recommended to always specify the + * address and port number on the command line, so this will not be + * used any more. + */ +#define OLD_GUESTFWD_ADDR "10.0.2.4" +#define OLD_GUESTFWD_PORT "6666" /* This is only a hint. If not defined, ignore it. */ #ifndef AI_ADDRCONFIG @@ -68,6 +74,12 @@ static char *read_cmdline (void); # define MAX(a,b) ((a)>(b)?(a):(b)) #endif +/* 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. + */ +static dev_t root_device = 0; + int verbose = 0; static int print_shell_quote (FILE *stream, const struct printf_info *info, const void *const *args); @@ -157,6 +169,10 @@ main (int argc, char *argv[]) #endif #endif + struct stat statbuf; + if (stat ("/", &statbuf) == 0) + root_device = statbuf.st_dev; + for (;;) { c = getopt_long (argc, argv, options, long_options, NULL); if (c == -1) break; @@ -285,7 +301,7 @@ main (int argc, char *argv[]) /* Default vmchannel. */ if (vmchannel == NULL) { - vmchannel = strdup ("tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT); + vmchannel = strdup ("tcp:" OLD_GUESTFWD_ADDR ":" OLD_GUESTFWD_PORT); if (!vmchannel) { perror ("strdup"); exit (EXIT_FAILURE); @@ -443,6 +459,22 @@ read_cmdline (void) return r; } +/* Return true iff device is the root device (and therefore should be + * ignored from the point of view of user calls). + */ +int +is_root_device (const char *device) +{ + struct stat statbuf; + if (stat (device, &statbuf) == -1) { + perror (device); + return 0; + } + if (statbuf.st_rdev == root_device) + return 1; + return 0; +} + /* Turn "/path" into "/sysroot/path". * * Caller must check for NULL and call reply_with_perror ("malloc") @@ -538,16 +570,23 @@ add_string (char ***argv, int *size, int *alloc, const char *str) return 0; } -int +size_t count_strings (char *const *argv) { - int argc; + size_t argc; for (argc = 0; argv[argc] != NULL; ++argc) ; return argc; } +/* http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 */ +int +is_power_of_2 (unsigned long v) +{ + return v && ((v & (v - 1)) == 0); +} + static int compare (const void *vp1, const void *vp2) { @@ -710,6 +749,14 @@ commandvf (char **stdoutput, char **stderror, int flags, * 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, @@ -717,7 +764,9 @@ commandrvf (char **stdoutput, char **stderror, int flags, { int so_size = 0, se_size = 0; int so_fd[2], se_fd[2]; - pid_t pid; + 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]; @@ -733,24 +782,43 @@ commandrvf (char **stdoutput, char **stderror, int flags, 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) { - perror ("pipe"); - return -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) { - perror ("fork"); - close (so_fd[0]); - close (so_fd[1]); - close (se_fd[0]); - close (se_fd[1]); - return -1; + error (0, errno, "fork"); + abort (); } - if (pid == 0) { /* Child process. */ + if (pid == 0) { /* Child process running the command. */ close (0); - open ("/dev/null", O_RDONLY); /* Set stdin to /dev/null (ignore failure) */ + 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) */ + open ("/dev/null", O_RDONLY); + } close (so_fd[0]); close (se_fd[0]); if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR)) @@ -763,7 +831,61 @@ commandrvf (char **stdoutput, char **stderror, int flags, execvp (argv[0], (void *) argv); perror (argv[0]); - _exit (1); + _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. */ @@ -786,6 +908,7 @@ commandrvf (char **stdoutput, char **stderror, int flags, close (so_fd[0]); close (se_fd[0]); waitpid (pid, NULL, 0); + if (stdin_pid >= 0) waitpid (stdin_pid, NULL, 0); return -1; } @@ -866,6 +989,23 @@ commandrvf (char **stdoutput, char **stderror, int flags, } } + 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");