rescue: Add -w|--rw option.
[libguestfs.git] / rescue / virt-rescue.c
1 /* virt-rescue
2  * Copyright (C) 2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <inttypes.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <errno.h>
27 #include <locale.h>
28 #include <assert.h>
29
30 #include "progname.h"
31 #include "xvasprintf.h"
32
33 #include "guestfs.h"
34 #include "options.h"
35
36 /* Currently open libguestfs handle. */
37 guestfs_h *g;
38
39 int read_only = 0;
40 int live = 0;
41 int verbose = 0;
42 int keys_from_stdin = 0;
43 int echo_keys = 0;
44 const char *libvirt_uri = NULL;
45 int inspector = 0;
46
47 static inline char *
48 bad_cast (char const *s)
49 {
50   return (char *) s;
51 }
52
53 static void __attribute__((noreturn))
54 usage (int status)
55 {
56   if (status != EXIT_SUCCESS)
57     fprintf (stderr, _("Try `%s --help' for more information.\n"),
58              program_name);
59   else {
60     fprintf (stdout,
61            _("%s: Run a rescue shell on a virtual machine\n"
62              "Copyright (C) 2009-2010 Red Hat Inc.\n"
63              "Usage:\n"
64              "  %s [--options] -d domname\n"
65              "  %s [--options] -a disk.img [-a disk.img ...]\n"
66              "Options:\n"
67              "  -a|--add image       Add image\n"
68              "  --append kernelopts  Append kernel options\n"
69              "  -c|--connect uri     Specify libvirt URI for -d option\n"
70              "  -d|--domain guest    Add disks from libvirt guest\n"
71              "  --format[=raw|..]    Force disk format for -a option\n"
72              "  --help               Display brief help\n"
73              "  -m|--memsize MB      Set memory size in megabytes\n"
74              "  --network            Enable network\n"
75              "  -r|--ro              Access read-only\n"
76              "  --selinux            Enable SELinux\n"
77              "  -v|--verbose         Verbose messages\n"
78              "  -V|--version         Display version and exit\n"
79              "  -w|--rw              Mount read-write\n"
80              "  -x                   Trace libguestfs API calls\n"
81              "For more information, see the manpage %s(1).\n"),
82              program_name, program_name, program_name,
83              program_name);
84   }
85   exit (status);
86 }
87
88 int
89 main (int argc, char *argv[])
90 {
91   /* Set global program name that is not polluted with libtool artifacts.  */
92   set_program_name (argv[0]);
93
94   setlocale (LC_ALL, "");
95   bindtextdomain (PACKAGE, LOCALEBASEDIR);
96   textdomain (PACKAGE);
97
98   enum { HELP_OPTION = CHAR_MAX + 1 };
99
100   static const char *options = "a:c:d:m:rvVx";
101   static const struct option long_options[] = {
102     { "add", 1, 0, 'a' },
103     { "append", 1, 0, 0 },
104     { "connect", 1, 0, 'c' },
105     { "domain", 1, 0, 'd' },
106     { "format", 2, 0, 0 },
107     { "help", 0, 0, HELP_OPTION },
108     { "memsize", 1, 0, 'm' },
109     { "network", 0, 0, 0 },
110     { "ro", 0, 0, 'r' },
111     { "rw", 0, 0, 'w' },
112     { "selinux", 0, 0, 0 },
113     { "verbose", 0, 0, 'v' },
114     { "version", 0, 0, 'V' },
115     { 0, 0, 0, 0 }
116   };
117   struct drv *drvs = NULL;
118   struct drv *drv;
119   const char *format = NULL;
120   int c;
121   int option_index;
122   int network = 0;
123   const char *append = NULL;
124   char *append_full;
125   int memsize = 0;
126
127   g = guestfs_create ();
128   if (g == NULL) {
129     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
130     exit (EXIT_FAILURE);
131   }
132
133   argv[0] = bad_cast (program_name);
134
135   for (;;) {
136     c = getopt_long (argc, argv, options, long_options, &option_index);
137     if (c == -1) break;
138
139     switch (c) {
140     case 0:                     /* options which are long only */
141       if (STREQ (long_options[option_index].name, "selinux")) {
142         guestfs_set_selinux (g, 1);
143       } else if (STREQ (long_options[option_index].name, "append")) {
144         append = optarg;
145       } else if (STREQ (long_options[option_index].name, "network")) {
146         network = 1;
147       } else if (STREQ (long_options[option_index].name, "format")) {
148         if (!optarg || STREQ (optarg, ""))
149           format = NULL;
150         else
151           format = optarg;
152       } else {
153         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
154                  program_name, long_options[option_index].name, option_index);
155         exit (EXIT_FAILURE);
156       }
157       break;
158
159     case 'a':
160       OPTION_a;
161       break;
162
163     case 'c':
164       OPTION_c;
165       break;
166
167     case 'd':
168       OPTION_d;
169       break;
170
171     case 'h':
172       usage (EXIT_SUCCESS);
173
174     case 'm':
175       if (sscanf (optarg, "%u", &memsize) != 1) {
176         fprintf (stderr, _("%s: could not parse memory size '%s'\n"),
177                  program_name, optarg);
178         exit (EXIT_FAILURE);
179       }
180       break;
181
182     case 'r':
183       OPTION_r;
184       break;
185
186     case 'v':
187       OPTION_v;
188       break;
189
190     case 'V':
191       OPTION_V;
192       break;
193
194     case 'w':
195       OPTION_w;
196       break;
197
198     case 'x':
199       OPTION_x;
200       break;
201
202     case HELP_OPTION:
203       usage (EXIT_SUCCESS);
204
205     default:
206       usage (EXIT_FAILURE);
207     }
208   }
209
210   /* Old-style syntax?  There were no -a or -d options in the old
211    * virt-rescue which is how we detect this.
212    */
213   if (drvs == NULL) {
214     while (optind < argc) {
215       if (strchr (argv[optind], '/') ||
216           access (argv[optind], F_OK) == 0) { /* simulate -a option */
217         drv = malloc (sizeof (struct drv));
218         if (!drv) {
219           perror ("malloc");
220           exit (EXIT_FAILURE);
221         }
222         drv->type = drv_a;
223         drv->a.filename = argv[optind];
224         drv->a.format = NULL;
225         drv->next = drvs;
226         drvs = drv;
227       } else {                  /* simulate -d option */
228         drv = malloc (sizeof (struct drv));
229         if (!drv) {
230           perror ("malloc");
231           exit (EXIT_FAILURE);
232         }
233         drv->type = drv_d;
234         drv->d.guest = argv[optind];
235         drv->next = drvs;
236         drvs = drv;
237       }
238
239       optind++;
240     }
241   }
242
243   /* These are really constants, but they have to be variables for the
244    * options parsing code.  Assert here that they have known-good
245    * values.
246    */
247   assert (inspector == 0);
248   assert (keys_from_stdin == 0);
249   assert (echo_keys == 0);
250   assert (live == 0);
251
252   /* Must be no extra arguments on the command line. */
253   if (optind != argc)
254     usage (EXIT_FAILURE);
255
256   /* User must have specified some drives. */
257   if (drvs == NULL)
258     usage (EXIT_FAILURE);
259
260   /* Setting "direct mode" is required for the rescue appliance. */
261   guestfs_set_direct (g, 1);
262
263   /* Set other features. */
264   if (memsize > 0)
265     guestfs_set_memsize (g, memsize);
266   if (network)
267     guestfs_set_network (g, 1);
268
269   /* Kernel command line must include guestfs_rescue=1 (see
270    * appliance/init) as well as other options.
271    */
272   append_full = xasprintf ("guestfs_rescue=1%s%s",
273                            append ? " " : "",
274                            append ? append : "");
275   guestfs_set_append (g, append_full);
276   free (append_full);
277
278   /* Add drives. */
279   add_drives (drvs, 'a');
280
281   /* Free up data structures, no longer needed after this point. */
282   free_drives (drvs);
283
284   /* Run the appliance.  This won't return until the user quits the
285    * appliance.
286    */
287   guestfs_set_error_handler (g, NULL, NULL);
288   guestfs_launch (g);
289
290   /* launch() expects guestfsd to start. However, virt-rescue doesn't
291    * run guestfsd, so this will always fail with ECHILD when the
292    * appliance exits unexpectedly.
293    */
294   if (errno != ECHILD) {
295     fprintf (stderr, "%s: %s\n", program_name, guestfs_last_error (g));
296     guestfs_close (g);
297     exit (EXIT_FAILURE);
298   }
299
300   guestfs_close (g);
301
302   exit (EXIT_SUCCESS);
303 }
304
305 /* The following was a nice idea, but in fact it doesn't work.  This is
306  * because qemu has some (broken) pty emulation itself.
307  */
308 #if 0
309   int fd_m, fd_s, r;
310   pid_t pid;
311   struct termios tsorig, tsnew;
312
313   /* Set up pty. */
314   fd_m = posix_openpt (O_RDWR);
315   if (fd_m == -1) {
316     perror ("posix_openpt");
317     exit (EXIT_FAILURE);
318   }
319   r = grantpt (fd_m);
320   if (r == -1) {
321     perror ("grantpt");
322     exit (EXIT_FAILURE);
323   }
324   r = unlockpt (fd_m);
325   if (r == -1) {
326     perror ("unlockpt");
327     exit (EXIT_FAILURE);
328   }
329   fd_s = open (ptsname (fd_m), O_RDWR);
330   if (fd_s == -1) {
331     perror ("open ptsname");
332     exit (EXIT_FAILURE);
333   }
334
335   pid = fork ();
336   if (pid == -1) {
337     perror ("fork");
338     exit (EXIT_FAILURE);
339   }
340   if (pid == 0) {
341     /* Child process. */
342
343 #if 1
344     /* Set raw mode. */
345     r = tcgetattr (fd_s, &tsorig);
346     tsnew = tsorig;
347     cfmakeraw (&tsnew);
348     tcsetattr (fd_s, TCSANOW, &tsnew);
349 #endif
350
351     /* Close the master side of pty and set slave side as
352      * stdin/stdout/stderr.
353      */
354     close (fd_m);
355
356     close (0);
357     close (1);
358     close (2);
359     if (dup (fd_s) == -1 || dup (fd_s) == -1 || dup (fd_s) == -1) {
360       perror ("dup");
361       exit (EXIT_FAILURE);
362     }
363     close (fd_s);
364
365 #if 1
366     if (setsid () == -1)
367       perror ("warning: failed to setsid");
368     if (ioctl (0, TIOCSCTTY, 0) == -1)
369       perror ("warning: failed to TIOCSCTTY");
370 #endif
371
372     /* Run the appliance.  This won't return until the user quits the
373      * appliance.
374      */
375     guestfs_set_error_handler (g, NULL, NULL);
376     r = guestfs_launch (g);
377
378     /* launch() expects guestfsd to start. However, virt-rescue doesn't
379      * run guestfsd, so this will always fail with ECHILD when the
380      * appliance exits unexpectedly.
381      */
382     if (errno != ECHILD) {
383       fprintf (stderr, "%s: %s\n", program_name, guestfs_last_error (g));
384       guestfs_close (g);
385       exit (EXIT_FAILURE);
386     }
387
388     guestfs_close (g);
389     _exit (EXIT_SUCCESS);
390   }
391
392   /* Parent process continues ... */
393
394   /* Close slave side of pty. */
395   close (fd_s);
396
397   /* Set raw mode. */
398   r = tcgetattr (fd_s, &tsorig);
399   tsnew = tsorig;
400   cfmakeraw (&tsnew);
401   tcsetattr (fd_s, TCSANOW, &tsnew);
402
403   /* Send input and output to master side of pty. */
404   r = multiplex (fd_m);
405   tcsetattr (fd_s, TCSANOW, &tsorig); /* Restore cooked mode. */
406   if (r == -1)
407     exit (EXIT_FAILURE);
408
409   if (waitpid (pid, &r, 0) == -1) {
410     perror ("waitpid");
411     exit (EXIT_FAILURE);
412   }
413   if (!WIFEXITED (r)) {
414     /* abnormal child exit */
415     fprintf (stderr, _("%s: unknown child exit status (%d)\n"),
416              program_name, r);
417     exit (EXIT_FAILURE);
418   }
419   else
420     exit (WEXITSTATUS (r)); /* normal exit, return child process's status */
421 }
422
423 /* Naive and simple multiplex function. */
424 static int
425 multiplex (int fd_m)
426 {
427   int r, eof_stdin = 0;
428   fd_set rfds, wfds;
429   char tobuf[BUFSIZ], frombuf[BUFSIZ]; /* to/from slave */
430   size_t tosize = 0, fromsize = 0;
431   ssize_t n;
432   size_t count;
433   long flags_0, flags_1, flags_fd_m;
434
435   flags_0 = fcntl (0, F_GETFL);
436   fcntl (0, F_SETFL, O_NONBLOCK | flags_0);
437   flags_1 = fcntl (0, F_GETFL);
438   fcntl (1, F_SETFL, O_NONBLOCK | flags_1);
439   flags_fd_m = fcntl (0, F_GETFL);
440   fcntl (fd_m, F_SETFL, O_NONBLOCK | flags_fd_m);
441
442   for (;;) {
443     FD_ZERO (&rfds);
444     FD_ZERO (&wfds);
445
446     /* Still space in to-buffer?  If so, we can read from the user. */
447     if (!eof_stdin && tosize < BUFSIZ)
448       FD_SET (0, &rfds);
449     /* Still space in from-buffer?  If so, we can read from the slave. */
450     if (fromsize < BUFSIZ)
451       FD_SET (fd_m, &rfds);
452     /* Content in to-buffer?  If so, we want to write to the slave. */
453     if (tosize > 0)
454       FD_SET (fd_m, &wfds);
455     /* Content in from-buffer?  If so, we want to write to the user. */
456     if (fromsize > 0)
457       FD_SET (1, &wfds);
458
459     r = select (fd_m+1, &rfds, &wfds, NULL, NULL);
460     if (r == -1) {
461       if (errno == EAGAIN || errno == EINTR)
462         continue;
463       perror ("select");
464       return -1;
465     }
466
467     /* Input from user: Put it in the to-buffer. */
468     if (FD_ISSET (0, &rfds)) {
469       count = BUFSIZ - tosize;
470       n = read (0, &tobuf[tosize], count);
471       if (n == -1) {
472         perror ("read");
473         return -1;
474       }
475       if (n == 0) { /* stdin was closed */
476         eof_stdin = 1;
477         /* This is what telnetd does ... */
478         tobuf[tosize] = '\004';
479         tosize += 1;
480       } else
481         tosize += n;
482     }
483
484     /* Input from slave: Put it in the from-buffer. */
485     if (FD_ISSET (fd_m, &rfds)) {
486       count = BUFSIZ - fromsize;
487       n = read (fd_m, &frombuf[fromsize], count);
488       if (n == -1) {
489         if (errno != EIO) /* EIO if slave process dies */
490           perror ("read");
491         break;
492       }
493       if (n == 0) /* slave closed the connection */
494         break;
495       fromsize += n;
496     }
497
498     /* Can write to user. */
499     if (FD_ISSET (1, &wfds)) {
500       n = write (1, frombuf, fromsize);
501       if (n == -1) {
502         perror ("write");
503         return -1;
504       }
505       memmove (frombuf, &frombuf[n], BUFSIZ - n);
506       fromsize -= n;
507     }
508
509     /* Can write to slave. */
510     if (FD_ISSET (fd_m, &wfds)) {
511       n = write (fd_m, tobuf, tosize);
512       if (n == -1) {
513         perror ("write");
514         return -1;
515       }
516       memmove (tobuf, &tobuf[n], BUFSIZ - n);
517       tosize -= n;
518     }
519   } /* for (;;) */
520
521   /* We end up here when slave has closed the connection. */
522   close (fd_m);
523
524   /* Restore blocking behaviour. */
525   fcntl (1, F_SETFL, flags_1);
526
527   /* Last chance to write out any remaining data in the buffers, but
528    * don't bother about errors.
529    */
530   ignore_value (write (1, frombuf, fromsize));
531
532   return 0;
533 }
534 #endif