X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=7f0f8214a89951b701f98c3673a19c10b118d42d;hp=6de631ff53db97a798e417dae1046a02dda2937e;hb=f4299f7ea55c4bbc9302e102d2fc801829e75ef6;hpb=d134143b55ecb5f7e6f74318acbf04f9e1370af6 diff --git a/src/guestfs.c b/src/guestfs.c index 6de631f..7f0f821 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -19,7 +19,7 @@ #include #define _BSD_SOURCE /* for mkdtemp, usleep */ -#define _GNU_SOURCE /* for vasprintf, GNU strerror_r */ +#define _GNU_SOURCE /* for vasprintf, GNU strerror_r, strchrnul */ #include #include @@ -58,15 +58,18 @@ static void error (guestfs_h *g, const char *fs, ...); static void perrorf (guestfs_h *g, const char *fs, ...); -static void *safe_malloc (guestfs_h *g, int nbytes); +static void *safe_malloc (guestfs_h *g, size_t nbytes); static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes); static char *safe_strdup (guestfs_h *g, const char *str); +static void *safe_memdup (guestfs_h *g, void *ptr, size_t size); static void default_error_cb (guestfs_h *g, void *data, const char *msg); static void stdout_event (void *data, int watch, int fd, int events); static void sock_read_event (void *data, int watch, int fd, int events); static void sock_write_event (void *data, int watch, int fd, int events); +static void close_handles (void); + static int select_add_handle (guestfs_h *g, int fd, int events, guestfs_handle_event_cb cb, void *data); static int select_remove_handle (guestfs_h *g, int watch); static int select_add_timeout (guestfs_h *g, int interval, guestfs_handle_timeout_cb cb, void *data); @@ -95,6 +98,8 @@ enum state { CONFIG, LAUNCHING, READY, BUSY, NO_HANDLE }; struct guestfs_h { + struct guestfs_h *next; /* Linked list of open handles. */ + /* State: see the state machine diagram in the man page guestfs(3). */ enum state state; @@ -112,6 +117,9 @@ struct guestfs_h int cmdline_size; int verbose; + int autosync; + + const char *path; /* Callbacks. */ guestfs_abort_cb abort_cb; @@ -144,6 +152,9 @@ struct guestfs_h int msg_next_serial; }; +static guestfs_h *handles = NULL; +static int atexit_handler_set = 0; + guestfs_h * guestfs_create (void) { @@ -170,11 +181,30 @@ guestfs_create (void) str = getenv ("LIBGUESTFS_DEBUG"); g->verbose = str != NULL && strcmp (str, "1") == 0; + str = getenv ("LIBGUESTFS_PATH"); + g->path = str != NULL ? str : GUESTFS_DEFAULT_PATH; + /* XXX We should probably make QEMU configurable as well. */ + /* Start with large serial numbers so they are easy to spot * inside the protocol. */ g->msg_next_serial = 0x00123400; + /* Link the handles onto a global list. This is the one area + * where the library needs to be made thread-safe. (XXX) + */ + /* acquire mutex (XXX) */ + g->next = handles; + handles = g; + if (!atexit_handler_set) { + atexit (close_handles); + atexit_handler_set = 1; + } + /* release mutex (XXX) */ + + if (g->verbose) + fprintf (stderr, "new guestfs handle %p\n", g); + return g; } @@ -183,6 +213,7 @@ 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 ... */ @@ -190,6 +221,13 @@ guestfs_close (guestfs_h *g) return; } + if (g->verbose) + fprintf (stderr, "closing guestfs handle %p (state %d)\n", g, g->state); + + /* Try to sync if autosync flag is set. */ + if (g->autosync && g->state == READY) + guestfs_sync (g); + /* Remove any handlers that might be called back before we kill the * subprocess. */ @@ -216,9 +254,26 @@ guestfs_close (guestfs_h *g) /* Mark the handle as dead before freeing it. */ g->state = NO_HANDLE; + /* acquire mutex (XXX) */ + if (handles == g) + handles = g->next; + else { + for (gg = handles; gg->next != g; gg = gg->next) + ; + gg->next = g->next; + } + /* release mutex (XXX) */ + free (g); } +/* Close all open handles (called from atexit(3)). */ +static void +close_handles (void) +{ + while (handles) guestfs_close (handles); +} + static void default_error_cb (guestfs_h *g, void *data, const char *msg) { @@ -274,7 +329,7 @@ perrorf (guestfs_h *g, const char *fs, ...) } static void * -safe_malloc (guestfs_h *g, int nbytes) +safe_malloc (guestfs_h *g, size_t nbytes) { void *ptr = malloc (nbytes); if (!ptr) g->abort_cb (); @@ -297,6 +352,15 @@ safe_strdup (guestfs_h *g, const char *str) return s; } +static void * +safe_memdup (guestfs_h *g, void *ptr, size_t size) +{ + void *p = malloc (size); + if (!p) g->abort_cb (); + memcpy (p, ptr, size); + return p; +} + void guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb cb) { @@ -323,10 +387,11 @@ guestfs_get_error_handler (guestfs_h *g, void **data_rtn) return g->error_cb; } -void +int guestfs_set_verbose (guestfs_h *g, int v) { - g->verbose = v; + g->verbose = !!v; + return 0; } int @@ -335,6 +400,35 @@ guestfs_get_verbose (guestfs_h *g) return g->verbose; } +int +guestfs_set_autosync (guestfs_h *g, int a) +{ + g->autosync = !!a; + return 0; +} + +int +guestfs_get_autosync (guestfs_h *g) +{ + return g->autosync; +} + +int +guestfs_set_path (guestfs_h *g, const char *path) +{ + if (path == NULL) + g->path = GUESTFS_DEFAULT_PATH; + else + g->path = path; + return 0; +} + +const char * +guestfs_get_path (guestfs_h *g) +{ + return g->path; +} + /* Add a string to the current command line. */ static void incr_cmdline_size (guestfs_h *g) @@ -407,6 +501,11 @@ guestfs_add_drive (guestfs_h *g, const char *filename) return -1; } + if (access (filename, F_OK) == -1) { + perrorf (g, "%s", filename); + return -1; + } + snprintf (buf, len, "file=%s", filename); return guestfs_config (g, "-drive", buf); @@ -420,6 +519,11 @@ guestfs_add_cdrom (guestfs_h *g, const char *filename) return -1; } + if (access (filename, F_OK) == -1) { + perrorf (g, "%s", filename); + return -1; + } + return guestfs_config (g, "-cdrom", filename); } @@ -427,19 +531,16 @@ int guestfs_launch (guestfs_h *g) { static const char *dir_template = "/tmp/libguestfsXXXXXX"; - int r, i; + int r, i, len, pmore; int wfd[2], rfd[2]; int tries; - /*const char *qemu = QEMU;*/ /* XXX */ - const char *qemu = "/usr/bin/qemu-system-x86_64"; - const char *kernel = "vmlinuz.fedora-10.x86_64"; - const char *initrd = "initramfs.fedora-10.x86_64.img"; + const char *kernel_name = "vmlinuz." REPO "." host_cpu; + const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; + char *path, *pelem, *pend; + char *kernel = NULL, *initrd = NULL; char unixsock[256]; struct sockaddr_un addr; - /* XXX Choose which qemu to run. */ - /* XXX Choose initrd, etc. */ - /* Configured? */ if (!g->cmdline) { error (g, "you must call guestfs_add_drive before guestfs_launch"); @@ -451,12 +552,59 @@ guestfs_launch (guestfs_h *g) return -1; } + /* Search g->path for the kernel and initrd. */ + pelem = path = safe_strdup (g, g->path); + do { + pend = strchrnul (pelem, ':'); + pmore = *pend == ':'; + *pend = '\0'; + len = pend - pelem; + + /* Empty element or "." means cwd. */ + if (len == 0 || (len == 1 && *pelem == '.')) { + if (g->verbose) + fprintf (stderr, + "looking for kernel and initrd in current directory\n"); + if (access (kernel_name, F_OK) == 0 && access (initrd_name, F_OK) == 0) { + kernel = safe_strdup (g, kernel_name); + initrd = safe_strdup (g, initrd_name); + break; + } + } + /* Look at /kernel etc. */ + else { + kernel = safe_malloc (g, len + strlen (kernel_name) + 2); + initrd = safe_malloc (g, len + strlen (initrd_name) + 2); + sprintf (kernel, "%s/%s", pelem, kernel_name); + sprintf (initrd, "%s/%s", pelem, initrd_name); + + if (g->verbose) + fprintf (stderr, "looking for %s and %s\n", kernel, initrd); + + if (access (kernel, F_OK) == 0 && access (initrd, F_OK) == 0) + break; + free (kernel); + free (initrd); + kernel = initrd = NULL; + } + + pelem = pend + 1; + } while (pmore); + + free (path); + + if (kernel == NULL || initrd == NULL) { + error (g, "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)", + kernel_name, initrd_name, g->path); + goto cleanup0; + } + /* Make the temporary directory containing the socket. */ if (!g->tmpdir) { g->tmpdir = safe_strdup (g, dir_template); if (mkdtemp (g->tmpdir) == NULL) { perrorf (g, "%s: cannot create temporary directory", dir_template); - return -1; + goto cleanup0; } } @@ -465,7 +613,7 @@ guestfs_launch (guestfs_h *g) if (pipe (wfd) == -1 || pipe (rfd) == -1) { perrorf (g, "pipe"); - return -1; + goto cleanup0; } r = fork (); @@ -475,7 +623,7 @@ guestfs_launch (guestfs_h *g) close (wfd[1]); close (rfd[0]); close (rfd[1]); - return -1; + goto cleanup0; } if (r == 0) { /* Child (qemu). */ @@ -485,7 +633,7 @@ guestfs_launch (guestfs_h *g) /* Set up the full command line. Do this in the subprocess so we * don't need to worry about cleaning up. */ - g->cmdline[0] = (char *) qemu; + g->cmdline[0] = (char *) QEMU; /* Construct the -net channel parameter for qemu. */ snprintf (vmchannel, sizeof vmchannel, @@ -497,7 +645,8 @@ guestfs_launch (guestfs_h *g) "console=ttyS0 guestfs=%s:%d", VMCHANNEL_ADDR, VMCHANNEL_PORT); add_cmdline (g, "-m"); - add_cmdline (g, "384"); /* XXX Choose best size. */ + add_cmdline (g, "384"); /* XXX Choose best size. */ + add_cmdline (g, "-no-kqemu"); /* Avoids a warning. */ add_cmdline (g, "-kernel"); add_cmdline (g, (char *) kernel); add_cmdline (g, "-initrd"); @@ -517,7 +666,7 @@ guestfs_launch (guestfs_h *g) g->cmdline[g->cmdline_size-1] = NULL; if (g->verbose) { - fprintf (stderr, "%s", qemu); + fprintf (stderr, "%s", QEMU); for (i = 0; g->cmdline[i]; ++i) fprintf (stderr, " %s", g->cmdline[i]); fprintf (stderr, "\n"); @@ -530,6 +679,8 @@ guestfs_launch (guestfs_h *g) close (rfd[0]); dup (wfd[0]); dup (rfd[1]); + close (wfd[0]); + close (rfd[1]); #if 0 /* Set up a new process group, so we can signal this process @@ -538,8 +689,8 @@ guestfs_launch (guestfs_h *g) setpgid (0, 0); #endif - execv (qemu, g->cmdline); /* Run qemu. */ - perror (qemu); + execv (QEMU, g->cmdline); /* Run qemu. */ + perror (QEMU); _exit (1); } @@ -656,6 +807,10 @@ guestfs_launch (guestfs_h *g) g->start_t = 0; g->stdout_watch = -1; g->sock_watch = -1; + + cleanup0: + free (kernel); + free (initrd); return -1; } @@ -716,7 +871,7 @@ guestfs_kill_subprocess (guestfs_h *g) } if (g->verbose) - fprintf (stderr, "sending SIGTERM to process group %d\n", g->pid); + fprintf (stderr, "sending SIGTERM to process %d\n", g->pid); kill (g->pid, SIGTERM); @@ -877,6 +1032,28 @@ sock_read_event (void *data, int watch, int fd, int events) goto cleanup; } + /* Got the full message, begin processing it. */ + if (g->verbose) { + int i, j; + + for (i = 0; i < g->msg_in_size; i += 16) { + printf ("%04x: ", i); + for (j = i; j < MIN (i+16, g->msg_in_size); ++j) + printf ("%02x ", (unsigned char) g->msg_in[j]); + for (; j < i+16; ++j) + printf (" "); + printf ("|"); + for (j = i; j < MIN (i+16, g->msg_in_size); ++j) + if (isprint (g->msg_in[j])) + printf ("%c", g->msg_in[j]); + else + printf ("."); + for (; j < i+16; ++j) + printf (" "); + printf ("|\n"); + } + } + /* Not in the expected state. */ if (g->state != BUSY) error (g, "state %d != BUSY", g->state); @@ -1052,11 +1229,68 @@ dispatch (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args) return -1; } +/* Check the return message from a call for validity. */ +static int +check_reply_header (guestfs_h *g, + const struct guestfs_message_header *hdr, + int proc_nr, int serial) +{ + if (hdr->prog != GUESTFS_PROGRAM) { + error (g, "wrong program (%d/%d)", hdr->prog, GUESTFS_PROGRAM); + return -1; + } + if (hdr->vers != GUESTFS_PROTOCOL_VERSION) { + error (g, "wrong protocol version (%d/%d)", + hdr->vers, GUESTFS_PROTOCOL_VERSION); + return -1; + } + if (hdr->direction != GUESTFS_DIRECTION_REPLY) { + error (g, "unexpected message direction (%d/%d)", + hdr->direction, GUESTFS_DIRECTION_REPLY); + return -1; + } + if (hdr->proc != proc_nr) { + error (g, "unexpected procedure number (%d/%d)", hdr->proc, proc_nr); + return -1; + } + if (hdr->serial != serial) { + error (g, "unexpected serial (%d/%d)", hdr->serial, serial); + return -1; + } + + return 0; +} + /* The high-level actions are autogenerated by generator.ml. Include * them here. */ #include "guestfs-actions.c" +/* Structure-freeing functions. These rely on the fact that the + * structure format is identical to the XDR format. See note in + * generator.ml. + */ +void +guestfs_free_lvm_pv_list (struct guestfs_lvm_pv_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_pv_list, (char *) x); + free (x); +} + +void +guestfs_free_lvm_vg_list (struct guestfs_lvm_vg_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_vg_list, (char *) x); + free (x); +} + +void +guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_lv_list, (char *) x); + free (x); +} + /* This is the default main loop implementation, using select(2). */ struct handle_cb_data {