X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=daemon%2Fguestfsd.c;h=e398b7d8348410fad052a94ebe27bde014e4b0a9;hp=be793002317a7f1a4cd8d0ffaf1dbb3e14902eda;hb=d600342b7d29c0176ff96a7807ebb38303ecb3a6;hpb=34067b5c362d5070e91364cc1ed6487497462b42 diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index be79300..e398b7d 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef HAVE_PRINTF_H # include @@ -55,24 +56,21 @@ static char *read_cmdline (void); -/* 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 -# define AI_ADDRCONFIG 0 -#endif - #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) #endif +/* Not the end of the world if this open flag is not defined. */ +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 +#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); @@ -127,15 +125,14 @@ static void usage (void) { fprintf (stderr, - "guestfsd [-f|--foreground] [-c|--channel vmchannel] [-v|--verbose]\n"); + "guestfsd [-f|--foreground] [-v|--verbose]\n"); } int main (int argc, char *argv[]) { - static const char *options = "fc:v?"; + static const char *options = "fv?"; static const struct option long_options[] = { - { "channel", required_argument, 0, 'c' }, { "foreground", 0, 0, 'f' }, { "help", 0, 0, '?' }, { "verbose", 0, 0, 'v' }, @@ -144,7 +141,6 @@ main (int argc, char *argv[]) int c; int dont_fork = 0; char *cmdline; - char *vmchannel = NULL; if (winsock_init () == -1) error (EXIT_FAILURE, 0, "winsock initialization failed"); @@ -162,15 +158,15 @@ 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; switch (c) { - case 'c': - vmchannel = optarg; - break; - case 'f': dont_fork = 1; break; @@ -245,118 +241,13 @@ main (int argc, char *argv[]) _umask (0); #endif - /* Get the vmchannel string. - * - * Sources: - * --channel/-c option on the command line - * guestfs_vmchannel=... from the kernel command line - * guestfs=... from the kernel command line - * built-in default - * - * At the moment we expect this to contain "tcp:ip:port" but in - * future it might contain a device name, eg. "/dev/vcon4" for - * virtio-console vmchannel. - */ - if (vmchannel == NULL && cmdline) { - char *p; - size_t len; - - p = strstr (cmdline, "guestfs_vmchannel="); - if (p) { - len = strcspn (p + 18, " \t\n"); - vmchannel = strndup (p + 18, len); - if (!vmchannel) { - perror ("strndup"); - exit (EXIT_FAILURE); - } - } - - /* Old libraries passed guestfs=host:port. Rewrite it as tcp:host:port. */ - if (vmchannel == NULL) { - /* We will rewrite it part of the "guestfs=" string with - * "tcp:" hence p + 4 below. */ - p = strstr (cmdline, "guestfs="); - if (p) { - len = strcspn (p + 4, " \t\n"); - vmchannel = strndup (p + 4, len); - if (!vmchannel) { - perror ("strndup"); - exit (EXIT_FAILURE); - } - memcpy (vmchannel, "tcp:", 4); - } - } - } - - /* Default vmchannel. */ - if (vmchannel == NULL) { - vmchannel = strdup ("tcp:" OLD_GUESTFWD_ADDR ":" OLD_GUESTFWD_PORT); - if (!vmchannel) { - perror ("strdup"); - exit (EXIT_FAILURE); - } - } - - if (verbose) - printf ("vmchannel: %s\n", vmchannel); - - /* Connect to vmchannel. */ - int sock = -1; - - if (STREQLEN (vmchannel, "tcp:", 4)) { - /* Resolve the hostname. */ - struct addrinfo *res, *rr; - struct addrinfo hints; - int r; - char *host, *port; - - host = vmchannel+4; - port = strchr (host, ':'); - if (port) { - port[0] = '\0'; - port++; - } else { - fprintf (stderr, "vmchannel: expecting \"tcp::\": %s\n", - vmchannel); - exit (EXIT_FAILURE); - } - - memset (&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - r = getaddrinfo (host, port, &hints, &res); - if (r != 0) { - fprintf (stderr, "%s:%s: %s\n", - host, port, gai_strerror (r)); - exit (EXIT_FAILURE); - } - - /* Connect to the given TCP socket. */ - for (rr = res; rr != NULL; rr = rr->ai_next) { - sock = socket (rr->ai_family, rr->ai_socktype, rr->ai_protocol); - if (sock != -1) { - if (connect (sock, rr->ai_addr, rr->ai_addrlen) == 0) - break; - perror ("connect"); - - close (sock); - sock = -1; - } - } - freeaddrinfo (res); - } else { - fprintf (stderr, - "unknown vmchannel connection type: %s\n" - "expecting \"tcp::\"\n", - vmchannel); - exit (EXIT_FAILURE); - } - + /* Connect to virtio-serial channel. */ + int sock = open ("/dev/virtio-ports/org.libguestfs.channel.0", + O_RDWR | O_CLOEXEC); if (sock == -1) { fprintf (stderr, "\n" - "Failed to connect to any vmchannel implementation.\n" - "vmchannel: %s\n" + "Failed to connect to virtio-serial channel.\n" "\n" "This is a fatal error and the appliance will now exit.\n" "\n" @@ -366,8 +257,7 @@ main (int argc, char *argv[]) "'libguestfs-test-tool' and provide the complete, unedited\n" "output to the libguestfs developers, either in a bug report\n" "or on the libguestfs redhat com mailing list.\n" - "\n", - vmchannel); + "\n"); exit (EXIT_FAILURE); } @@ -448,6 +338,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") @@ -543,16 +449,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) { @@ -715,6 +628,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, @@ -722,7 +643,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]; @@ -748,19 +671,33 @@ commandrvf (char **stdoutput, char **stderror, int flags, */ if (pipe (so_fd) == -1 || pipe (se_fd) == -1) { - perror ("pipe"); + 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"); + 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)) @@ -773,7 +710,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. */ @@ -796,6 +787,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; } @@ -876,6 +868,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");