X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=9f7348665966f961698041380ab4b4bd05767ab1;hp=79fc5bfabafd8b73b8325d145f2f118aad5fe707;hb=690ff694ca6db586c06ec484ea158261c156aa2f;hpb=d2636b0984830d950423dadd936114ff242c4a00 diff --git a/src/guestfs.c b/src/guestfs.c index 79fc5bf..9f73486 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 @@ -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 @@ -143,6 +143,9 @@ guestfs_create (void) */ g->msg_next_serial = 0x00123400; + /* Default is uniprocessor appliance. */ + g->smp = 1; + /* Link the handles onto a global list. */ gl_lock_lock (handles_lock); g->next = handles; @@ -153,8 +156,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,37 +171,42 @@ 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_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); + guestfs___free_drives (&g->drives); + /* Close sockets. */ if (g->fd[0] >= 0) close (g->fd[0]); @@ -212,20 +219,16 @@ 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. */ + guestfs___remove_tmpdir (g->tmpdir); + free (g->tmpdir); if (g->cmdline) { + size_t i; + for (i = 0; i < g->cmdline_size; ++i) free (g->cmdline[i]); free (g->cmdline); @@ -238,6 +241,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; @@ -282,6 +287,81 @@ 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); + + free (msg); +} + +/* 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) { @@ -322,14 +402,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, ": "); @@ -467,6 +541,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) { @@ -645,44 +725,80 @@ 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; + } + + return 0; } -void -guestfs_set_subprocess_quit_callback (guestfs_h *g, - guestfs_subprocess_quit_cb cb, void *opaque) +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->subprocess_quit_cb = cb; - g->subprocess_quit_cb_data = opaque; + g->pgroup = !!v; + return 0; } -void -guestfs_set_launch_done_callback (guestfs_h *g, - guestfs_launch_done_cb cb, void *opaque) +int +guestfs__get_pgroup (guestfs_h *g) { - g->launch_done_cb = cb; - g->launch_done_cb_data = opaque; + return g->pgroup; } -void -guestfs_set_close_callback (guestfs_h *g, - guestfs_close_cb cb, void *opaque) +int +guestfs__set_smp (guestfs_h *g, int v) { - g->close_cb = cb; - g->close_cb_data = opaque; + if (v >= 1) { + g->smp = v; + return 0; + } else { + error (g, "invalid smp parameter: %d", v); + return -1; + } } -void -guestfs_set_progress_callback (guestfs_h *g, - guestfs_progress_cb cb, void *opaque) +int +guestfs__get_smp (guestfs_h *g) { - g->progress_cb = cb; - g->progress_cb_data = opaque; + return g->smp; } /* Note the private data area is allocated lazily, since the vast @@ -761,6 +877,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). */ @@ -803,3 +960,22 @@ guestfs___free_string_list (char **argv) free (argv[i]); free (argv); } + +void +guestfs___free_drives (struct drive **drives) +{ + struct drive *i = *drives; + *drives = NULL; + + while (i != NULL) { + struct drive *next = i->next; + + free (i->path); + free (i->format); + free (i->iface); + free (i->name); + free (i); + + i = next; + } +}