New event API (RHBZ#664558).
[libguestfs.git] / src / guestfs.c
index 79fc5bf..776214e 100644 (file)
@@ -132,7 +132,7 @@ guestfs_create (void)
   str = getenv ("LIBGUESTFS_MEMSIZE");
   if (str) {
     if (sscanf (str, "%d", &g->memsize) != 1 || g->memsize <= 256) {
-      fprintf (stderr, "libguestfs: non-numeric or too small value for LIBGUESTFS_MEMSIZE\n");
+      warning (g, "non-numeric or too small value for LIBGUESTFS_MEMSIZE");
       goto error;
     }
   } else
@@ -153,8 +153,7 @@ guestfs_create (void)
   }
   gl_lock_unlock (handles_lock);
 
-  if (g->verbose)
-    fprintf (stderr, "new guestfs handle %p\n", g);
+  debug (g, "new guestfs handle %p", g);
 
   return g;
 
@@ -174,32 +173,33 @@ guestfs_close (guestfs_h *g)
   guestfs_h *gg;
 
   if (g->state == NO_HANDLE) {
-    /* Not safe to call 'error' here, so ... */
+    /* Not safe to call ANY callbacks here, so ... */
     fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
     return;
   }
 
-  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);
-
-  guestfs___free_inspect_info (g);
+  debug (g, "closing guestfs handle %p (state %d)", g, g->state);
 
   /* Try to sync if autosync flag is set. */
   if (g->autosync && g->state == READY)
     guestfs_internal_autosync (g);
 
-  /* Remove any handlers that might be called back before we kill the
-   * subprocess.
-   */
-  g->log_message_cb = NULL;
-
+  /* Kill the qemu subprocess. */
   if (g->state != CONFIG)
     guestfs_kill_subprocess (g);
 
+  /* Run user close callbacks. */
+  guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);
+
+  /* Remove all other registered callbacks.  Since we've already
+   * called the close callbacks, we shouldn't call any others.
+   */
+  free (g->events);
+  g->nr_events = 0;
+  g->events = NULL;
+
+  guestfs___free_inspect_info (g);
+
   /* Close sockets. */
   if (g->fd[0] >= 0)
     close (g->fd[0]);
@@ -212,7 +212,7 @@ guestfs_close (guestfs_h *g)
   g->sock = -1;
 
   /* Wait for subprocess(es) to exit. */
-  waitpid (g->pid, NULL, 0);
+  if (g->pid > 0) waitpid (g->pid, NULL, 0);
   if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
 
   /* Remove tmpfiles. */
@@ -282,6 +282,79 @@ set_last_error (guestfs_h *g, int errnum, const char *msg)
   g->last_errnum = errnum;
 }
 
+/* Warning are printed unconditionally.  We try to make these rare.
+ * Generally speaking, a warning should either be an error, or if it's
+ * not important for end users then it should be a debug message.
+ */
+void
+guestfs___warning (guestfs_h *g, const char *fs, ...)
+{
+  va_list args;
+  char *msg, *msg2;
+  int len;
+
+  va_start (args, fs);
+  len = vasprintf (&msg, fs, args);
+  va_end (args);
+
+  if (len < 0) return;
+
+  len = asprintf (&msg2, _("warning: %s"), msg);
+  free (msg);
+
+  if (len < 0) return;
+
+  guestfs___call_callbacks_message (g, GUESTFS_EVENT_LIBRARY, msg2, len);
+
+  free (msg2);
+}
+
+/* Debug messages. */
+void
+guestfs___debug (guestfs_h *g, const char *fs, ...)
+{
+  va_list args;
+  char *msg;
+  int len;
+
+  /* The cpp macro "debug" has already checked that g->verbose is true
+   * before calling this function, but we check it again just in case
+   * anyone calls this function directly.
+   */
+  if (!g->verbose)
+    return;
+
+  va_start (args, fs);
+  len = vasprintf (&msg, fs, args);
+  va_end (args);
+
+  if (len < 0) return;
+
+  guestfs___call_callbacks_message (g, GUESTFS_EVENT_LIBRARY, msg, len);
+}
+
+/* Call trace messages.  These are enabled by setting g->trace, and
+ * calls to this function should only happen from the generated code
+ * in src/actions.c
+ */
+void
+guestfs___trace (guestfs_h *g, const char *fs, ...)
+{
+  va_list args;
+  char *msg;
+  int len;
+
+  va_start (args, fs);
+  len = vasprintf (&msg, fs, args);
+  va_end (args);
+
+  if (len < 0) return;
+
+  guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE, msg, len);
+
+  free (msg);
+}
+
 static void
 default_error_cb (guestfs_h *g, void *data, const char *msg)
 {
@@ -645,44 +718,49 @@ guestfs__get_network (guestfs_h *g)
   return g->enable_network;
 }
 
-void
-guestfs_set_log_message_callback (guestfs_h *g,
-                                  guestfs_log_message_cb cb, void *opaque)
+int
+guestfs__set_attach_method (guestfs_h *g, const char *method)
 {
-  g->log_message_cb = cb;
-  g->log_message_cb_data = opaque;
-}
+  if (STREQ (method, "appliance")) {
+    g->attach_method = ATTACH_METHOD_APPLIANCE;
+    free (g->attach_method_arg);
+    g->attach_method_arg = NULL;
+  }
+  else if (STRPREFIX (method, "unix:") && strlen (method) > 5) {
+    g->attach_method = ATTACH_METHOD_UNIX;
+    free (g->attach_method_arg);
+    g->attach_method_arg = safe_strdup (g, method + 5);
+    /* Note that we don't check the path exists until launch is called. */
+  }
+  else {
+    error (g, "invalid attach method: %s", method);
+    return -1;
+  }
 
-void
-guestfs_set_subprocess_quit_callback (guestfs_h *g,
-                                      guestfs_subprocess_quit_cb cb, void *opaque)
-{
-  g->subprocess_quit_cb = cb;
-  g->subprocess_quit_cb_data = opaque;
+  return 0;
 }
 
-void
-guestfs_set_launch_done_callback (guestfs_h *g,
-                                  guestfs_launch_done_cb cb, void *opaque)
+char *
+guestfs__get_attach_method (guestfs_h *g)
 {
-  g->launch_done_cb = cb;
-  g->launch_done_cb_data = opaque;
-}
+  char *ret;
 
-void
-guestfs_set_close_callback (guestfs_h *g,
-                            guestfs_close_cb cb, void *opaque)
-{
-  g->close_cb = cb;
-  g->close_cb_data = opaque;
-}
+  switch (g->attach_method) {
+  case ATTACH_METHOD_APPLIANCE:
+    ret = safe_strdup (g, "appliance");
+    break;
 
-void
-guestfs_set_progress_callback (guestfs_h *g,
-                               guestfs_progress_cb cb, void *opaque)
-{
-  g->progress_cb = cb;
-  g->progress_cb_data = opaque;
+  case ATTACH_METHOD_UNIX:
+    ret = safe_malloc (g, strlen (g->attach_method_arg) + 5 + 1);
+    strcpy (ret, "unix:");
+    strcat (ret, g->attach_method_arg);
+    break;
+
+  default: /* keep GCC happy - this is not reached */
+    abort ();
+  }
+
+  return ret;
 }
 
 /* Note the private data area is allocated lazily, since the vast
@@ -761,6 +839,47 @@ guestfs_get_private (guestfs_h *g, const char *key)
     return NULL;
 }
 
+/* Iterator. */
+void *
+guestfs_first_private (guestfs_h *g, const char **key_rtn)
+{
+  if (g->pda == NULL)
+    return NULL;
+
+  g->pda_next = hash_get_first (g->pda);
+
+  /* Ignore any keys with NULL data pointers. */
+  while (g->pda_next && g->pda_next->data == NULL)
+    g->pda_next = hash_get_next (g->pda, g->pda_next);
+
+  if (g->pda_next == NULL)
+    return NULL;
+
+  *key_rtn = g->pda_next->key;
+  return g->pda_next->data;
+}
+
+void *
+guestfs_next_private (guestfs_h *g, const char **key_rtn)
+{
+  if (g->pda == NULL)
+    return NULL;
+
+  if (g->pda_next == NULL)
+    return NULL;
+
+  /* Walk to the next key with a non-NULL data pointer. */
+  do {
+    g->pda_next = hash_get_next (g->pda, g->pda_next);
+  } while (g->pda_next && g->pda_next->data == NULL);
+
+  if (g->pda_next == NULL)
+    return NULL;
+
+  *key_rtn = g->pda_next->key;
+  return g->pda_next->data;
+}
+
 /* When tracing, be careful how we print BufferIn parameters which
  * usually contain large amounts of binary data (RHBZ#646822).
  */