X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=002418a446def731761205c423053fdf98ba2474;hp=cef80db8698ec94a52719ed47af5cd3a9e16d990;hb=60d5a50f4d3d9e2c2f5a7d42a6859de709bda3f6;hpb=8289aa1ad68ec94c87fc4d538f638d8816052d92 diff --git a/src/guestfs.c b/src/guestfs.c index cef80db..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 @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -63,7 +63,8 @@ #include "c-ctype.h" #include "glthread/lock.h" -#include "ignore-value.h" +#include "hash.h" +#include "hash-pjw.h" #include "guestfs.h" #include "guestfs-internal.h" @@ -71,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); @@ -99,6 +101,7 @@ guestfs_create (void) g->error_cb_data = NULL; g->recovery_proc = 1; + g->autosync = 1; str = getenv ("LIBGUESTFS_DEBUG"); g->verbose = str != NULL && STREQ (str, "1"); @@ -130,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 @@ -151,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; @@ -167,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]); @@ -212,26 +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); - - snprintf (filename, sizeof filename, "%s/initrd", g->tmpdir); - unlink (filename); - - snprintf (filename, sizeof filename, "%s/kernel", 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); @@ -244,12 +237,16 @@ 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; } gl_lock_unlock (handles_lock); + if (g->pda) + hash_free (g->pda); free (g->last_error); free (g->path); free (g->qemu); @@ -259,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) @@ -272,11 +306,91 @@ guestfs_last_error (guestfs_h *g) return g->last_error; } +int +guestfs_last_errno (guestfs_h *g) +{ + return g->last_errnum; +} + static void -set_last_error (guestfs_h *g, const char *msg) +set_last_error (guestfs_h *g, int errnum, const char *msg) { free (g->last_error); g->last_error = strdup (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 @@ -286,7 +400,7 @@ default_error_cb (guestfs_h *g, void *data, const char *msg) } void -guestfs_error (guestfs_h *g, const char *fs, ...) +guestfs_error_errno (guestfs_h *g, int errnum, const char *fs, ...) { va_list args; char *msg; @@ -297,8 +411,11 @@ guestfs_error (guestfs_h *g, const char *fs, ...) if (err < 0) return; + /* set_last_error first so that the callback can access the error + * message and errno through the handle if it wishes. + */ + set_last_error (g, errnum, msg); if (g->error_cb) g->error_cb (g, g->error_cb_data, msg); - set_last_error (g, msg); free (msg); } @@ -316,21 +433,18 @@ 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, ": "); strcat (msg, buf); + /* set_last_error first so that the callback can access the error + * message and errno through the handle if it wishes. + */ + set_last_error (g, errnum, msg); if (g->error_cb) g->error_cb (g, g->error_cb_data, msg); - set_last_error (g, msg); free (msg); } @@ -416,6 +530,22 @@ guestfs_safe_memdup (guestfs_h *g, void *ptr, size_t size) return p; } +char * +guestfs_safe_asprintf (guestfs_h *g, const char *fs, ...) +{ + va_list args; + char *msg; + + va_start (args, fs); + int err = vasprintf (&msg, fs, args); + va_end (args); + + if (err == -1) + g->abort_cb (); + + return msg; +} + void guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb cb) { @@ -442,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) { @@ -607,34 +743,233 @@ guestfs__get_recovery_proc (guestfs_h *g) return g->recovery_proc; } +int +guestfs__set_network (guestfs_h *g, int v) +{ + g->enable_network = !!v; + return 0; +} + +int +guestfs__get_network (guestfs_h *g) +{ + return g->enable_network; +} + +int +guestfs__set_attach_method (guestfs_h *g, const char *method) +{ + 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; + } + + return 0; +} + +char * +guestfs__get_attach_method (guestfs_h *g) +{ + 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; +} + +int +guestfs__set_pgroup (guestfs_h *g, int v) +{ + g->pgroup = !!v; + return 0; +} + +int +guestfs__get_pgroup (guestfs_h *g) +{ + return g->pgroup; +} + +/* Note the private data area is allocated lazily, since the vast + * majority of callers will never use it. This means g->pda is + * likely to be NULL. + */ +struct pda_entry { + char *key; /* key */ + void *data; /* opaque user data pointer */ +}; + +static size_t +hasher (void const *x, size_t table_size) +{ + struct pda_entry const *p = x; + return hash_pjw (p->key, table_size); +} + +static bool +comparator (void const *x, void const *y) +{ + struct pda_entry const *a = x; + struct pda_entry const *b = y; + return STREQ (a->key, b->key); +} + +static void +freer (void *x) +{ + if (x) { + struct pda_entry *p = x; + free (p->key); + free (p); + } +} + void -guestfs_set_log_message_callback (guestfs_h *g, - guestfs_log_message_cb cb, void *opaque) +guestfs_set_private (guestfs_h *g, const char *key, void *data) +{ + if (g->pda == NULL) { + g->pda = hash_initialize (16, NULL, hasher, comparator, freer); + if (g->pda == NULL) + g->abort_cb (); + } + + struct pda_entry *new_entry = safe_malloc (g, sizeof *new_entry); + new_entry->key = safe_strdup (g, key); + new_entry->data = data; + + struct pda_entry *old_entry = hash_delete (g->pda, new_entry); + freer (old_entry); + + struct pda_entry *entry = hash_insert (g->pda, new_entry); + if (entry == NULL) + g->abort_cb (); + assert (entry == new_entry); +} + +static inline char * +bad_cast (char const *s) +{ + return (char *) s; +} + +void * +guestfs_get_private (guestfs_h *g, const char *key) { - g->log_message_cb = cb; - g->log_message_cb_data = opaque; + if (g->pda == NULL) + return NULL; /* no keys have been set */ + + const struct pda_entry k = { .key = bad_cast (key) }; + struct pda_entry *entry = hash_lookup (g->pda, &k); + if (entry) + return entry->data; + else + 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). + */ void -guestfs_set_subprocess_quit_callback (guestfs_h *g, - guestfs_subprocess_quit_cb cb, void *opaque) +guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size) { - g->subprocess_quit_cb = cb; - g->subprocess_quit_cb_data = opaque; + size_t i; + size_t orig_size = buf_size; + + if (buf_size > 256) + buf_size = 256; + + fputc ('"', out); + + for (i = 0; i < buf_size; ++i) { + if (c_isprint (buf[i])) + fputc (buf[i], out); + else + fprintf (out, "\\x%02x", (unsigned char) buf[i]); + } + + fputc ('"', out); + + if (orig_size > buf_size) + fprintf (out, + _(""), orig_size); } void -guestfs_set_launch_done_callback (guestfs_h *g, - guestfs_launch_done_cb cb, void *opaque) +guestfs___print_BufferOut (FILE *out, const char *buf, size_t buf_size) { - g->launch_done_cb = cb; - g->launch_done_cb_data = opaque; + guestfs___print_BufferIn (out, buf, buf_size); } void -guestfs_set_close_callback (guestfs_h *g, - guestfs_close_cb cb, void *opaque) +guestfs___free_string_list (char **argv) { - g->close_cb = cb; - g->close_cb_data = opaque; + size_t i; + for (i = 0; argv[i] != NULL; ++i) + free (argv[i]); + free (argv); }