generator: Make documentation inside guestfish match man page.
[libguestfs.git] / src / guestfs.c
index e0f60f4..85a042a 100644 (file)
@@ -183,6 +183,8 @@ struct guestfs_h
   void *                     subprocess_quit_cb_data;
   guestfs_launch_done_cb     launch_done_cb;
   void *                     launch_done_cb_data;
+  guestfs_close_cb           close_cb;
+  void *                     close_cb_data;
 
   int msg_next_serial;
 };
@@ -294,6 +296,10 @@ guestfs_close (guestfs_h *g)
   if (g->verbose)
     fprintf (stderr, "closing guestfs handle %p (state %d)\n", g, g->state);
 
+  /* Run user close callback before anything else. */
+  if (g->close_cb)
+    g->close_cb (g, g->close_cb_data);
+
   /* Try to sync if autosync flag is set. */
   if (g->autosync && g->state == READY) {
     guestfs_umount_all (g);
@@ -928,10 +934,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)
@@ -948,9 +950,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
@@ -960,18 +974,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) {
@@ -980,6 +982,14 @@ guestfs__launch (guestfs_h *g)
     }
   }
 
+  /* Allow anyone to read the temporary directory.  There are no
+   * secrets in the kernel or initrd files.  The socket in this
+   * directory won't be readable but anyone can see it exists if they
+   * want. (RHBZ#610880).
+   */
+  if (chmod (g->tmpdir, 0755) == -1)
+    fprintf (stderr, "chmod: %s: %m (ignored)\n", g->tmpdir);
+
   /* First search g->path for the supermin appliance, and try to
    * synthesize a kernel and initrd from that.  If it fails, we
    * try the path search again looking for a backup ordinary
@@ -998,8 +1008,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;
@@ -1011,8 +1020,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;
@@ -1590,12 +1598,20 @@ build_supermin_appliance (guestfs_h *g, const char *path,
   *initrd = safe_malloc (g, len + 8);
   snprintf (*initrd, len+8, "%s/initrd", g->tmpdir);
 
+  /* Set a sensible umask in the subprocess, so kernel and initrd
+   * output files are world-readable (RHBZ#610880).
+   */
   snprintf (cmd, sizeof cmd,
-            "PATH='%s':$PATH "
-            "libguestfs-supermin-helper%s '%s' " host_cpu " " REPO " %s %s",
-            path,
+            "umask 0002; "
+            "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);
 
@@ -1929,6 +1945,14 @@ guestfs_set_launch_done_callback (guestfs_h *g,
   g->launch_done_cb_data = opaque;
 }
 
+void
+guestfs_set_close_callback (guestfs_h *g,
+                            guestfs_close_cb cb, void *opaque)
+{
+  g->close_cb = cb;
+  g->close_cb_data = opaque;
+}
+
 /*----------------------------------------------------------------------*/
 
 /* This is the code used to send and receive RPC messages and (for
@@ -2038,7 +2062,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;
@@ -2059,6 +2083,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;
   }
 
@@ -2166,7 +2197,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)) {
@@ -2249,7 +2280,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;
@@ -2404,7 +2435,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)) {