2 * Copyright (C) 2009 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #define _BSD_SOURCE /* for mkdtemp, usleep */
36 #ifdef HAVE_SYS_TYPES_H
37 #include <sys/types.h>
40 #ifdef HAVE_SYS_WAIT_H
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
54 static int error (guestfs_h *g, const char *fs, ...);
55 static int perrorf (guestfs_h *g, const char *fs, ...);
56 static void *safe_malloc (guestfs_h *g, int nbytes);
57 static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes);
58 static char *safe_strdup (guestfs_h *g, const char *str);
60 #define VMCHANNEL_PORT 6666
61 #define VMCHANNEL_ADDR "10.0.2.4"
63 /* GuestFS handle and connection. */
66 /* All these socks/pids are -1 if not connected. */
67 int sock; /* Daemon communications socket. */
68 int pid; /* Qemu PID. */
69 time_t start_t; /* The time when we started qemu. */
70 int daemon_up; /* Received hello message from daemon. */
72 char *tmpdir; /* Temporary directory containing logfile
73 * and socket. Cleaned up unless there is
77 char **cmdline; /* Qemu command line. */
80 guestfs_abort_fn abort_fn;
90 g = malloc (sizeof (*g));
101 g->abort_fn = abort; /* Have to set these before safe_malloc. */
102 g->exit_on_error = 0;
103 g->verbose = getenv ("LIBGUESTFS_VERBOSE") != NULL;
105 g->cmdline = safe_malloc (g, sizeof (char *) * 1);
107 g->cmdline[0] = NULL; /* This is chosen by guestfs_launch. */
113 guestfs_free (guestfs_h *g)
118 if (g->pid) guestfs_kill_subprocess (g);
120 /* The assumption is that programs calling this have successfully
121 * used qemu, so delete the logfile and socket directory.
124 snprintf (filename, sizeof filename, "%s/sock", g->tmpdir);
127 snprintf (filename, sizeof filename, "%s/qemu.log", g->tmpdir);
135 for (i = 0; i < g->cmdline_size; ++i)
136 free (g->cmdline[i]);
142 /* Cleanup fds and sockets, assuming the subprocess is dead already. */
144 cleanup_fds (guestfs_h *g)
146 if (g->sock >= 0) close (g->sock);
150 /* Wait for subprocess to exit. */
152 wait_subprocess (guestfs_h *g)
154 if (g->pid >= 0) waitpid (g->pid, NULL, 0);
159 error (guestfs_h *g, const char *fs, ...)
163 fprintf (stderr, "libguestfs: ");
165 vfprintf (stderr, fs, args);
167 fputc ('\n', stderr);
169 if (g->exit_on_error) {
170 guestfs_kill_subprocess (g);
177 perrorf (guestfs_h *g, const char *fs, ...)
183 fprintf (stderr, "libguestfs: ");
185 vfprintf (stderr, fs, args);
187 strerror_r (err, buf, sizeof buf);
188 fprintf (stderr, ": %s\n", buf);
190 if (g->exit_on_error) {
191 guestfs_kill_subprocess (g);
198 safe_malloc (guestfs_h *g, int nbytes)
200 void *ptr = malloc (nbytes);
201 if (!ptr) g->abort_fn ();
206 safe_realloc (guestfs_h *g, void *ptr, int nbytes)
208 void *p = realloc (ptr, nbytes);
209 if (!p) g->abort_fn ();
214 safe_strdup (guestfs_h *g, const char *str)
216 char *s = strdup (str);
217 if (!s) g->abort_fn ();
222 guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_fn a)
228 guestfs_get_out_of_memory_handler (guestfs_h *g)
234 guestfs_set_exit_on_error (guestfs_h *g, int e)
236 g->exit_on_error = e;
240 guestfs_get_exit_on_error (guestfs_h *g)
242 return g->exit_on_error;
246 guestfs_set_verbose (guestfs_h *g, int v)
252 guestfs_get_verbose (guestfs_h *g)
257 /* Add an escaped string to the current command line. */
259 add_cmdline (guestfs_h *g, const char *str)
262 return error (g, "command line cannot be altered after qemu subprocess launched");
265 g->cmdline = safe_realloc (g, g->cmdline, sizeof (char *) * g->cmdline_size);
266 g->cmdline[g->cmdline_size-1] = safe_strdup (g, str);
272 guestfs_config (guestfs_h *g,
273 const char *qemu_param, const char *qemu_value)
275 if (qemu_param[0] != '-')
276 return error (g, "guestfs_config: parameter must begin with '-' character");
278 /* A bit fascist, but the user will probably break the extra
279 * parameters that we add if they try to set any of these.
281 if (strcmp (qemu_param, "-kernel") == 0 ||
282 strcmp (qemu_param, "-initrd") == 0 ||
283 strcmp (qemu_param, "-nographic") == 0 ||
284 strcmp (qemu_param, "-serial") == 0 ||
285 strcmp (qemu_param, "-vnc") == 0 ||
286 strcmp (qemu_param, "-full-screen") == 0 ||
287 strcmp (qemu_param, "-std-vga") == 0 ||
288 strcmp (qemu_param, "-vnc") == 0)
289 return error (g, "guestfs_config: parameter '%s' isn't allowed");
291 if (add_cmdline (g, qemu_param) != 0) return -1;
293 if (qemu_value != NULL) {
294 if (add_cmdline (g, qemu_value) != 0) return -1;
301 guestfs_add_drive (guestfs_h *g, const char *filename)
303 int len = strlen (filename) + 64;
306 if (strchr (filename, ',') != NULL)
307 return error (g, "filename cannot contain ',' (comma) character");
309 snprintf (buf, len, "file=%s,media=disk", filename);
311 return guestfs_config (g, "-drive", buf);
315 guestfs_add_cdrom (guestfs_h *g, const char *filename)
317 int len = strlen (filename) + 64;
320 if (strchr (filename, ',') != NULL)
321 return error (g, "filename cannot contain ',' (comma) character");
323 snprintf (buf, len, "file=%s,if=ide,index=1,media=cdrom", filename);
325 return guestfs_config (g, "-drive", buf);
329 guestfs_launch (guestfs_h *g)
331 static const char *dir_template = "/tmp/libguestfsXXXXXX";
333 /*const char *qemu = QEMU;*/ /* XXX */
334 const char *qemu = "/home/rjones/d/redhat/libguestfs/qemu";
335 const char *kernel = "/boot/vmlinuz-2.6.27.15-170.2.24.fc10.x86_64";
336 const char *initrd = "/tmp/initrd-2.6.27.15-170.2.24.fc10.x86_64.img";
339 /* XXX Choose which qemu to run. */
340 /* XXX Choose initrd, etc. */
342 /* Make the temporary directory containing the logfile and socket. */
344 g->tmpdir = safe_strdup (g, dir_template);
345 if (mkdtemp (g->tmpdir) == NULL)
346 return perrorf (g, "%s: cannot create temporary directory", dir_template);
348 snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir);
353 return perrorf (g, "fork");
355 if (r > 0) { /* Parent (library). */
358 /* If qemu is going to die during startup, give it a tiny amount
359 * of time to print the error message.
363 /* Start the clock ... */
366 else { /* Child (qemu). */
371 /* Set up the full command line. Do this in the subprocess so we
372 * don't need to worry about cleaning up.
374 g->cmdline[0] = (char *) qemu;
377 realloc (g->cmdline, sizeof (char *) * (g->cmdline_size + 16));
378 if (g->cmdline == NULL) {
383 /* Construct the -net channel parameter for qemu. */
384 snprintf (vmchannel, sizeof vmchannel,
385 "channel,%d:unix:%s,server,nowait", VMCHANNEL_PORT, unixsock);
387 /* Linux kernel command line. */
388 snprintf (append, sizeof append,
389 "console=ttyS0 guestfs=%s:%d", VMCHANNEL_ADDR, VMCHANNEL_PORT);
393 g->cmdline[g->cmdline_size ] = "-kernel";
394 g->cmdline[g->cmdline_size+ 1] = (char *) kernel;
395 g->cmdline[g->cmdline_size+ 2] = "-initrd";
396 g->cmdline[g->cmdline_size+ 3] = (char *) initrd;
397 g->cmdline[g->cmdline_size+ 4] = "-append";
398 g->cmdline[g->cmdline_size+ 5] = append;
399 g->cmdline[g->cmdline_size+ 6] = "-nographic";
400 g->cmdline[g->cmdline_size+ 7] = "-serial";
401 g->cmdline[g->cmdline_size+ 8] = "stdio";
402 g->cmdline[g->cmdline_size+ 9] = "-net";
403 g->cmdline[g->cmdline_size+10] = vmchannel;
404 g->cmdline[g->cmdline_size+11] = "-net";
405 g->cmdline[g->cmdline_size+12] = "user,vlan=0";
406 g->cmdline[g->cmdline_size+13] = "-net";
407 g->cmdline[g->cmdline_size+14] = "nic,vlan=0";
408 g->cmdline[g->cmdline_size+15] = NULL;
411 fprintf (stderr, "Running %s", qemu);
412 for (i = 0; g->cmdline[i]; ++i)
413 fprintf (stderr, " %s", g->cmdline[i]);
414 fprintf (stderr, "\n");
417 /* Set up stdin, stdout. Messages should go to the logfile. */
420 open ("/dev/null", O_RDONLY);
421 snprintf (logfile, sizeof logfile, "%s/qemu.log", g->tmpdir);
422 open (logfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
425 /* Set up a new process group, so we can signal this process
426 * and all subprocesses (eg. if qemu is really a shell script).
430 execv (qemu, g->cmdline); /* Run qemu. */
438 /* A peculiarity of qemu's vmchannel implementation is that both sides
439 * connect to qemu, ie:
441 * libguestfs --- connect --> qemu <-- connect --- daemon
444 * This has several implications: (1) qemu creates the Unix socket, so
445 * we have to wait for it to do that. (2) we have to arrange for the
446 * daemon to send a "hello" message which we also wait for.
448 * At any time during this, the qemu subprocess might run slowly, die
449 * or hang (it's very prone to just hanging if the BIOS fails for any
450 * reason or if the kernel cannot be found to boot from).
452 * The only realistic way to handle this is, unfortunately, using
453 * timeouts, also checking if the qemu subprocess is still alive.
455 * We could do better here by monitoring the Linux kernel log messages
456 * (via the serial console, which is currently just redirected to a
457 * log file) and seeing if the Linux guest is making progress. (XXX)
460 #define QEMU_SOCKET_TIMEOUT 5 /* How long we wait for qemu to make
461 * the socket. This should be very quick.
463 #define DAEMON_TIMEOUT 60 /* How long we wait for guest to boot
464 * and start the daemon. This could take
465 * a potentially long time, and is very
466 * sensitive to the overall load on the host.
469 static int wait_ready (guestfs_h *g);
472 guestfs_wait_ready (guestfs_h *g)
476 /* Launch the subprocess, if there isn't one already. */
478 if (guestfs_launch (g) != 0)
484 if (r == -1) { /* Error. */
485 guestfs_kill_subprocess (g);
488 else if (r > 0) { /* Keep waiting. */
492 else if (r == 0) /* Daemon is ready. */
499 #define UNIX_PATH_MAX 108
501 /* This function is called repeatedly until the qemu subprocess and
502 * daemon is ready. It returns:
504 * 0 : done, daemon is ready
505 * >0 : not ready, keep waiting
508 wait_ready (guestfs_h *g)
513 struct sockaddr_un addr;
516 if (g->pid == -1) abort (); /* Internal state error. */
518 /* Check the daemon is still around. */
519 r = waitpid (g->pid, NULL, WNOHANG);
521 if (r > 0 || (r == -1 && errno == ECHILD)) {
524 "qemu subprocess exited unexpectedly during initialization");
528 elapsed = difftime (now, g->start_t);
531 /* Create the socket. */
532 sock = socket (AF_UNIX, SOCK_STREAM, 0);
534 return perrorf (g, "socket");
536 addr.sun_family = AF_UNIX;
537 snprintf (addr.sun_path, UNIX_PATH_MAX, "%s/sock", g->tmpdir);
539 if (connect (sock, (struct sockaddr *) &addr, sizeof addr) == -1) {
540 if (elapsed <= QEMU_SOCKET_TIMEOUT) {
542 return 1; /* Keep waiting for the socket ... */
544 perrorf (g, "qemu process hanging before making vmchannel socket");
549 if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1) {
550 perrorf (g, "set socket non-blocking");
559 /* Wait for the daemon to say hello. */
561 r = read (g->sock, &m, 1);
567 error (g, "unexpected message from qemu vmchannel or daemon");
571 if (errno == EAGAIN) {
572 if (elapsed <= DAEMON_TIMEOUT)
573 return 1; /* Keep waiting for the daemon ... */
574 error (g, "timeout waiting for guest to become ready");
586 guestfs_kill_subprocess (guestfs_h *g)
590 fprintf (stderr, "sending SIGTERM to pgid %d\n", g->pid);
592 kill (- g->pid, SIGTERM);