X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=002418a446def731761205c423053fdf98ba2474;hp=488b6586ed031acefc9f412dadb48eafb88789d4;hb=60d5a50f4d3d9e2c2f5a7d42a6859de709bda3f6;hpb=f445d25d7b6bb65470564c96d7198d63538880a3 diff --git a/src/guestfs.c b/src/guestfs.c index 488b658..002418a 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -1,5 +1,5 @@ /* libguestfs - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -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; @@ -169,39 +169,41 @@ guestfs_create (void) void guestfs_close (guestfs_h *g) { - int i; - char filename[256]; - 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); + if (g->trace) { + const char trace_msg[] = "close"; - /* Run user close callback before anything else. */ - if (g->close_cb) - g->close_cb (g, g->close_cb_data); + guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE, + trace_msg, strlen (trace_msg)); + } - 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_umount_all (g); - guestfs_sync (g); - } - - /* Remove any handlers that might be called back before we kill the - * subprocess. - */ - g->log_message_cb = NULL; + if (g->autosync && g->state == READY) + guestfs_internal_autosync (g); + /* 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]); @@ -214,20 +216,15 @@ 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) { + size_t i; + for (i = 0; i < g->cmdline_size; ++i) free (g->cmdline[i]); free (g->cmdline); @@ -240,6 +237,8 @@ guestfs_close (guestfs_h *g) if (handles == g) handles = g->next; else { + guestfs_h *gg; + for (gg = handles; gg->next != g; gg = gg->next) ; gg->next = g->next; @@ -257,6 +256,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) @@ -284,6 +320,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) { @@ -324,14 +433,8 @@ guestfs_perrorf (guestfs_h *g, const char *fs, ...) if (err < 0) return; -#if !defined(_GNU_SOURCE) || defined(__APPLE__) char buf[256]; strerror_r (errnum, buf, sizeof buf); -#else - char _buf[256]; - char *buf; - buf = strerror_r (errnum, _buf, sizeof _buf); -#endif msg = safe_realloc (g, msg, strlen (msg) + 2 + strlen (buf) + 1); strcat (msg, ": "); @@ -469,6 +572,12 @@ guestfs_get_error_handler (guestfs_h *g, void **data_rtn) return g->error_cb; } +void +guestfs_user_cancel (guestfs_h *g) +{ + g->user_cancel = 1; +} + int guestfs__set_verbose (guestfs_h *g, int v) { @@ -647,44 +756,62 @@ 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; + + switch (g->attach_method) { + case ATTACH_METHOD_APPLIANCE: + ret = safe_strdup (g, "appliance"); + break; + + 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; } -void -guestfs_set_close_callback (guestfs_h *g, - guestfs_close_cb cb, void *opaque) +int +guestfs__set_pgroup (guestfs_h *g, int v) { - g->close_cb = cb; - g->close_cb_data = opaque; + g->pgroup = !!v; + return 0; } -void -guestfs_set_progress_callback (guestfs_h *g, - guestfs_progress_cb cb, void *opaque) +int +guestfs__get_pgroup (guestfs_h *g) { - g->progress_cb = cb; - g->progress_cb_data = opaque; + return g->pgroup; } /* Note the private data area is allocated lazily, since the vast @@ -763,6 +890,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). */