X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=daemon%2Fguestfsd.c;h=03a975a7553ccff549a0b709a7f2acbcaa0e68a4;hp=db0bff926de99cb89d4003e6c5ed3c9a9917aa42;hb=8a9f2ca65521d093ac14307aca4370d9497ac840;hpb=9a8889e4d0c532b9f77af3a9cc7aae06adebfb83 diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index db0bff9..03a975a 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -20,6 +20,10 @@ #define _BSD_SOURCE /* for daemon(3) */ +#ifdef HAVE_WINDOWS_H +# include +#endif + #include #include #include @@ -27,26 +31,47 @@ #include #include #include -#include #include -#include #include -#include #include #include #include -#include +#include +#include +#include +#include +#include +#ifdef HAVE_PRINTF_H +# include +#endif + +#include "sockets.h" #include "c-ctype.h" #include "ignore-value.h" +#include "error.h" #include "daemon.h" 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 +# define AI_ADDRCONFIG 0 +#endif + +#ifndef MAX +# define MAX(a,b) ((a)>(b)?(a):(b)) +#endif int verbose = 0; @@ -62,10 +87,42 @@ static int print_arginfo (const struct printf_info *info, size_t n, int *argtype #endif #endif +#ifdef WIN32 +static int +daemon (int nochdir, int noclose) +{ + fprintf (stderr, + "On Windows the daemon does not support forking into the " + "background.\nYou *must* run the daemon with the -f option.\n"); + exit (EXIT_FAILURE); +} +#endif /* WIN32 */ + +#ifdef WIN32 +static int +winsock_init (void) +{ + int r; + + /* http://msdn2.microsoft.com/en-us/library/ms742213.aspx */ + r = gl_sockets_startup (SOCKETS_2_2); + return r == 0 ? 0 : -1; +} +#else /* !WIN32 */ +static int +winsock_init (void) +{ + return 0; +} +#endif /* !WIN32 */ + /* Location to mount root device. */ const char *sysroot = "/sysroot"; /* No trailing slash. */ int sysroot_len = 8; +/* Not used explicitly, but required by the gnulib 'error' module. */ +const char *program_name = "guestfsd"; + static void usage (void) { @@ -89,6 +146,9 @@ main (int argc, char *argv[]) char *cmdline; char *vmchannel = NULL; + if (winsock_init () == -1) + error (EXIT_FAILURE, 0, "winsock initialization failed"); + #ifdef HAVE_REGISTER_PRINTF_SPECIFIER /* http://udrepper.livejournal.com/20948.html */ register_printf_specifier ('Q', print_shell_quote, print_arginfo); @@ -121,17 +181,17 @@ main (int argc, char *argv[]) case '?': usage (); - exit (0); + exit (EXIT_SUCCESS); default: fprintf (stderr, "guestfsd: unexpected command line option 0x%x\n", c); - exit (1); + exit (EXIT_FAILURE); } } if (optind < argc) { usage (); - exit (1); + exit (EXIT_FAILURE); } cmdline = read_cmdline (); @@ -149,6 +209,7 @@ main (int argc, char *argv[]) printf ("could not read linux command line\n"); } +#ifndef WIN32 /* Make sure SIGPIPE doesn't kill us. */ struct sigaction sa; memset (&sa, 0, sizeof sa); @@ -156,17 +217,33 @@ main (int argc, char *argv[]) sa.sa_flags = 0; if (sigaction (SIGPIPE, &sa, NULL) == -1) perror ("sigaction SIGPIPE"); /* but try to continue anyway ... */ +#endif +#ifdef WIN32 +# define setenv(n,v,f) _putenv(n "=" v) +#endif /* Set up a basic environment. After we are called by /init the * environment is essentially empty. * https://bugzilla.redhat.com/show_bug.cgi?id=502074#c5 + * + * NOTE: if you change $PATH, you must also change 'prog_exists' + * function below. */ - setenv ("PATH", "/usr/bin:/bin", 1); + setenv ("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1); setenv ("SHELL", "/bin/sh", 1); setenv ("LC_ALL", "C", 1); + setenv ("TERM", "dumb", 1); +#ifndef WIN32 /* We document that umask defaults to 022 (it should be this anyway). */ umask (022); +#else + /* This is the default for Windows anyway. It's not even clear if + * Windows ever uses this -- the MSDN documentation for the function + * contains obvious errors. + */ + _umask (0); +#endif /* Get the vmchannel string. * @@ -190,7 +267,7 @@ main (int argc, char *argv[]) vmchannel = strndup (p + 18, len); if (!vmchannel) { perror ("strndup"); - exit (1); + exit (EXIT_FAILURE); } } @@ -204,7 +281,7 @@ main (int argc, char *argv[]) vmchannel = strndup (p + 4, len); if (!vmchannel) { perror ("strndup"); - exit (1); + exit (EXIT_FAILURE); } memcpy (vmchannel, "tcp:", 4); } @@ -213,10 +290,10 @@ 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 (1); + exit (EXIT_FAILURE); } } @@ -241,7 +318,7 @@ main (int argc, char *argv[]) } else { fprintf (stderr, "vmchannel: expecting \"tcp::\": %s\n", vmchannel); - exit (1); + exit (EXIT_FAILURE); } memset (&hints, 0, sizeof hints); @@ -251,7 +328,7 @@ main (int argc, char *argv[]) if (r != 0) { fprintf (stderr, "%s:%s: %s\n", host, port, gai_strerror (r)); - exit (1); + exit (EXIT_FAILURE); } /* Connect to the given TCP socket. */ @@ -272,7 +349,7 @@ main (int argc, char *argv[]) "unknown vmchannel connection type: %s\n" "expecting \"tcp::\"\n", vmchannel); - exit (1); + exit (EXIT_FAILURE); } if (sock == -1) { @@ -291,7 +368,7 @@ main (int argc, char *argv[]) "or on the libguestfs redhat com mailing list.\n" "\n", vmchannel); - exit (1); + exit (EXIT_FAILURE); } /* Send the magic length message which indicates that @@ -301,10 +378,10 @@ main (int argc, char *argv[]) XDR xdr; uint32_t len = GUESTFS_LAUNCH_FLAG; xdrmem_create (&xdr, lenbuf, sizeof lenbuf, XDR_ENCODE); - xdr_uint32_t (&xdr, &len); + xdr_u_int (&xdr, &len); if (xwrite (sock, lenbuf, sizeof lenbuf) == -1) - exit (1); + exit (EXIT_FAILURE); xdr_destroy (&xdr); @@ -312,14 +389,14 @@ main (int argc, char *argv[]) if (!dont_fork) { if (daemon (0, 1) == -1) { perror ("daemon"); - exit (1); + exit (EXIT_FAILURE); } } /* Enter the main loop, reading and performing actions. */ main_loop (sock); - exit (0); + exit (EXIT_SUCCESS); } /* Read /proc/cmdline. */ @@ -860,6 +937,28 @@ split_lines (char *str) 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. */ @@ -928,53 +1027,74 @@ print_arginfo (const struct printf_info *info, size_t n, int *argtypes) * the device nodes themselves will exist in the appliance. */ int -device_name_translation (char *device, const char *func) +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) { - error: - reply_with_perror ("%s: %s", func, device); + if (errno != ENXIO && errno != ENOENT) return -1; - } /* If the name begins with "/dev/sd" then try the alternatives. */ if (STRNEQLEN (device, "/dev/sd", 7)) - goto error; + return -1; device[5] = 'h'; /* /dev/hd (old IDE driver) */ fd = open (device, O_RDONLY); - if (fd >= 0) { - close (fd); - return 0; - } + if (fd >= 0) + goto close_ok; device[5] = 'v'; /* /dev/vd (for virtio devices) */ fd = open (device, O_RDONLY); - if (fd >= 0) { - close (fd); - return 0; - } + if (fd >= 0) + goto close_ok; device[5] = 's'; /* Restore original device name. */ - goto error; + 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) { - command (NULL, NULL, "/sbin/udevadm", "settle", NULL); + (void) command (NULL, NULL, "udevadm", "settle", NULL); + (void) command (NULL, NULL, "udevsettle", NULL); }