Running qemu as a subprocess.
[libguestfs.git] / src / guestfs.c
1 /* libguestfs
2  * Copyright (C) 2009 Red Hat Inc. 
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include <config.h>
20
21 #define _BSD_SOURCE /* for mkdtemp, usleep */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <fcntl.h>
30
31 #ifdef HAVE_ERRNO_H
32 #include <errno.h>
33 #endif
34
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38
39 #ifdef HAVE_SYS_WAIT_H
40 #include <sys/wait.h>
41 #endif
42
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46
47 #ifdef HAVE_SYS_UN_H
48 #include <sys/un.h>
49 #endif
50
51 #include "guestfs.h"
52
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);
58
59 /* GuestFS handle and connection. */
60 struct guestfs_h
61 {
62   /* All these socks/pids are -1 if not connected. */
63   int sock;                     /* Daemon communications socket. */
64   int pid;                      /* Qemu PID. */
65
66   char *tmpdir;                 /* Temporary directory containing logfile
67                                  * and socket.  Cleaned up unless there is
68                                  * an error.
69                                  */
70
71   char **cmdline;               /* Qemu command line. */
72   int cmdline_size;
73
74   guestfs_abort_fn abort_fn;
75   int exit_on_error;
76   int verbose;
77 };
78
79 guestfs_h *
80 guestfs_create (void)
81 {
82   guestfs_h *g;
83
84   g = malloc (sizeof (*g));
85   if (!g) return NULL;
86
87   g->sock = -1;
88   g->pid = -1;
89
90   g->tmpdir = NULL;
91
92   g->abort_fn = abort;          /* Have to set these before safe_malloc. */
93   g->exit_on_error = 0;
94   g->verbose = getenv ("LIBGUESTFS_VERBOSE") != NULL;
95
96   g->cmdline = safe_malloc (g, sizeof (char *) * 1);
97   g->cmdline_size = 1;
98   g->cmdline[0] = NULL;         /* This is chosen by guestfs_launch. */
99
100   return g;
101 }
102
103 void
104 guestfs_free (guestfs_h *g)
105 {
106   int i;
107   char filename[256];
108
109   if (g->pid) guestfs_kill_subprocess (g);
110
111   /* The assumption is that programs calling this have successfully
112    * used qemu, so delete the logfile and socket directory.
113    */
114   if (g->tmpdir) {
115     snprintf (filename, sizeof filename, "%s/sock", g->tmpdir);
116     unlink (filename);
117
118     snprintf (filename, sizeof filename, "%s/qemu.log", g->tmpdir);
119     unlink (filename);
120
121     rmdir (g->tmpdir);
122
123     free (g->tmpdir);
124   }
125
126   for (i = 0; i < g->cmdline_size; ++i)
127     free (g->cmdline[i]);
128   free (g->cmdline);
129
130   free (g);
131 }
132
133 /* Cleanup fds and sockets, assuming the subprocess is dead already. */
134 static void
135 cleanup_fds (guestfs_h *g)
136 {
137   if (g->sock >= 0) close (g->sock);
138   g->sock = -1;
139 }
140
141 /* Wait for subprocess to exit. */
142 static void
143 wait_subprocess (guestfs_h *g)
144 {
145   if (g->pid >= 0) waitpid (g->pid, NULL, 0);
146   g->pid = -1;
147 }
148
149 static int
150 error (guestfs_h *g, const char *fs, ...)
151 {
152   va_list args;
153
154   fprintf (stderr, "libguestfs: ");
155   va_start (args, fs);
156   vfprintf (stderr, fs, args);
157   va_end (args);
158   fputc ('\n', stderr);
159
160   if (g->exit_on_error) exit (1);
161   return -1;
162 }
163
164 static int
165 perrorf (guestfs_h *g, const char *fs, ...)
166 {
167   va_list args;
168   char buf[256];
169   int err = errno;
170
171   fprintf (stderr, "libguestfs: ");
172   va_start (args, fs);
173   vfprintf (stderr, fs, args);
174   va_end (args);
175   strerror_r (err, buf, sizeof buf);
176   fprintf (stderr, ": %s\n", buf);
177
178   if (g->exit_on_error) exit (1);
179   return -1;
180 }
181
182 static void *
183 safe_malloc (guestfs_h *g, int nbytes)
184 {
185   void *ptr = malloc (nbytes);
186   if (!ptr) g->abort_fn ();
187   return ptr;
188 }
189
190 static void *
191 safe_realloc (guestfs_h *g, void *ptr, int nbytes)
192 {
193   void *p = realloc (ptr, nbytes);
194   if (!p) g->abort_fn ();
195   return p;
196 }
197
198 static char *
199 safe_strdup (guestfs_h *g, const char *str)
200 {
201   char *s = strdup (str);
202   if (!s) g->abort_fn ();
203   return s;
204 }
205
206 void
207 guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_fn a)
208 {
209   g->abort_fn = a;
210 }
211
212 guestfs_abort_fn
213 guestfs_get_out_of_memory_handler (guestfs_h *g)
214 {
215   return g->abort_fn;
216 }
217
218 void
219 guestfs_set_exit_on_error (guestfs_h *g, int e)
220 {
221   g->exit_on_error = e;
222 }
223
224 int
225 guestfs_get_exit_on_error (guestfs_h *g)
226 {
227   return g->exit_on_error;
228 }
229
230 void
231 guestfs_set_verbose (guestfs_h *g, int v)
232 {
233   g->verbose = v;
234 }
235
236 int
237 guestfs_get_verbose (guestfs_h *g)
238 {
239   return g->verbose;
240 }
241
242 /* Add an escaped string to the current command line. */
243 static int
244 add_cmdline (guestfs_h *g, const char *str)
245 {
246   if (g->pid >= 0)
247     return error (g, "command line cannot be altered after qemu subprocess launched");
248
249   g->cmdline_size++;
250   g->cmdline = safe_realloc (g, g->cmdline, sizeof (char *) * g->cmdline_size);
251   g->cmdline[g->cmdline_size-1] = safe_strdup (g, str);
252
253   return 0;
254 }
255
256 int
257 guestfs_config (guestfs_h *g,
258                 const char *qemu_param, const char *qemu_value)
259 {
260   if (qemu_param[0] != '-')
261     return error (g, "guestfs_config: parameter must begin with '-' character");
262
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.
265    */
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");
275
276   if (add_cmdline (g, qemu_param) != 0) return -1;
277
278   if (qemu_value != NULL) {
279     if (add_cmdline (g, qemu_value) != 0) return -1;
280   }
281
282   return 0;
283 }
284
285 int
286 guestfs_add_drive (guestfs_h *g, const char *filename)
287 {
288   int len = strlen (filename) + 64;
289   char buf[len];
290
291   if (strchr (filename, ',') != NULL)
292     return error (g, "filename cannot contain ',' (comma) character");
293
294   snprintf (buf, len, "file=%s,media=disk", filename);
295
296   return guestfs_config (g, "-drive", buf);
297 }
298
299 int
300 guestfs_add_cdrom (guestfs_h *g, const char *filename)
301 {
302   int len = strlen (filename) + 64;
303   char buf[len];
304
305   if (strchr (filename, ',') != NULL)
306     return error (g, "filename cannot contain ',' (comma) character");
307
308   snprintf (buf, len, "file=%s,if=ide,index=1,media=cdrom", filename);
309
310   return guestfs_config (g, "-drive", buf);
311 }
312
313 int
314 guestfs_launch (guestfs_h *g)
315 {
316   static const char *dir_template = "/tmp/libguestfsXXXXXX";
317   int r, i;
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";
321   char unixsock[256];
322   char vmchannel[256];
323   char tmpfile[256];
324
325   /* XXX Choose which qemu to run. */
326   /* XXX Choose initrd, etc. */
327
328   /* Make the temporary directory containing the logfile and socket. */
329   if (!g->tmpdir) {
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);
333
334     snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir);
335   }
336
337   r = fork ();
338   if (r == -1)
339     return perrorf (g, "fork");
340
341   if (r > 0) {                  /* Parent (library). */
342     g->pid = r;
343
344     /* If qemu is going to die during startup, give it a tiny amount of
345      * time to print the error message.
346      */
347     usleep (10000);
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.
351      */
352     g->cmdline[0] = (char *) qemu;
353
354     g->cmdline = realloc (g->cmdline, sizeof (char *) * (g->cmdline_size + 14));
355     if (g->cmdline == NULL) {
356       perror ("realloc");
357       _exit (1);
358     }
359
360     snprintf (vmchannel, sizeof vmchannel,
361               "channel,%d:unix:%s,server,nowait", 666, unixsock);
362
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;
377
378     if (g->verbose) {
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");
383     }
384
385     /* Set up stdin, stdout.  Messages should go to the logfile. */
386     close (0);
387     close (1);
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);
391     /*dup2 (1, 2);*/
392
393     execv (qemu, g->cmdline);   /* Run qemu. */
394     perror (qemu);
395     _exit (1);
396   }
397
398   return 0;
399 }
400
401 #define UNIX_PATH_MAX 108
402
403 int
404 guestfs_wait_ready (guestfs_h *g)
405 {
406   int r, i, lsock;
407   struct sockaddr_un addr;
408
409   if (guestfs_ready (g)) return 0;
410
411   /* Launch the subprocess, if there isn't one already. */
412   if (g->pid == -1) {
413     if (guestfs_launch (g) != 0)
414       return -1;
415   }
416
417   if (g->sock >= 0) {
418     close (g->sock);
419     g->sock = -1;
420   }
421
422   lsock = socket (AF_UNIX, SOCK_STREAM, 0);
423   if (lsock == -1)
424     return perrorf (g, "socket");
425
426   addr.sun_family = AF_UNIX;
427   snprintf (addr.sun_path, UNIX_PATH_MAX, "%s/sock", g->tmpdir);
428
429   if (bind (lsock, (struct sockaddr *) &addr, sizeof addr) == -1) {
430     perrorf (g, "bind");
431     close (lsock);
432     return -1;
433   }
434
435   if (listen (lsock, 1) == -1) {
436     perrorf (g, "listen");
437     close (lsock);
438     return -1;
439   }
440
441   if (fcntl (lsock, F_SETFL, O_NONBLOCK) == -1) {
442     perrorf (g, "set socket non-blocking");
443     close (lsock);
444     return -1;
445   }
446
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.
451    */
452   for (i = 0; i < 30; ++i) {
453     r = waitpid (g->pid, NULL, WNOHANG);
454
455     if (r > 0 || (r == -1 && errno == ECHILD)) {
456       error (g, "qemu subprocess exited unexpectedly during initialization");
457       g->pid = -1;
458       cleanup_fds (g);
459       close (lsock);
460       return -1;
461     }
462
463     r = accept (lsock, NULL, 0);
464     if (r >= 0) {
465       g->sock = r;
466       fcntl (g->sock, F_SETFL, O_NONBLOCK);
467       close (lsock);
468       return 0;
469     }
470     if (errno == EAGAIN) {
471       sleep (1);
472       continue;
473     }
474     perrorf (g, "accept");
475     close (lsock);
476     guestfs_kill_subprocess (g);
477     return -1;
478   }
479
480   close (lsock);
481   return error (g, "timeout waiting for guest to become ready");
482 }
483
484 int
485 guestfs_ready (guestfs_h *g)
486 {
487   return
488     g->pid >= 0 &&
489     kill (g->pid, 0) == 0 &&
490     g->sock >= 0 /* &&
491     guestfs_ping_daemon (g) >= 0 */;
492 }
493
494 int
495 guestfs_kill_subprocess (guestfs_h *g)
496 {
497   if (g->pid >= 0) {
498     if (g->verbose)
499       fprintf (stderr, "sending SIGINT to pid %d\n", g->pid);
500
501     kill (g->pid, SIGINT);
502     wait_subprocess (g);
503   }
504
505   cleanup_fds (g);
506
507   return 0;
508 }