daemon/Win32: Win32 can't fork message moved to separate function (Jim Meyering)
[libguestfs.git] / daemon / guestfsd.c
index e3b8883..eef7c6d 100644 (file)
 
 #define _BSD_SOURCE            /* for daemon(3) */
 
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <rpc/types.h>
 #include <rpc/xdr.h>
 #include <getopt.h>
-#include <netdb.h>
 #include <sys/param.h>
-#include <sys/select.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <ctype.h>
 #include <signal.h>
+#include <netdb.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_PRINTF_H
 #include <printf.h>
+#endif
+
+#include "c-ctype.h"
+#include "ignore-value.h"
 
 #include "daemon.h"
 
@@ -46,6 +55,15 @@ static char *read_cmdline (void);
 #define GUESTFWD_ADDR "10.0.2.4"
 #define 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;
 
 static int print_shell_quote (FILE *stream, const struct printf_info *info, const void *const *args);
@@ -60,6 +78,17 @@ 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 */
+
 /* Location to mount root device. */
 const char *sysroot = "/sysroot"; /* No trailing slash. */
 int sysroot_len = 8;
@@ -119,17 +148,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 ();
@@ -147,6 +176,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);
@@ -154,7 +184,11 @@ 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
@@ -163,8 +197,16 @@ main (int argc, char *argv[])
   setenv ("SHELL", "/bin/sh", 1);
   setenv ("LC_ALL", "C", 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.
    *
@@ -188,7 +230,7 @@ main (int argc, char *argv[])
       vmchannel = strndup (p + 18, len);
       if (!vmchannel) {
         perror ("strndup");
-        exit (1);
+        exit (EXIT_FAILURE);
       }
     }
 
@@ -202,7 +244,7 @@ main (int argc, char *argv[])
         vmchannel = strndup (p + 4, len);
         if (!vmchannel) {
           perror ("strndup");
-          exit (1);
+          exit (EXIT_FAILURE);
         }
         memcpy (vmchannel, "tcp:", 4);
       }
@@ -214,7 +256,7 @@ main (int argc, char *argv[])
     vmchannel = strdup ("tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT);
     if (!vmchannel) {
       perror ("strdup");
-      exit (1);
+      exit (EXIT_FAILURE);
     }
   }
 
@@ -224,7 +266,7 @@ main (int argc, char *argv[])
   /* Connect to vmchannel. */
   int sock = -1;
 
-  if (strncmp (vmchannel, "tcp:", 4) == 0) {
+  if (STREQLEN (vmchannel, "tcp:", 4)) {
     /* Resolve the hostname. */
     struct addrinfo *res, *rr;
     struct addrinfo hints;
@@ -239,7 +281,7 @@ main (int argc, char *argv[])
     } else {
       fprintf (stderr, "vmchannel: expecting \"tcp:<ip>:<port>\": %s\n",
                vmchannel);
-      exit (1);
+      exit (EXIT_FAILURE);
     }
 
     memset (&hints, 0, sizeof hints);
@@ -249,7 +291,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. */
@@ -270,7 +312,7 @@ main (int argc, char *argv[])
              "unknown vmchannel connection type: %s\n"
              "expecting \"tcp:<ip>:<port>\"\n",
              vmchannel);
-    exit (1);
+    exit (EXIT_FAILURE);
   }
 
   if (sock == -1) {
@@ -289,7 +331,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
@@ -299,10 +341,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);
 
@@ -310,14 +352,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. */
@@ -508,13 +550,11 @@ free_stringslen (char **argv, int len)
   free (argv);
 }
 
-/* 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.
+/* Easy ways to run external commands.  For full documentation, see
+ * 'commandrvf' below.
  */
 int
-command (char **stdoutput, char **stderror, const char *name, ...)
+commandf (char **stdoutput, char **stderror, int flags, const char *name, ...)
 {
   va_list args;
   const char **argv;
@@ -548,7 +588,7 @@ command (char **stdoutput, char **stderror, const char *name, ...)
 
   va_end (args);
 
-  r = commandv (stdoutput, stderror, (char **) argv);
+  r = commandvf (stdoutput, stderror, flags, (const char * const*) argv);
 
   /* NB: Mustn't free the strings which are on the stack. */
   free (argv);
@@ -561,7 +601,7 @@ command (char **stdoutput, char **stderror, const char *name, ...)
  * We still return -1 if there was some other error.
  */
 int
-commandr (char **stdoutput, char **stderror, const char *name, ...)
+commandrf (char **stdoutput, char **stderror, int flags, const char *name, ...)
 {
   va_list args;
   const char **argv;
@@ -595,7 +635,7 @@ commandr (char **stdoutput, char **stderror, const char *name, ...)
 
   va_end (args);
 
-  r = commandrv (stdoutput, stderror, argv);
+  r = commandrvf (stdoutput, stderror, flags, argv);
 
   /* NB: Mustn't free the strings which are on the stack. */
   free (argv);
@@ -605,19 +645,43 @@ commandr (char **stdoutput, char **stderror, const char *name, ...)
 
 /* Same as 'command', but passing an argv. */
 int
-commandv (char **stdoutput, char **stderror, char *const *argv)
+commandvf (char **stdoutput, char **stderror, int flags,
+           char const *const *argv)
 {
   int r;
 
-  r = commandrv (stdoutput, stderror, (void *) argv);
+  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
-commandrv (char **stdoutput, char **stderror, char const* const *argv)
+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];
@@ -654,9 +718,13 @@ commandrv (char **stdoutput, char **stderror, char const* const *argv)
 
   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]);
-    dup2 (so_fd[1], 1);
+    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]);
@@ -717,15 +785,20 @@ commandrv (char **stdoutput, char **stderror, char const* const *argv)
       }
       if (r == 0) { FD_CLR (se_fd[0], &rset); quit++; }
 
-      if (r > 0 && stderror) {
-        se_size += r;
-        p = realloc (*stderror, se_size);
-        if (p == NULL) {
-          perror ("realloc");
-          goto 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);
         }
-        *stderror = p;
-        memcpy (*stderror + se_size - r, buf, r);
       }
     }
   }
@@ -798,7 +871,7 @@ split_lines (char *str)
   int size = 0, alloc = 0;
   char *p, *pend;
 
-  if (strcmp (str, "") == 0)
+  if (STREQ (str, ""))
     goto empty_list;
 
   p = str;
@@ -835,7 +908,7 @@ print_shell_quote (FILE *stream,
                    const struct printf_info *info ATTRIBUTE_UNUSED,
                    const void *const *args)
 {
-#define SAFE(c) (isalnum((c)) ||                                       \
+#define SAFE(c) (c_isalnum((c)) ||                                     \
                  (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
   int i, len;
   const char *str = *((const char **) (args[0]));
@@ -912,7 +985,7 @@ device_name_translation (char *device, const char *func)
   }
 
   /* If the name begins with "/dev/sd" then try the alternatives. */
-  if (strncmp (device, "/dev/sd", 7) != 0)
+  if (STRNEQLEN (device, "/dev/sd", 7))
     goto error;
 
   device[5] = 'h';             /* /dev/hd (old IDE driver) */
@@ -943,5 +1016,25 @@ device_name_translation (char *device, const char *func)
 void
 udev_settle (void)
 {
-  command (NULL, NULL, "/sbin/udevadm", "settle", NULL);
+  static int which_prog = 0;
+
+  if (which_prog == 0) {
+    if (access ("/sbin/udevsettle", X_OK) == 0)
+      which_prog = 2;
+    else if (access ("/sbin/udevadm", X_OK) == 0)
+      which_prog = 1;
+    else
+      which_prog = 3;
+  }
+
+  switch (which_prog) {
+  case 1:
+    command (NULL, NULL, "/sbin/udevadm", "settle", NULL);
+    break;
+  case 2:
+    command (NULL, NULL, "/sbin/udevsettle", NULL);
+    break;
+  default:
+    ;
+  }
 }