New APIs: vfs-label and vfs-uuid return label and uuid for many fs types.
[libguestfs.git] / src / guestfs.c
index e6fcb0e..0e4cb73 100644 (file)
@@ -110,8 +110,27 @@ static int qemu_supports (guestfs_h *g, const char *option);
 #define xdr_uint32_t xdr_u_int32_t
 #endif
 
-/* Also in guestfsd.c */
-#define GUESTFWD_ADDR "10.0.2.4"
+/* Network configuration of the appliance.  Note these addresses are
+ * only meaningful within the context of the running appliance.  QEMU
+ * translates network connections to these magic addresses into
+ * userspace calls on the host (eg. connect(2)).  qemu-doc has a nice
+ * diagram which is also useful to refer to.
+ *
+ * NETWORK: The network.
+ *
+ * ROUTER: The address of the "host", ie. this library.
+ *
+ * [Note: If you change NETWORK and ROUTER then you also have to
+ * change the network configuration in appliance/init].
+ *
+ * GUESTFWD_ADDR, GUESTFWD_PORT: The guestfwd feature of qemu
+ * magically connects this pseudo-address to the guestfwd channel.  In
+ * typical Linux configurations of libguestfs, guestfwd is not
+ * actually used any more.
+ */
+#define NETWORK "169.254.0.0/16"
+#define ROUTER "169.254.2.2"
+#define GUESTFWD_ADDR "169.254.2.4"
 #define GUESTFWD_PORT "6666"
 
 /* GuestFS handle and connection. */
@@ -909,10 +928,6 @@ static void print_cmdline (guestfs_h *g);
 
 static const char *kernel_name = "vmlinuz." REPO "." host_cpu;
 static const char *initrd_name = "initramfs." REPO "." host_cpu ".img";
-static const char *supermin_name =
-  "initramfs." REPO "." host_cpu ".supermin.img";
-static const char *supermin_hostfiles_name =
-  "initramfs." REPO "." host_cpu ".supermin.hostfiles";
 
 int
 guestfs__launch (guestfs_h *g)
@@ -929,9 +944,21 @@ guestfs__launch (guestfs_h *g)
   char unixsock[256];
   struct sockaddr_un addr;
 
+  /* Configured? */
+  if (!g->cmdline) {
+    error (g, _("you must call guestfs_add_drive before guestfs_launch"));
+    return -1;
+  }
+
+  if (g->state != CONFIG) {
+    error (g, _("the libguestfs handle has already been launched"));
+    return -1;
+  }
+
   /* Start the clock ... */
   gettimeofday (&g->launch_t, NULL);
 
+  /* Make the temporary directory. */
 #ifdef P_tmpdir
   tmpdir = P_tmpdir;
 #else
@@ -941,18 +968,6 @@ guestfs__launch (guestfs_h *g)
   tmpdir = getenv ("TMPDIR") ? : tmpdir;
   snprintf (dir_template, sizeof dir_template, "%s/libguestfsXXXXXX", tmpdir);
 
-  /* Configured? */
-  if (!g->cmdline) {
-    error (g, _("you must call guestfs_add_drive before guestfs_launch"));
-    return -1;
-  }
-
-  if (g->state != CONFIG) {
-    error (g, _("qemu has already been launched"));
-    return -1;
-  }
-
-  /* Make the temporary directory. */
   if (!g->tmpdir) {
     g->tmpdir = safe_strdup (g, dir_template);
     if (mkdtemp (g->tmpdir) == NULL) {
@@ -979,8 +994,7 @@ guestfs__launch (guestfs_h *g)
         fprintf (stderr,
                  "looking for supermin appliance in current directory\n");
       if (dir_contains_files (".",
-                              supermin_name, supermin_hostfiles_name,
-                              "kmod.whitelist", NULL)) {
+                              "supermin.d", "kmod.whitelist", NULL)) {
         if (build_supermin_appliance (g, ".", &kernel, &initrd) == -1)
           return -1;
         break;
@@ -992,8 +1006,7 @@ guestfs__launch (guestfs_h *g)
         fprintf (stderr, "looking for supermin appliance in %s\n", pelem);
 
       if (dir_contains_files (pelem,
-                              supermin_name, supermin_hostfiles_name,
-                              "kmod.whitelist", NULL)) {
+                              "supermin.d", "kmod.whitelist", NULL)) {
         if (build_supermin_appliance (g, pelem, &kernel, &initrd) == -1)
           return -1;
         break;
@@ -1189,10 +1202,11 @@ guestfs__launch (guestfs_h *g)
      */
     if (null_vmchannel_sock) {
       add_cmdline (g, "-net");
-      add_cmdline (g, "user,vlan=0,net=10.0.2.0/8");
+      add_cmdline (g, "user,vlan=0,net=" NETWORK);
 
       snprintf (buf, sizeof buf,
-                "guestfs_vmchannel=tcp:10.0.2.2:%d", null_vmchannel_sock);
+                "guestfs_vmchannel=tcp:" ROUTER ":%d",
+                null_vmchannel_sock);
       vmchannel = strdup (buf);
     }
 
@@ -1215,7 +1229,7 @@ guestfs__launch (guestfs_h *g)
       add_cmdline (g, buf);
 
       snprintf (buf, sizeof buf,
-                "user,vlan=0,net=10.0.2.0/8,"
+                "user,vlan=0,net=" NETWORK ","
                 "guestfwd=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT
                 "-chardev:guestfsvmc");
 
@@ -1235,7 +1249,7 @@ guestfs__launch (guestfs_h *g)
       add_cmdline (g, "-net");
       add_cmdline (g, buf);
       add_cmdline (g, "-net");
-      add_cmdline (g, "user,vlan=0,net=10.0.2.0/8");
+      add_cmdline (g, "user,vlan=0,net=" NETWORK);
 
       vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT;
     }
@@ -1257,10 +1271,12 @@ guestfs__launch (guestfs_h *g)
               "%s "             /* (selinux) */
               "%s "             /* (vmchannel) */
               "%s "             /* (verbose) */
+              "TERM=%s "        /* (TERM environment variable) */
               "%s",             /* (append) */
               g->selinux ? "selinux=1 enforcing=0" : "selinux=0",
               vmchannel ? vmchannel : "",
               g->verbose ? "guestfs_verbose=1" : "",
+              getenv ("TERM") ? : "linux",
               g->append ? g->append : "");
 
     add_cmdline (g, "-kernel");
@@ -1403,6 +1419,7 @@ guestfs__launch (guestfs_h *g)
                  "libguestfs: warning: unexpected connection from UID %d to port %d\n",
                  uid, null_vmchannel_sock);
         close (sock);
+        sock = -1;
         continue;
       }
     }
@@ -1497,7 +1514,7 @@ guestfs__launch (guestfs_h *g)
     close (wfd[1]);
     close (rfd[0]);
   }
-  kill (g->pid, 9);
+  if (g->pid > 0) kill (g->pid, 9);
   if (g->recoverypid > 0) kill (g->recoverypid, 9);
   waitpid (g->pid, NULL, 0);
   if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
@@ -1568,11 +1585,15 @@ build_supermin_appliance (guestfs_h *g, const char *path,
   snprintf (*initrd, len+8, "%s/initrd", g->tmpdir);
 
   snprintf (cmd, sizeof cmd,
-            "PATH='%s':$PATH "
-            "libguestfs-supermin-helper%s '%s' " host_cpu " " REPO " %s %s",
-            path,
+            "febootstrap-supermin-helper%s "
+            "-k '%s/kmod.whitelist' "
+            "'%s/supermin.d' "
+            host_cpu " "
+            "%s %s",
             g->verbose ? " --verbose" : "",
-            path, *kernel, *initrd);
+            path,
+            path,
+            *kernel, *initrd);
   if (g->verbose)
     print_timestamped_message (g, "%s", cmd);
 
@@ -1845,7 +1866,7 @@ guestfs__kill_subprocess (guestfs_h *g)
   if (g->verbose)
     fprintf (stderr, "sending SIGTERM to process %d\n", g->pid);
 
-  kill (g->pid, SIGTERM);
+  if (g->pid > 0) kill (g->pid, SIGTERM);
   if (g->recoverypid > 0) kill (g->recoverypid, 9);
 
   return 0;
@@ -1996,7 +2017,7 @@ child_cleanup (guestfs_h *g)
   if (g->verbose)
     fprintf (stderr, "child_cleanup: %p: child process died\n", g);
 
-  /*kill (g->pid, SIGTERM);*/
+  /*if (g->pid > 0) kill (g->pid, SIGTERM);*/
   if (g->recoverypid > 0) kill (g->recoverypid, 9);
   waitpid (g->pid, NULL, 0);
   if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
@@ -2015,7 +2036,7 @@ child_cleanup (guestfs_h *g)
 }
 
 static int
-read_log_message_or_eof (guestfs_h *g, int fd)
+read_log_message_or_eof (guestfs_h *g, int fd, int error_if_eof)
 {
   char buf[BUFSIZ];
   int n;
@@ -2036,6 +2057,13 @@ read_log_message_or_eof (guestfs_h *g, int fd)
   if (n == 0) {
     /* Hopefully this indicates the qemu child process has died. */
     child_cleanup (g);
+
+    if (error_if_eof) {
+      /* We weren't expecting eof here (called from launch) so place
+       * something in the error buffer.  RHBZ#588851.
+       */
+      error (g, "child process died unexpectedly");
+    }
     return -1;
   }
 
@@ -2143,7 +2171,7 @@ send_to_daemon (guestfs_h *g, const void *v_buf, size_t n)
     }
 
     if (FD_ISSET (g->fd[1], &rset2)) {
-      if (read_log_message_or_eof (g, g->fd[1]) == -1)
+      if (read_log_message_or_eof (g, g->fd[1], 0) == -1)
         return -1;
     }
     if (FD_ISSET (g->sock, &rset2)) {
@@ -2226,7 +2254,7 @@ recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn)
     }
 
     if (FD_ISSET (g->fd[1], &rset2)) {
-      if (read_log_message_or_eof (g, g->fd[1]) == -1) {
+      if (read_log_message_or_eof (g, g->fd[1], 0) == -1) {
         free (*buf_rtn);
         *buf_rtn = NULL;
         return -1;
@@ -2363,8 +2391,16 @@ accept_from_daemon (guestfs_h *g)
   int sock = -1;
 
   while (sock == -1) {
+    /* If the qemu process has died, clean up the zombie (RHBZ#579155).
+     * By partially polling in the select below we ensure that this
+     * function will be called eventually.
+     */
+    waitpid (g->pid, NULL, WNOHANG);
+
     rset2 = rset;
-    int r = select (max_fd+1, &rset2, NULL, NULL, NULL);
+
+    struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+    int r = select (max_fd+1, &rset2, NULL, NULL, &tv);
     if (r == -1) {
       if (errno == EINTR || errno == EAGAIN)
         continue;
@@ -2373,7 +2409,7 @@ accept_from_daemon (guestfs_h *g)
     }
 
     if (FD_ISSET (g->fd[1], &rset2)) {
-      if (read_log_message_or_eof (g, g->fd[1]) == -1)
+      if (read_log_message_or_eof (g, g->fd[1], 1) == -1)
         return -1;
     }
     if (FD_ISSET (g->sock, &rset2)) {