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 */
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
39 #ifdef HAVE_SYS_WAIT_H
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
53 static int error (guestfs_h *g, const char *fs, ...);
54 static int perrorf (guestfs_h *g, const char *fs, ...);
55 static void *safe_malloc (guestfs_h *g, int nbytes);
56 static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes);
57 static char *safe_strdup (guestfs_h *g, const char *str);
59 /* GuestFS handle and connection. */
62 /* All these socks/pids are -1 if not connected. */
63 int sock; /* Daemon communications socket. */
64 int pid; /* Qemu PID. */
66 char *tmpdir; /* Temporary directory containing logfile
67 * and socket. Cleaned up unless there is
71 char **cmdline; /* Qemu command line. */
74 guestfs_abort_fn abort_fn;
84 g = malloc (sizeof (*g));
92 g->abort_fn = abort; /* Have to set these before safe_malloc. */
94 g->verbose = getenv ("LIBGUESTFS_VERBOSE") != NULL;
96 g->cmdline = safe_malloc (g, sizeof (char *) * 1);
98 g->cmdline[0] = NULL; /* This is chosen by guestfs_launch. */
104 guestfs_free (guestfs_h *g)
109 if (g->pid) guestfs_kill_subprocess (g);
111 /* The assumption is that programs calling this have successfully
112 * used qemu, so delete the logfile and socket directory.
115 snprintf (filename, sizeof filename, "%s/sock", g->tmpdir);
118 snprintf (filename, sizeof filename, "%s/qemu.log", g->tmpdir);
126 for (i = 0; i < g->cmdline_size; ++i)
127 free (g->cmdline[i]);
133 /* Cleanup fds and sockets, assuming the subprocess is dead already. */
135 cleanup_fds (guestfs_h *g)
137 if (g->sock >= 0) close (g->sock);
141 /* Wait for subprocess to exit. */
143 wait_subprocess (guestfs_h *g)
145 if (g->pid >= 0) waitpid (g->pid, NULL, 0);
150 error (guestfs_h *g, const char *fs, ...)
154 fprintf (stderr, "libguestfs: ");
156 vfprintf (stderr, fs, args);
158 fputc ('\n', stderr);
160 if (g->exit_on_error) exit (1);
165 perrorf (guestfs_h *g, const char *fs, ...)
171 fprintf (stderr, "libguestfs: ");
173 vfprintf (stderr, fs, args);
175 strerror_r (err, buf, sizeof buf);
176 fprintf (stderr, ": %s\n", buf);
178 if (g->exit_on_error) exit (1);
183 safe_malloc (guestfs_h *g, int nbytes)
185 void *ptr = malloc (nbytes);
186 if (!ptr) g->abort_fn ();
191 safe_realloc (guestfs_h *g, void *ptr, int nbytes)
193 void *p = realloc (ptr, nbytes);
194 if (!p) g->abort_fn ();
199 safe_strdup (guestfs_h *g, const char *str)
201 char *s = strdup (str);
202 if (!s) g->abort_fn ();
207 guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_fn a)
213 guestfs_get_out_of_memory_handler (guestfs_h *g)
219 guestfs_set_exit_on_error (guestfs_h *g, int e)
221 g->exit_on_error = e;
225 guestfs_get_exit_on_error (guestfs_h *g)
227 return g->exit_on_error;
231 guestfs_set_verbose (guestfs_h *g, int v)
237 guestfs_get_verbose (guestfs_h *g)
242 /* Add an escaped string to the current command line. */
244 add_cmdline (guestfs_h *g, const char *str)
247 return error (g, "command line cannot be altered after qemu subprocess launched");
250 g->cmdline = safe_realloc (g, g->cmdline, sizeof (char *) * g->cmdline_size);
251 g->cmdline[g->cmdline_size-1] = safe_strdup (g, str);
257 guestfs_config (guestfs_h *g,
258 const char *qemu_param, const char *qemu_value)
260 if (qemu_param[0] != '-')
261 return error (g, "guestfs_config: parameter must begin with '-' character");
263 /* A bit fascist, but the user will probably break the extra
264 * parameters that we add if they try to set any of these.
266 if (strcmp (qemu_param, "-kernel") == 0 ||
267 strcmp (qemu_param, "-initrd") == 0 ||
268 strcmp (qemu_param, "-nographic") == 0 ||
269 strcmp (qemu_param, "-serial") == 0 ||
270 strcmp (qemu_param, "-vnc") == 0 ||
271 strcmp (qemu_param, "-full-screen") == 0 ||
272 strcmp (qemu_param, "-std-vga") == 0 ||
273 strcmp (qemu_param, "-vnc") == 0)
274 return error (g, "guestfs_config: parameter '%s' isn't allowed");
276 if (add_cmdline (g, qemu_param) != 0) return -1;
278 if (qemu_value != NULL) {
279 if (add_cmdline (g, qemu_value) != 0) return -1;
286 guestfs_add_drive (guestfs_h *g, const char *filename)
288 int len = strlen (filename) + 64;
291 if (strchr (filename, ',') != NULL)
292 return error (g, "filename cannot contain ',' (comma) character");
294 snprintf (buf, len, "file=%s,media=disk", filename);
296 return guestfs_config (g, "-drive", buf);
300 guestfs_add_cdrom (guestfs_h *g, const char *filename)
302 int len = strlen (filename) + 64;
305 if (strchr (filename, ',') != NULL)
306 return error (g, "filename cannot contain ',' (comma) character");
308 snprintf (buf, len, "file=%s,if=ide,index=1,media=cdrom", filename);
310 return guestfs_config (g, "-drive", buf);
314 guestfs_launch (guestfs_h *g)
316 static const char *dir_template = "/tmp/libguestfsXXXXXX";
318 const char *qemu = QEMU; /* XXX */
319 const char *kernel = "/boot/vmlinuz-2.6.27.15-170.2.24.fc10.x86_64";
320 const char *initrd = "/boot/initrd-2.6.27.15-170.2.24.fc10.x86_64.img";
325 /* XXX Choose which qemu to run. */
326 /* XXX Choose initrd, etc. */
328 /* Make the temporary directory containing the logfile and socket. */
330 g->tmpdir = safe_strdup (g, dir_template);
331 if (mkdtemp (g->tmpdir) == NULL)
332 return perrorf (g, "%s: cannot create temporary directory", dir_template);
334 snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir);
339 return perrorf (g, "fork");
341 if (r > 0) { /* Parent (library). */
344 /* If qemu is going to die during startup, give it a tiny amount of
345 * time to print the error message.
348 } else { /* Child (qemu). */
349 /* Set up the full command line. Do this in the subprocess so we
350 * don't need to worry about cleaning up.
352 g->cmdline[0] = (char *) qemu;
354 g->cmdline = realloc (g->cmdline, sizeof (char *) * (g->cmdline_size + 14));
355 if (g->cmdline == NULL) {
360 snprintf (vmchannel, sizeof vmchannel,
361 "channel,%d:unix:%s,server,nowait", 666, unixsock);
363 g->cmdline[g->cmdline_size ] = "-kernel";
364 g->cmdline[g->cmdline_size+ 1] = (char *) kernel;
365 g->cmdline[g->cmdline_size+ 2] = "-initrd";
366 g->cmdline[g->cmdline_size+ 3] = (char *) initrd;
367 g->cmdline[g->cmdline_size+ 4] = "-append";
368 g->cmdline[g->cmdline_size+ 5] = "console=ttyS0";
369 g->cmdline[g->cmdline_size+ 6] = "-nographic";
370 g->cmdline[g->cmdline_size+ 7] = "-serial";
371 g->cmdline[g->cmdline_size+ 8] = "stdio";
372 g->cmdline[g->cmdline_size+ 9] = "-net";
373 g->cmdline[g->cmdline_size+10] = vmchannel;
374 g->cmdline[g->cmdline_size+11] = "-net";
375 g->cmdline[g->cmdline_size+12] = "user,vlan0";
376 g->cmdline[g->cmdline_size+13] = NULL;
379 fprintf (stderr, "Running %s", qemu);
380 for (i = 0; g->cmdline[i]; ++i)
381 fprintf (stderr, " %s", g->cmdline[i]);
382 fprintf (stderr, "\n");
385 /* Set up stdin, stdout. Messages should go to the logfile. */
388 open ("/dev/null", O_RDONLY);
389 snprintf (tmpfile, sizeof tmpfile, "%s/qemu.log", g->tmpdir);
390 open (tmpfile, O_WRONLY|O_CREAT|O_APPEND, 0644);
393 execv (qemu, g->cmdline); /* Run qemu. */
401 #define UNIX_PATH_MAX 108
404 guestfs_wait_ready (guestfs_h *g)
407 struct sockaddr_un addr;
409 if (guestfs_ready (g)) return 0;
411 /* Launch the subprocess, if there isn't one already. */
413 if (guestfs_launch (g) != 0)
422 lsock = socket (AF_UNIX, SOCK_STREAM, 0);
424 return perrorf (g, "socket");
426 addr.sun_family = AF_UNIX;
427 snprintf (addr.sun_path, UNIX_PATH_MAX, "%s/sock", g->tmpdir);
429 if (bind (lsock, (struct sockaddr *) &addr, sizeof addr) == -1) {
435 if (listen (lsock, 1) == -1) {
436 perrorf (g, "listen");
441 if (fcntl (lsock, F_SETFL, O_NONBLOCK) == -1) {
442 perrorf (g, "set socket non-blocking");
447 /* Wait until the daemon running inside the guest connects to the
448 * Unix socket, which indicates it's alive. Qemu might exit in the
449 * meantime if there is a problem. More problematically qemu might
450 * hang, which we can only detect by timeout.
452 for (i = 0; i < 30; ++i) {
453 r = waitpid (g->pid, NULL, WNOHANG);
455 if (r > 0 || (r == -1 && errno == ECHILD)) {
456 error (g, "qemu subprocess exited unexpectedly during initialization");
463 r = accept (lsock, NULL, 0);
466 fcntl (g->sock, F_SETFL, O_NONBLOCK);
470 if (errno == EAGAIN) {
474 perrorf (g, "accept");
476 guestfs_kill_subprocess (g);
481 return error (g, "timeout waiting for guest to become ready");
485 guestfs_ready (guestfs_h *g)
489 kill (g->pid, 0) == 0 &&
491 guestfs_ping_daemon (g) >= 0 */;
495 guestfs_kill_subprocess (guestfs_h *g)
499 fprintf (stderr, "sending SIGINT to pid %d\n", g->pid);
501 kill (g->pid, SIGINT);