X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=206841e6089768cf64dc549c88ab4e7c6e33b455;hp=79fc5bfabafd8b73b8325d145f2f118aad5fe707;hb=70975981bed8e0c01b5966c10b507bb82086e5f8;hpb=d2636b0984830d950423dadd936114ff242c4a00 diff --git a/src/guestfs.c b/src/guestfs.c index 79fc5bf..206841e 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -72,6 +72,7 @@ #include "guestfs_protocol.h" static void default_error_cb (guestfs_h *g, void *data, const char *msg); +static void remove_tmpdir (guestfs_h *g); static void close_handles (void); gl_lock_define_initialized (static, handles_lock); @@ -132,7 +133,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 +154,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 +174,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,18 +213,11 @@ 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. */ - if (g->tmpdir) { - snprintf (filename, sizeof filename, "%s/sock", g->tmpdir); - unlink (filename); - - rmdir (g->tmpdir); - - free (g->tmpdir); - } + /* Remove whole temporary directory. */ + remove_tmpdir (g); if (g->cmdline) { for (i = 0; i < g->cmdline_size; ++i) @@ -255,6 +249,43 @@ guestfs_close (guestfs_h *g) free (g); } +/* g->tmpdir can contain any files (but not subdirectories). Remove + * those and the directory itself. Note that errors in this function + * aren't really that important: if we end up not deleting temporary + * files it's only annoying. + */ +static void +remove_tmpdir (guestfs_h *g) +{ + DIR *dir; + struct dirent *d; + + if (!g->tmpdir) + return; + + dir = opendir (g->tmpdir); + if (dir == NULL) { + perror (g->tmpdir); + return; + } + + while ((d = readdir (dir)) != NULL) { + if (STRNEQ (d->d_name, ".") && STRNEQ (d->d_name, "..")) { + if (unlinkat (dirfd (dir), d->d_name, 0) == -1) + perror (d->d_name); + } + } + + if (closedir (dir) == -1) + perror (g->tmpdir); + + if (rmdir (g->tmpdir) == -1) + perror (g->tmpdir); + + free (g->tmpdir); + g->tmpdir = NULL; +} + /* Close all open handles (called from atexit(3)). */ static void close_handles (void) @@ -282,6 +313,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 +749,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 +870,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). */