cf547c4e02d34ec693835eaa83cece6820148b84
[libguestfs.git] / fish / fish.c
1 /* guestfish - the filesystem interactive shell
2  * Copyright (C) 2009-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 <string.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <getopt.h>
28 #include <signal.h>
29 #include <assert.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <locale.h>
33 #include <langinfo.h>
34 #include <termios.h>
35
36 #ifdef HAVE_LIBREADLINE
37 #include <readline/readline.h>
38 #include <readline/history.h>
39 #endif
40
41 #include <guestfs.h>
42
43 #include "fish.h"
44 #include "c-ctype.h"
45 #include "closeout.h"
46 #include "progname.h"
47
48 /* List of drives added via -a, -d or -N options. */
49 struct drv {
50   struct drv *next;
51   enum { drv_a, drv_d, drv_N } type;
52   union {
53     struct {
54       char *filename;       /* disk filename */
55     } a;
56     struct {
57       char *guest;          /* guest name */
58     } d;
59     struct {
60       char *filename;       /* disk filename (testX.img) */
61       prep_data *data;      /* prepared type */
62       char *device;         /* device inside the appliance */
63     } N;
64   };
65 };
66
67 struct mp {
68   struct mp *next;
69   char *device;
70   char *mountpoint;
71 };
72
73 static void set_up_terminal (void);
74 static char add_drives (struct drv *drv, char next_drive);
75 static void prepare_drives (struct drv *drv);
76 static void mount_mps (struct mp *mp);
77 static int launch (void);
78 static void interactive (void);
79 static void shell_script (void);
80 static void script (int prompt);
81 static void cmdline (char *argv[], int optind, int argc);
82 static void initialize_readline (void);
83 static void cleanup_readline (void);
84 #ifdef HAVE_LIBREADLINE
85 static void add_history_line (const char *);
86 #endif
87
88 static int override_progress_bars = -1;
89
90 /* Currently open libguestfs handle. */
91 guestfs_h *g;
92
93 int read_only = 0;
94 int quit = 0;
95 int verbose = 0;
96 int remote_control_listen = 0;
97 int remote_control = 0;
98 int exit_on_error = 1;
99 int command_num = 0;
100 int keys_from_stdin = 0;
101 const char *libvirt_uri = NULL;
102 int inspector = 0;
103 int utf8_mode = 0;
104 int have_terminfo = 0;
105 int progress_bars = 0;
106
107 static void __attribute__((noreturn))
108 usage (int status)
109 {
110   if (status != EXIT_SUCCESS)
111     fprintf (stderr, _("Try `%s --help' for more information.\n"),
112              program_name);
113   else {
114     fprintf (stdout,
115            _("%s: guest filesystem shell\n"
116              "%s lets you edit virtual machine filesystems\n"
117              "Copyright (C) 2009 Red Hat Inc.\n"
118              "Usage:\n"
119              "  %s [--options] cmd [: cmd : cmd ...]\n"
120              "  %s -i libvirt-domain\n"
121              "  %s -i disk-image(s)\n"
122              "or for interactive use:\n"
123              "  %s\n"
124              "or from a shell script:\n"
125              "  %s <<EOF\n"
126              "  cmd\n"
127              "  ...\n"
128              "  EOF\n"
129              "Options:\n"
130              "  -h|--cmd-help        List available commands\n"
131              "  -h|--cmd-help cmd    Display detailed help on 'cmd'\n"
132              "  -a|--add image       Add image\n"
133              "  -c|--connect uri     Specify libvirt URI for -d option\n"
134              "  -d|--domain guest    Add disks from libvirt guest\n"
135              "  -D|--no-dest-paths   Don't tab-complete paths from guest fs\n"
136              "  -f|--file file       Read commands from file\n"
137              "  -i|--inspector       Automatically mount filesystems\n"
138              "  --keys-from-stdin    Read passphrases from stdin\n"
139              "  --listen             Listen for remote commands\n"
140              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
141              "  -n|--no-sync         Don't autosync\n"
142              "  -N|--new type        Create prepared disk (test1.img, ...)\n"
143              "  --progress-bars      Enable progress bars even when not interactive\n"
144              "  --no-progress-bars   Disable progress bars\n"
145              "  --remote[=pid]       Send commands to remote %s\n"
146              "  -r|--ro              Mount read-only\n"
147              "  --selinux            Enable SELinux support\n"
148              "  -v|--verbose         Verbose messages\n"
149              "  -x                   Echo each command before executing it\n"
150              "  -V|--version         Display version and exit\n"
151              "For more information,  see the manpage %s(1).\n"),
152              program_name, program_name, program_name,
153              program_name, program_name, program_name,
154              program_name, program_name, program_name);
155   }
156   exit (status);
157 }
158
159 int
160 main (int argc, char *argv[])
161 {
162   /* Set global program name that is not polluted with libtool artifacts.  */
163   set_program_name (argv[0]);
164
165   atexit (close_stdout);
166
167   setlocale (LC_ALL, "");
168   bindtextdomain (PACKAGE, LOCALEBASEDIR);
169   textdomain (PACKAGE);
170
171   set_up_terminal ();
172
173   enum { HELP_OPTION = CHAR_MAX + 1 };
174
175   static const char *options = "a:c:d:Df:h::im:nN:rv?Vx";
176   static const struct option long_options[] = {
177     { "add", 1, 0, 'a' },
178     { "cmd-help", 2, 0, 'h' },
179     { "connect", 1, 0, 'c' },
180     { "domain", 1, 0, 'd' },
181     { "file", 1, 0, 'f' },
182     { "help", 0, 0, HELP_OPTION },
183     { "inspector", 0, 0, 'i' },
184     { "keys-from-stdin", 0, 0, 0 },
185     { "listen", 0, 0, 0 },
186     { "mount", 1, 0, 'm' },
187     { "new", 1, 0, 'N' },
188     { "no-dest-paths", 0, 0, 'D' },
189     { "no-sync", 0, 0, 'n' },
190     { "progress-bars", 0, 0, 0 },
191     { "no-progress-bars", 0, 0, 0 },
192     { "remote", 2, 0, 0 },
193     { "ro", 0, 0, 'r' },
194     { "selinux", 0, 0, 0 },
195     { "verbose", 0, 0, 'v' },
196     { "version", 0, 0, 'V' },
197     { 0, 0, 0, 0 }
198   };
199   struct drv *drvs = NULL;
200   struct drv *drv;
201   struct mp *mps = NULL;
202   struct mp *mp;
203   char *p, *file = NULL;
204   int c;
205   int option_index;
206   struct sigaction sa;
207   int next_prepared_drive = 1;
208
209   initialize_readline ();
210
211   memset (&sa, 0, sizeof sa);
212   sa.sa_handler = SIG_IGN;
213   sa.sa_flags = SA_RESTART;
214   sigaction (SIGPIPE, &sa, NULL);
215
216   /* guestfs_create is meant to be a lightweight operation, so
217    * it's OK to do it early here.
218    */
219   g = guestfs_create ();
220   if (g == NULL) {
221     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
222     exit (EXIT_FAILURE);
223   }
224
225   guestfs_set_autosync (g, 1);
226
227   /* If developing, add ./appliance to the path.  Note that libtools
228    * interferes with this because uninstalled guestfish is a shell
229    * script that runs the real program with an absolute path.  Detect
230    * that too.
231    *
232    * BUT if LIBGUESTFS_PATH environment variable is already set by
233    * the user, then don't override it.
234    */
235   if (getenv ("LIBGUESTFS_PATH") == NULL &&
236       argv[0] &&
237       (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL))
238     guestfs_set_path (g, "appliance:" GUESTFS_DEFAULT_PATH);
239
240   /* CAUTION: we are careful to modify argv[0] here, only after
241    * using it just above.
242    *
243    * getopt_long uses argv[0], so give it the sanitized name.  Save a copy
244    * of the original, in case it's needed below.
245    */
246   char *real_argv0 = argv[0];
247   argv[0] = bad_cast (program_name);
248
249   for (;;) {
250     c = getopt_long (argc, argv, options, long_options, &option_index);
251     if (c == -1) break;
252
253     switch (c) {
254     case 0:                     /* options which are long only */
255       if (STREQ (long_options[option_index].name, "listen"))
256         remote_control_listen = 1;
257       else if (STREQ (long_options[option_index].name, "remote")) {
258         if (optarg) {
259           if (sscanf (optarg, "%d", &remote_control) != 1) {
260             fprintf (stderr, _("%s: --listen=PID: PID was not a number: %s\n"),
261                      program_name, optarg);
262             exit (EXIT_FAILURE);
263           }
264         } else {
265           p = getenv ("GUESTFISH_PID");
266           if (!p || sscanf (p, "%d", &remote_control) != 1) {
267             fprintf (stderr, _("%s: remote: $GUESTFISH_PID must be set"
268                                " to the PID of the remote process\n"),
269                      program_name);
270             exit (EXIT_FAILURE);
271           }
272         }
273       } else if (STREQ (long_options[option_index].name, "selinux")) {
274         guestfs_set_selinux (g, 1);
275       } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
276         keys_from_stdin = 1;
277       } else if (STREQ (long_options[option_index].name, "progress-bars")) {
278         override_progress_bars = 1;
279       } else if (STREQ (long_options[option_index].name, "no-progress-bars")) {
280         override_progress_bars = 0;
281       } else {
282         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
283                  program_name, long_options[option_index].name, option_index);
284         exit (EXIT_FAILURE);
285       }
286       break;
287
288     case 'a':
289       if (access (optarg, R_OK) != 0) {
290         perror (optarg);
291         exit (EXIT_FAILURE);
292       }
293       drv = malloc (sizeof (struct drv));
294       if (!drv) {
295         perror ("malloc");
296         exit (EXIT_FAILURE);
297       }
298       drv->type = drv_a;
299       drv->a.filename = optarg;
300       drv->next = drvs;
301       drvs = drv;
302       break;
303
304     case 'c':
305       libvirt_uri = optarg;
306       break;
307
308     case 'd':
309       drv = malloc (sizeof (struct drv));
310       if (!drv) {
311         perror ("malloc");
312         exit (EXIT_FAILURE);
313       }
314       drv->type = drv_d;
315       drv->d.guest = optarg;
316       drv->next = drvs;
317       drvs = drv;
318       break;
319
320     case 'N':
321       if (STRCASEEQ (optarg, "list") ||
322           STRCASEEQ (optarg, "help") ||
323           STRCASEEQ (optarg, "h") ||
324           STRCASEEQ (optarg, "?")) {
325         list_prepared_drives ();
326         exit (EXIT_SUCCESS);
327       }
328       drv = malloc (sizeof (struct drv));
329       if (!drv) {
330         perror ("malloc");
331         exit (EXIT_FAILURE);
332       }
333       drv->type = drv_N;
334       if (asprintf (&drv->N.filename, "test%d.img",
335                     next_prepared_drive++) == -1) {
336         perror ("asprintf");
337         exit (EXIT_FAILURE);
338       }
339       drv->N.data = create_prepared_file (optarg, drv->N.filename);
340       drv->N.device = NULL;     /* filled in by add_drives */
341       drv->next = drvs;
342       drvs = drv;
343       break;
344
345     case 'D':
346       complete_dest_paths = 0;
347       break;
348
349     case 'f':
350       if (file) {
351         fprintf (stderr, _("%s: only one -f parameter can be given\n"),
352                  program_name);
353         exit (EXIT_FAILURE);
354       }
355       file = optarg;
356       break;
357
358     case 'h': {
359       int r = 0;
360
361       if (optarg)
362         r = display_command (optarg);
363       else if (argv[optind] && argv[optind][0] != '-')
364         r = display_command (argv[optind++]);
365       else
366         list_commands ();
367
368       exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
369     }
370
371     case 'i':
372       inspector = 1;
373       break;
374
375     case 'm':
376       mp = malloc (sizeof (struct mp));
377       if (!mp) {
378         perror ("malloc");
379         exit (EXIT_FAILURE);
380       }
381       p = strchr (optarg, ':');
382       if (p) {
383         *p = '\0';
384         mp->mountpoint = p+1;
385       } else
386         mp->mountpoint = bad_cast ("/");
387       mp->device = optarg;
388       mp->next = mps;
389       mps = mp;
390       break;
391
392     case 'n':
393       guestfs_set_autosync (g, 0);
394       break;
395
396     case 'r':
397       read_only = 1;
398       break;
399
400     case 'v':
401       verbose++;
402       guestfs_set_verbose (g, verbose);
403       break;
404
405     case 'V': {
406       struct guestfs_version *v = guestfs_version (g);
407       printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", program_name,
408               v->major, v->minor, v->release, v->extra);
409       exit (EXIT_SUCCESS);
410     }
411
412     case 'x':
413       guestfs_set_trace (g, 1);
414       break;
415
416     case HELP_OPTION:
417       usage (EXIT_SUCCESS);
418
419     default:
420       usage (EXIT_FAILURE);
421     }
422   }
423
424   /* Old-style -i syntax?  Since -a/-d/-N and -i was disallowed
425    * previously, if we have -i without any drives but with something
426    * on the command line, it must be old-style syntax.
427    */
428   if (inspector && drvs == NULL && optind < argc) {
429     while (optind < argc) {
430       if (strchr (argv[optind], '/') ||
431           access (argv[optind], F_OK) == 0) { /* simulate -a option */
432         drv = malloc (sizeof (struct drv));
433         if (!drv) {
434           perror ("malloc");
435           exit (EXIT_FAILURE);
436         }
437         drv->type = drv_a;
438         drv->a.filename = argv[optind];
439         drv->next = drvs;
440         drvs = drv;
441       } else {                  /* simulate -d option */
442         drv = malloc (sizeof (struct drv));
443         if (!drv) {
444           perror ("malloc");
445           exit (EXIT_FAILURE);
446         }
447         drv->type = drv_d;
448         drv->d.guest = argv[optind];
449         drv->next = drvs;
450         drvs = drv;
451       }
452
453       optind++;
454     }
455   }
456
457   /* If we've got drives to add, add them now. */
458   add_drives (drvs, 'a');
459
460   /* If we've got mountpoints or prepared drives or -i option, we must
461    * launch the guest and mount them.
462    */
463   if (next_prepared_drive > 1 || mps != NULL || inspector) {
464     /* RHBZ#612178: If --listen flag is given, then we will fork into
465      * the background in rc_listen().  However you can't do this while
466      * holding a libguestfs handle open because the recovery process
467      * will think the main program has died and kill qemu.  Therefore
468      * don't use the recovery process for this case.  (A better
469      * solution would be to call launch () etc after the fork, but
470      * that greatly complicates the code here).
471      */
472     if (remote_control_listen)
473       guestfs_set_recovery_proc (g, 0);
474
475     if (launch () == -1) exit (EXIT_FAILURE);
476
477     if (inspector)
478       inspect_mount ();
479
480     prepare_drives (drvs);
481     mount_mps (mps);
482   }
483
484   /* Remote control? */
485   if (remote_control_listen && remote_control) {
486     fprintf (stderr,
487              _("%s: cannot use --listen and --remote options at the same time\n"),
488              program_name);
489     exit (EXIT_FAILURE);
490   }
491
492   if (remote_control_listen) {
493     if (optind < argc) {
494       fprintf (stderr,
495                _("%s: extra parameters on the command line with --listen flag\n"),
496                program_name);
497       exit (EXIT_FAILURE);
498     }
499     if (file) {
500       fprintf (stderr,
501                _("%s: cannot use --listen and --file options at the same time\n"),
502                program_name);
503       exit (EXIT_FAILURE);
504     }
505     rc_listen ();
506   }
507
508   /* -f (file) parameter? */
509   if (file) {
510     close (0);
511     if (open (file, O_RDONLY) == -1) {
512       perror (file);
513       exit (EXIT_FAILURE);
514     }
515   }
516
517   /* Decide if we display progress bars. */
518   progress_bars =
519     override_progress_bars >= 0
520     ? override_progress_bars
521     : (optind >= argc && isatty (0));
522
523   if (progress_bars)
524     guestfs_set_progress_callback (g, progress_callback, NULL);
525
526   /* Interactive, shell script, or command(s) on the command line? */
527   if (optind >= argc) {
528     if (isatty (0))
529       interactive ();
530     else
531       shell_script ();
532   }
533   else
534     cmdline (argv, optind, argc);
535
536   cleanup_readline ();
537
538   exit (EXIT_SUCCESS);
539 }
540
541 /* The <term.h> header file which defines this has "issues". */
542 extern int tgetent (char *, const char *);
543
544 static void
545 set_up_terminal (void)
546 {
547   /* http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate */
548   utf8_mode = STREQ (nl_langinfo (CODESET), "UTF-8");
549
550   char *term = getenv ("TERM");
551   if (term == NULL) {
552     //fprintf (stderr, _("guestfish: TERM (terminal type) not defined.\n"));
553     return;
554   }
555
556   int r = tgetent (NULL, term);
557   if (r == -1) {
558     fprintf (stderr, _("guestfish: could not access termcap or terminfo database.\n"));
559     return;
560   }
561   if (r == 0) {
562     fprintf (stderr, _("guestfish: terminal type \"%s\" not defined.\n"),
563              term);
564     return;
565   }
566
567   have_terminfo = 1;
568 }
569
570 void
571 pod2text (const char *name, const char *shortdesc, const char *str)
572 {
573   FILE *fp;
574
575   fp = popen ("pod2text", "w");
576   if (fp == NULL) {
577     /* pod2text failed, maybe not found, so let's just print the
578      * source instead, since that's better than doing nothing.
579      */
580     printf ("%s - %s\n\n%s\n", name, shortdesc, str);
581     return;
582   }
583   fprintf (fp, "=head1 NAME\n\n%s - %s\n\n", name, shortdesc);
584   fputs (str, fp);
585   pclose (fp);
586 }
587
588 /* List is built in reverse order, so mount them in reverse order. */
589 static void
590 mount_mps (struct mp *mp)
591 {
592   int r;
593
594   if (mp) {
595     mount_mps (mp->next);
596
597     /* Don't use guestfs_mount here because that will default to mount
598      * options -o sync,noatime.  For more information, see guestfs(3)
599      * section "LIBGUESTFS GOTCHAS".
600      */
601     const char *options = read_only ? "ro" : "";
602     r = guestfs_mount_options (g, options, mp->device, mp->mountpoint);
603     if (r == -1)
604       exit (EXIT_FAILURE);
605   }
606 }
607
608 static char
609 add_drives (struct drv *drv, char next_drive)
610 {
611   int r;
612
613   if (next_drive > 'z') {
614     fprintf (stderr,
615              _("guestfish: too many drives added on the command line\n"));
616     exit (EXIT_FAILURE);
617   }
618
619   if (drv) {
620     next_drive = add_drives (drv->next, next_drive);
621
622     switch (drv->type) {
623     case drv_a:
624       if (!read_only)
625         r = guestfs_add_drive (g, drv->a.filename);
626       else
627         r = guestfs_add_drive_ro (g, drv->a.filename);
628       if (r == -1)
629         exit (EXIT_FAILURE);
630
631       next_drive++;
632       break;
633
634     case drv_d:
635       r = add_libvirt_drives (drv->d.guest);
636       if (r == -1)
637         exit (EXIT_FAILURE);
638
639       next_drive += r;
640       break;
641
642     case drv_N:
643       /* -N option is not affected by --ro */
644       r = guestfs_add_drive (g, drv->N.filename);
645       if (r == -1)
646         exit (EXIT_FAILURE);
647
648       if (asprintf (&drv->N.device, "/dev/sd%c", next_drive) == -1) {
649         perror ("asprintf");
650         exit (EXIT_FAILURE);
651       }
652
653       next_drive++;
654       break;
655
656     default: /* keep GCC happy */
657       abort ();
658     }
659   }
660
661   return next_drive;
662 }
663
664 static void
665 prepare_drives (struct drv *drv)
666 {
667   if (drv) {
668     prepare_drives (drv->next);
669     if (drv->type == drv_N)
670       prepare_drive (drv->N.filename, drv->N.data, drv->N.device);
671   }
672 }
673
674 static int
675 launch (void)
676 {
677   if (guestfs_is_config (g)) {
678     if (guestfs_launch (g) == -1)
679       return -1;
680   }
681   return 0;
682 }
683
684 static void
685 interactive (void)
686 {
687   script (1);
688 }
689
690 static void
691 shell_script (void)
692 {
693   script (0);
694 }
695
696 #define FISH "><fs> "
697
698 static char *line_read = NULL;
699
700 static char *
701 rl_gets (int prompt)
702 {
703 #ifdef HAVE_LIBREADLINE
704
705   if (prompt) {
706     if (line_read) {
707       free (line_read);
708       line_read = NULL;
709     }
710
711     line_read = readline (prompt ? FISH : "");
712
713     if (line_read && *line_read)
714       add_history_line (line_read);
715
716     return line_read;
717   }
718
719 #endif /* HAVE_LIBREADLINE */
720
721   static char buf[8192];
722   int len;
723
724   if (prompt) printf (FISH);
725   line_read = fgets (buf, sizeof buf, stdin);
726
727   if (line_read) {
728     len = strlen (line_read);
729     if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
730   }
731
732   return line_read;
733 }
734
735 static void
736 script (int prompt)
737 {
738   char *buf;
739   char *cmd;
740   char *p, *pend;
741   char *argv[64];
742   int len;
743   int global_exit_on_error = !prompt;
744   int tilde_candidate;
745
746   if (prompt) {
747     printf (_("\n"
748               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
749               "editing virtual machine filesystems.\n"
750               "\n"
751               "Type: 'help' for a list of commands\n"
752               "      'man' to read the manual\n"
753               "      'quit' to quit the shell\n"
754               "\n"));
755
756     if (inspector) {
757       print_inspect_prompt ();
758       printf ("\n");
759     }
760   }
761
762   while (!quit) {
763     char *pipe = NULL;
764
765     exit_on_error = global_exit_on_error;
766
767     buf = rl_gets (prompt);
768     if (!buf) {
769       quit = 1;
770       break;
771     }
772
773     /* Skip any initial whitespace before the command. */
774   again:
775     while (*buf && c_isspace (*buf))
776       buf++;
777
778     if (!*buf) continue;
779
780     /* If the next character is '#' then this is a comment. */
781     if (*buf == '#') continue;
782
783     /* If the next character is '!' then pass the whole lot to system(3). */
784     if (*buf == '!') {
785       int r;
786
787       r = system (buf+1);
788       if (exit_on_error) {
789         if (r == -1 ||
790             (WIFSIGNALED (r) &&
791              (WTERMSIG (r) == SIGINT || WTERMSIG (r) == SIGQUIT)) ||
792             WEXITSTATUS (r) != 0)
793           exit (EXIT_FAILURE);
794       }
795       continue;
796     }
797
798     /* If the next character is '-' allow the command to fail without
799      * exiting on error (just for this one command though).
800      */
801     if (*buf == '-') {
802       exit_on_error = 0;
803       buf++;
804       goto again;
805     }
806
807     /* Get the command (cannot be quoted). */
808     len = strcspn (buf, " \t");
809
810     if (len == 0) continue;
811
812     cmd = buf;
813     unsigned int i = 0;
814     if (buf[len] == '\0') {
815       argv[0] = NULL;
816       goto got_command;
817     }
818
819     buf[len] = '\0';
820     p = &buf[len+1];
821     p += strspn (p, " \t");
822
823     /* Get the parameters. */
824     while (*p && i < sizeof argv / sizeof argv[0]) {
825       tilde_candidate = 0;
826
827       /* Parameters which start with quotes or pipes are treated
828        * specially.  Bare parameters are delimited by whitespace.
829        */
830       if (*p == '"') {
831         p++;
832         len = strcspn (p, "\"");
833         if (p[len] == '\0') {
834           fprintf (stderr, _("%s: unterminated double quote\n"), program_name);
835           if (exit_on_error) exit (EXIT_FAILURE);
836           goto next_command;
837         }
838         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
839           fprintf (stderr,
840                    _("%s: command arguments not separated by whitespace\n"),
841                    program_name);
842           if (exit_on_error) exit (EXIT_FAILURE);
843           goto next_command;
844         }
845         p[len] = '\0';
846         pend = p[len+1] ? &p[len+2] : &p[len+1];
847       } else if (*p == '\'') {
848         p++;
849         len = strcspn (p, "'");
850         if (p[len] == '\0') {
851           fprintf (stderr, _("%s: unterminated single quote\n"), program_name);
852           if (exit_on_error) exit (EXIT_FAILURE);
853           goto next_command;
854         }
855         if (p[len+1] && (p[len+1] != ' ' && p[len+1] != '\t')) {
856           fprintf (stderr,
857                    _("%s: command arguments not separated by whitespace\n"),
858                    program_name);
859           if (exit_on_error) exit (EXIT_FAILURE);
860           goto next_command;
861         }
862         p[len] = '\0';
863         pend = p[len+1] ? &p[len+2] : &p[len+1];
864       } else if (*p == '|') {
865         *p = '\0';
866         pipe = p+1;
867         continue;
868         /*
869       } else if (*p == '[') {
870         int c = 1;
871         p++;
872         pend = p;
873         while (*pend && c != 0) {
874           if (*pend == '[') c++;
875           else if (*pend == ']') c--;
876           pend++;
877         }
878         if (c != 0) {
879           fprintf (stderr,
880                    _("%s: unterminated \"[...]\" sequence\n"), program_name);
881           if (exit_on_error) exit (EXIT_FAILURE);
882           goto next_command;
883         }
884         if (*pend && (*pend != ' ' && *pend != '\t')) {
885           fprintf (stderr,
886                    _("%s: command arguments not separated by whitespace\n"),
887                    program_name);
888           if (exit_on_error) exit (EXIT_FAILURE);
889           goto next_command;
890         }
891         *(pend-1) = '\0';
892         */
893       } else if (*p != ' ' && *p != '\t') {
894         /* If the first character is a ~ then note that this parameter
895          * is a candidate for ~username expansion.  NB this does not
896          * apply to quoted parameters.
897          */
898         tilde_candidate = *p == '~';
899         len = strcspn (p, " \t");
900         if (p[len]) {
901           p[len] = '\0';
902           pend = &p[len+1];
903         } else
904           pend = &p[len];
905       } else {
906         fprintf (stderr, _("%s: internal error parsing string at '%s'\n"),
907                  program_name, p);
908         abort ();
909       }
910
911       if (!tilde_candidate)
912         argv[i] = p;
913       else
914         argv[i] = try_tilde_expansion (p);
915       i++;
916       p = pend;
917
918       if (*p)
919         p += strspn (p, " \t");
920     }
921
922     if (i == sizeof argv / sizeof argv[0]) {
923       fprintf (stderr, _("%s: too many arguments\n"), program_name);
924       if (exit_on_error) exit (EXIT_FAILURE);
925       goto next_command;
926     }
927
928     argv[i] = NULL;
929
930   got_command:
931     if (issue_command (cmd, argv, pipe) == -1) {
932       if (exit_on_error) exit (EXIT_FAILURE);
933     }
934
935   next_command:;
936   }
937   if (prompt) printf ("\n");
938 }
939
940 static void
941 cmdline (char *argv[], int optind, int argc)
942 {
943   const char *cmd;
944   char **params;
945
946   exit_on_error = 1;
947
948   if (optind >= argc) return;
949
950   cmd = argv[optind++];
951   if (STREQ (cmd, ":")) {
952     fprintf (stderr, _("%s: empty command on command line\n"), program_name);
953     exit (EXIT_FAILURE);
954   }
955
956   /* Allow -cmd on the command line to mean (temporarily) override
957    * the normal exit on error (RHBZ#578407).
958    */
959   if (cmd[0] == '-') {
960     exit_on_error = 0;
961     cmd++;
962   }
963
964   params = &argv[optind];
965
966   /* Search for end of command list or ":" ... */
967   while (optind < argc && STRNEQ (argv[optind], ":"))
968     optind++;
969
970   if (optind == argc) {
971     if (issue_command (cmd, params, NULL) == -1 && exit_on_error)
972         exit (EXIT_FAILURE);
973   } else {
974     argv[optind] = NULL;
975     if (issue_command (cmd, params, NULL) == -1 && exit_on_error)
976       exit (EXIT_FAILURE);
977     cmdline (argv, optind+1, argc);
978   }
979 }
980
981 int
982 issue_command (const char *cmd, char *argv[], const char *pipecmd)
983 {
984   int argc;
985   int stdout_saved_fd = -1;
986   int pid = 0;
987   int i, r;
988
989   reset_progress_bar ();
990
991   /* This counts the commands issued, starting at 1. */
992   command_num++;
993
994   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
995   if (pipecmd) {
996     int fd[2];
997
998     if (fflush (stdout) == EOF) {
999       perror ("failed to flush standard output");
1000       return -1;
1001     }
1002     if (pipe (fd) < 0) {
1003       perror ("pipe failed");
1004       return -1;
1005     }
1006     pid = fork ();
1007     if (pid == -1) {
1008       perror ("fork");
1009       return -1;
1010     }
1011
1012     if (pid == 0) {             /* Child process. */
1013       close (fd[1]);
1014       if (dup2 (fd[0], 0) < 0) {
1015         perror ("dup2 of stdin failed");
1016         _exit (1);
1017       }
1018
1019       r = system (pipecmd);
1020       if (r == -1) {
1021         perror (pipecmd);
1022         _exit (1);
1023       }
1024       _exit (WEXITSTATUS (r));
1025     }
1026
1027     if ((stdout_saved_fd = dup (1)) < 0) {
1028       perror ("failed to dup stdout");
1029       return -1;
1030     }
1031     close (fd[0]);
1032     if (dup2 (fd[1], 1) < 0) {
1033       perror ("failed to dup stdout");
1034       close (stdout_saved_fd);
1035       return -1;
1036     }
1037     close (fd[1]);
1038   }
1039
1040   for (argc = 0; argv[argc] != NULL; ++argc)
1041     ;
1042
1043   /* If --remote was set, then send this command to a remote process. */
1044   if (remote_control)
1045     r = rc_remote (remote_control, cmd, argc, argv, exit_on_error);
1046
1047   /* Otherwise execute it locally. */
1048   else if (STRCASEEQ (cmd, "help")) {
1049     if (argc == 0) {
1050       list_commands ();
1051       r = 0;
1052     } else
1053       r = display_command (argv[0]);
1054   }
1055   else if (STRCASEEQ (cmd, "quit") ||
1056            STRCASEEQ (cmd, "exit") ||
1057            STRCASEEQ (cmd, "q")) {
1058     quit = 1;
1059     r = 0;
1060   }
1061   else if (STRCASEEQ (cmd, "alloc") ||
1062            STRCASEEQ (cmd, "allocate"))
1063     r = do_alloc (cmd, argc, argv);
1064   else if (STRCASEEQ (cmd, "copy-in") ||
1065            STRCASEEQ (cmd, "copy_in"))
1066     r = do_copy_in (cmd, argc, argv);
1067   else if (STRCASEEQ (cmd, "copy-out") ||
1068            STRCASEEQ (cmd, "copy_out"))
1069     r = do_copy_out (cmd, argc, argv);
1070   else if (STRCASEEQ (cmd, "echo"))
1071     r = do_echo (cmd, argc, argv);
1072   else if (STRCASEEQ (cmd, "edit") ||
1073            STRCASEEQ (cmd, "vi") ||
1074            STRCASEEQ (cmd, "emacs"))
1075     r = do_edit (cmd, argc, argv);
1076   else if (STRCASEEQ (cmd, "lcd"))
1077     r = do_lcd (cmd, argc, argv);
1078   else if (STRCASEEQ (cmd, "glob"))
1079     r = do_glob (cmd, argc, argv);
1080   else if (STRCASEEQ (cmd, "man") ||
1081            STRCASEEQ (cmd, "manual"))
1082     r = do_man (cmd, argc, argv);
1083   else if (STRCASEEQ (cmd, "more") ||
1084            STRCASEEQ (cmd, "less"))
1085     r = do_more (cmd, argc, argv);
1086   else if (STRCASEEQ (cmd, "reopen"))
1087     r = do_reopen (cmd, argc, argv);
1088   else if (STRCASEEQ (cmd, "sparse"))
1089     r = do_sparse (cmd, argc, argv);
1090   else if (STRCASEEQ (cmd, "supported"))
1091     r = do_supported (cmd, argc, argv);
1092   else if (STRCASEEQ (cmd, "time"))
1093     r = do_time (cmd, argc, argv);
1094   else
1095     r = run_action (cmd, argc, argv);
1096
1097   /* Always flush stdout after every command, so that messages, results
1098    * etc appear immediately.
1099    */
1100   if (fflush (stdout) == EOF) {
1101     perror ("failed to flush standard output");
1102     return -1;
1103   }
1104
1105   if (pipecmd) {
1106     close (1);
1107     if (dup2 (stdout_saved_fd, 1) < 0) {
1108       perror ("failed to dup2 standard output");
1109       r = -1;
1110     }
1111     close (stdout_saved_fd);
1112     if (waitpid (pid, NULL, 0) < 0) {
1113       perror ("waiting for command to complete");
1114       r = -1;
1115     }
1116   }
1117
1118   return r;
1119 }
1120
1121 void
1122 list_builtin_commands (void)
1123 {
1124   /* help, man and quit should appear at the top */
1125   printf ("%-20s %s\n",
1126           "help", _("display a list of commands or help on a command"));
1127   printf ("%-20s %s\n",
1128           "man", _("read the manual"));
1129   printf ("%-20s %s\n",
1130           "quit", _("quit guestfish"));
1131
1132   printf ("%-20s %s\n",
1133           "alloc", _("allocate an image"));
1134   printf ("%-20s %s\n",
1135           "copy-in", _("copy files into an image"));
1136   printf ("%-20s %s\n",
1137           "copy-out", _("copy files out of an image"));
1138   printf ("%-20s %s\n",
1139           "echo", _("display a line of text"));
1140   printf ("%-20s %s\n",
1141           "edit", _("edit a file in the image"));
1142   printf ("%-20s %s\n",
1143           "lcd", _("local change directory"));
1144   printf ("%-20s %s\n",
1145           "glob", _("expand wildcards in command"));
1146   printf ("%-20s %s\n",
1147           "more", _("view a file in the pager"));
1148   printf ("%-20s %s\n",
1149           "reopen", _("close and reopen libguestfs handle"));
1150   printf ("%-20s %s\n",
1151           "sparse", _("allocate a sparse image file"));
1152   printf ("%-20s %s\n",
1153           "supported", _("list supported groups of commands"));
1154   printf ("%-20s %s\n",
1155           "time", _("measure time taken to run command"));
1156
1157   /* actions are printed after this (see list_commands) */
1158 }
1159
1160 int
1161 display_builtin_command (const char *cmd)
1162 {
1163   /* help for actions is auto-generated, see display_command */
1164
1165   if (STRCASEEQ (cmd, "alloc") ||
1166       STRCASEEQ (cmd, "allocate")) {
1167     printf (_("alloc - allocate an image\n"
1168               "     alloc <filename> <size>\n"
1169               "\n"
1170               "    This creates an empty (zeroed) file of the given size,\n"
1171               "    and then adds so it can be further examined.\n"
1172               "\n"
1173               "    For more advanced image creation, see qemu-img utility.\n"
1174               "\n"
1175               "    Size can be specified using standard suffixes, eg. '1M'.\n"
1176               ));
1177     return 0;
1178   }
1179   else if (STRCASEEQ (cmd, "copy-in") ||
1180            STRCASEEQ (cmd, "copy_in")) {
1181     printf (_("copy-in - copy files into an image\n"
1182               "     copy-in <local> [<local> ...] <remotedir>\n"
1183               "\n"
1184               "    Copy local files or directories recursively into the\n"
1185               "    image, placing them on a remote directory.\n"
1186               ));
1187     return 0;
1188   }
1189   else if (STRCASEEQ (cmd, "copy-out") ||
1190            STRCASEEQ (cmd, "copy_out")) {
1191     printf (_("copy-out - copy files out of an image\n"
1192               "     copy-out <remote> [<remote> ...] <localdir>\n"
1193               "\n"
1194               "    Copy remote files or directories recursively out of the\n"
1195               "    image, placing them in a local directory.\n"
1196               ));
1197     return 0;
1198   }
1199   else if (STRCASEEQ (cmd, "echo")) {
1200     printf (_("echo - display a line of text\n"
1201               "     echo [<params> ...]\n"
1202               "\n"
1203               "    This echos the parameters to the terminal.\n"));
1204     return 0;
1205   }
1206   else if (STRCASEEQ (cmd, "edit") ||
1207            STRCASEEQ (cmd, "vi") ||
1208            STRCASEEQ (cmd, "emacs")) {
1209     printf (_("edit - edit a file in the image\n"
1210               "     edit <filename>\n"
1211               "\n"
1212               "    This is used to edit a file.\n"
1213               "\n"
1214               "    It is the equivalent of (and is implemented by)\n"
1215               "    running \"cat\", editing locally, and then \"write\".\n"
1216               "\n"
1217               "    Normally it uses $EDITOR, but if you use the aliases\n"
1218               "    \"vi\" or \"emacs\" you will get those editors.\n"
1219               "\n"
1220               "    NOTE: This will not work reliably for large files\n"
1221               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
1222     return 0;
1223   }
1224   else if (STRCASEEQ (cmd, "lcd")) {
1225     printf (_("lcd - local change directory\n"
1226               "    lcd <directory>\n"
1227               "\n"
1228               "    Change guestfish's current directory. This command is\n"
1229               "    useful if you want to download files to a particular\n"
1230               "    place.\n"));
1231     return 0;
1232   }
1233   else if (STRCASEEQ (cmd, "glob")) {
1234     printf (_("glob - expand wildcards in command\n"
1235               "    glob <command> [<args> ...]\n"
1236               "\n"
1237               "    Glob runs <command> with wildcards expanded in any\n"
1238               "    command args.  Note that the command is run repeatedly\n"
1239               "    once for each expanded argument.\n"));
1240     return 0;
1241   }
1242   else if (STRCASEEQ (cmd, "man") ||
1243            STRCASEEQ (cmd, "manual")) {
1244     printf (_("man - read the manual\n"
1245               "    man\n"
1246               "\n"
1247               "    Opens the manual page for guestfish.\n"));
1248     return 0;
1249   }
1250   else if (STRCASEEQ (cmd, "help")) {
1251     printf (_("help - display a list of commands or help on a command\n"
1252               "     help cmd\n"
1253               "     help\n"));
1254     return 0;
1255   }
1256   else if (STRCASEEQ (cmd, "more") ||
1257            STRCASEEQ (cmd, "less")) {
1258     printf (_("more - view a file in the pager\n"
1259               "     more <filename>\n"
1260               "\n"
1261               "    This is used to view a file in the pager.\n"
1262               "\n"
1263               "    It is the equivalent of (and is implemented by)\n"
1264               "    running \"cat\" and using the pager.\n"
1265               "\n"
1266               "    Normally it uses $PAGER, but if you use the alias\n"
1267               "    \"less\" then it always uses \"less\".\n"
1268               "\n"
1269               "    NOTE: This will not work reliably for large files\n"
1270               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
1271     return 0;
1272   }
1273   else if (STRCASEEQ (cmd, "quit") ||
1274            STRCASEEQ (cmd, "exit") ||
1275            STRCASEEQ (cmd, "q")) {
1276     printf (_("quit - quit guestfish\n"
1277               "     quit\n"));
1278     return 0;
1279   }
1280   else if (STRCASEEQ (cmd, "reopen")) {
1281     printf (_("reopen - close and reopen the libguestfs handle\n"
1282               "     reopen\n"
1283               "\n"
1284               "Close and reopen the libguestfs handle.  It is not necessary to use\n"
1285               "this normally, because the handle is closed properly when guestfish\n"
1286               "exits.  However this is occasionally useful for testing.\n"));
1287     return 0;
1288   }
1289   else if (STRCASEEQ (cmd, "sparse")) {
1290     printf (_("sparse - allocate a sparse image file\n"
1291               "     sparse <filename> <size>\n"
1292               "\n"
1293               "    This creates an empty sparse file of the given size,\n"
1294               "    and then adds so it can be further examined.\n"
1295               "\n"
1296               "    In all respects it works the same as the 'alloc'\n"
1297               "    command, except that the image file is allocated\n"
1298               "    sparsely, which means that disk blocks are not assigned\n"
1299               "    to the file until they are needed.  Sparse disk files\n"
1300               "    only use space when written to, but they are slower\n"
1301               "    and there is a danger you could run out of real disk\n"
1302               "    space during a write operation.\n"
1303               "\n"
1304               "    For more advanced image creation, see qemu-img utility.\n"
1305               "\n"
1306               "    Size can be specified using standard suffixes, eg. '1M'.\n"
1307               ));
1308     return 0;
1309   }
1310   else if (STRCASEEQ (cmd, "supported")) {
1311     printf (_("supported - list supported groups of commands\n"
1312               "     supported\n"
1313               "\n"
1314               "    This command returns a list of the optional groups\n"
1315               "    known to the daemon, and indicates which ones are\n"
1316               "    supported by this build of the libguestfs appliance.\n"
1317               "\n"
1318               "    See also guestfs(3) section AVAILABILITY.\n"
1319               ));
1320     return 0;
1321   }
1322   else if (STRCASEEQ (cmd, "time")) {
1323     printf (_("time - measure time taken to run command\n"
1324               "    time <command> [<args> ...]\n"
1325               "\n"
1326               "    This runs <command> as usual, and prints the elapsed\n"
1327               "    time afterwards.\n"));
1328     return 0;
1329   }
1330   else {
1331     fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
1332              cmd);
1333     return -1;
1334   }
1335 }
1336
1337 /* This is printed when the user types in an unknown command for the
1338  * first command issued.  A common case is the user doing:
1339  *   guestfish disk.img
1340  * expecting guestfish to open 'disk.img' (in fact, this tried to
1341  * run a command 'disk.img').
1342  */
1343 void
1344 extended_help_message (void)
1345 {
1346   fprintf (stderr,
1347            _("Did you mean to open a disk image?  guestfish -a disk.img\n"
1348              "For a list of commands:             guestfish -h\n"
1349              "For complete documentation:         man guestfish\n"));
1350 }
1351
1352 void
1353 free_strings (char **argv)
1354 {
1355   int argc;
1356
1357   for (argc = 0; argv[argc] != NULL; ++argc)
1358     free (argv[argc]);
1359   free (argv);
1360 }
1361
1362 int
1363 count_strings (char *const *argv)
1364 {
1365   int c;
1366
1367   for (c = 0; argv[c]; ++c)
1368     ;
1369   return c;
1370 }
1371
1372 void
1373 print_strings (char *const *argv)
1374 {
1375   int argc;
1376
1377   for (argc = 0; argv[argc] != NULL; ++argc)
1378     printf ("%s\n", argv[argc]);
1379 }
1380
1381 void
1382 print_table (char *const *argv)
1383 {
1384   int i;
1385
1386   for (i = 0; argv[i] != NULL; i += 2)
1387     printf ("%s: %s\n", argv[i], argv[i+1]);
1388 }
1389
1390 int
1391 is_true (const char *str)
1392 {
1393   return
1394     STRCASENEQ (str, "0") &&
1395     STRCASENEQ (str, "f") &&
1396     STRCASENEQ (str, "false") &&
1397     STRCASENEQ (str, "n") &&
1398     STRCASENEQ (str, "no");
1399 }
1400
1401 /* Free strings from a non-NULL terminated char** */
1402 static void
1403 free_n_strings (char **str, size_t len)
1404 {
1405   size_t i;
1406
1407   for (i = 0; i < len; i++) {
1408     free (str[i]);
1409   }
1410   free (str);
1411 }
1412
1413 char **
1414 parse_string_list (const char *str)
1415 {
1416   char **argv = NULL;
1417   size_t argv_len = 0;
1418
1419   /* Current position pointer */
1420   const char *p = str;
1421
1422   /* Token might be simple:
1423    *  Token
1424    * or be quoted:
1425    *  'This is a single token'
1426    * or contain embedded single-quoted sections:
1427    *  This' is a sing'l'e to'ken
1428    *
1429    * The latter may seem over-complicated, but it's what a normal shell does.
1430    * Not doing it risks surprising somebody.
1431    *
1432    * This outer loop is over complete tokens.
1433    */
1434   while (*p) {
1435     char *tok = NULL;
1436     size_t tok_len = 0;
1437
1438     /* Skip leading whitespace */
1439     p += strspn (p, " \t");
1440
1441     char in_quote = 0;
1442
1443     /* This loop is over token 'fragments'. A token can be in multiple bits if
1444      * it contains single quotes. We also treat both sides of an escaped quote
1445      * as separate fragments because we can't just copy it: we have to remove
1446      * the \.
1447      */
1448     while (*p && (!c_isblank (*p) || in_quote)) {
1449       const char *end = p;
1450
1451       /* Check if the fragment starts with a quote */
1452       if ('\'' == *p) {
1453         /* Toggle in_quote */
1454         in_quote = !in_quote;
1455
1456         /* Skip the quote */
1457         p++; end++;
1458       }
1459
1460       /* If we're in a quote, look for an end quote */
1461       if (in_quote) {
1462         end += strcspn (end, "'");
1463       }
1464
1465       /* Otherwise, look for whitespace or a quote */
1466       else {
1467         end += strcspn (end, " \t'");
1468       }
1469
1470       /* Grow the token to accommodate the fragment */
1471       size_t tok_end = tok_len;
1472       tok_len += end - p;
1473       char *tok_new = realloc (tok, tok_len + 1);
1474       if (NULL == tok_new) {
1475         perror ("realloc");
1476         free_n_strings (argv, argv_len);
1477         free (tok);
1478         exit (EXIT_FAILURE);
1479       }
1480       tok = tok_new;
1481
1482       /* Check if we stopped on an escaped quote */
1483       if ('\'' == *end && end != p && *(end-1) == '\\') {
1484         /* Add everything before \' to the token */
1485         memcpy (&tok[tok_end], p, end - p - 1);
1486
1487         /* Add the quote */
1488         tok[tok_len-1] = '\'';
1489
1490         /* Already processed the quote */
1491         p = end + 1;
1492       }
1493
1494       else {
1495         /* Add the whole fragment */
1496         memcpy (&tok[tok_end], p, end - p);
1497
1498         p = end;
1499       }
1500     }
1501
1502     /* We've reached the end of a token. We shouldn't still be in quotes. */
1503     if (in_quote) {
1504       fprintf (stderr, _("Runaway quote in string \"%s\"\n"), str);
1505
1506       free_n_strings (argv, argv_len);
1507
1508       return NULL;
1509     }
1510
1511     /* Add this token if there is one. There might not be if there was
1512      * whitespace at the end of the input string */
1513     if (tok) {
1514       /* Add the NULL terminator */
1515       tok[tok_len] = '\0';
1516
1517       /* Add the argument to the argument list */
1518       argv_len++;
1519       char **argv_new = realloc (argv, sizeof (*argv) * argv_len);
1520       if (NULL == argv_new) {
1521         perror ("realloc");
1522         free_n_strings (argv, argv_len-1);
1523         free (tok);
1524         exit (EXIT_FAILURE);
1525       }
1526       argv = argv_new;
1527
1528       argv[argv_len-1] = tok;
1529     }
1530   }
1531
1532   /* NULL terminate the argument list */
1533   argv_len++;
1534   char **argv_new = realloc (argv, sizeof (*argv) * argv_len);
1535   if (NULL == argv_new) {
1536     perror ("realloc");
1537     free_n_strings (argv, argv_len-1);
1538     exit (EXIT_FAILURE);
1539   }
1540   argv = argv_new;
1541
1542   argv[argv_len-1] = NULL;
1543
1544   return argv;
1545 }
1546
1547 #ifdef HAVE_LIBREADLINE
1548 static char histfile[1024];
1549 static int nr_history_lines = 0;
1550 #endif
1551
1552 static void
1553 initialize_readline (void)
1554 {
1555 #ifdef HAVE_LIBREADLINE
1556   const char *home;
1557
1558   home = getenv ("HOME");
1559   if (home) {
1560     snprintf (histfile, sizeof histfile, "%s/.guestfish", home);
1561     using_history ();
1562     (void) read_history (histfile);
1563   }
1564
1565   rl_readline_name = "guestfish";
1566   rl_attempted_completion_function = do_completion;
1567
1568   /* Note that .inputrc (or /etc/inputrc) is not read until the first
1569    * call the readline(), which happens later.  Therefore, these
1570    * provide default values which can be overridden by the user if
1571    * they wish.
1572    */
1573   (void) rl_variable_bind ("completion-ignore-case", "on");
1574 #endif
1575 }
1576
1577 static void
1578 cleanup_readline (void)
1579 {
1580 #ifdef HAVE_LIBREADLINE
1581   int fd;
1582
1583   if (histfile[0] != '\0') {
1584     fd = open (histfile, O_WRONLY|O_CREAT, 0644);
1585     if (fd == -1) {
1586       perror (histfile);
1587       return;
1588     }
1589     close (fd);
1590
1591 #ifdef HAVE_APPEND_HISTORY
1592     (void) append_history (nr_history_lines, histfile);
1593 #else
1594     (void) write_history (histfile);
1595 #endif
1596   }
1597 #endif
1598 }
1599
1600 #ifdef HAVE_LIBREADLINE
1601 static void
1602 add_history_line (const char *line)
1603 {
1604   add_history (line);
1605   nr_history_lines++;
1606 }
1607 #endif
1608
1609 int
1610 xwrite (int fd, const void *v_buf, size_t len)
1611 {
1612   int r;
1613   const char *buf = v_buf;
1614
1615   while (len > 0) {
1616     r = write (fd, buf, len);
1617     if (r == -1) {
1618       perror ("write");
1619       return -1;
1620     }
1621     buf += r;
1622     len -= r;
1623   }
1624
1625   return 0;
1626 }
1627
1628 /* Resolve the special "win:..." form for Windows-specific paths.
1629  * This always returns a newly allocated string which is freed by the
1630  * caller function in "cmds.c".
1631  */
1632 char *
1633 resolve_win_path (const char *path)
1634 {
1635   char *ret;
1636   size_t i;
1637
1638   if (STRCASENEQLEN (path, "win:", 4)) {
1639     ret = strdup (path);
1640     if (ret == NULL)
1641       perror ("strdup");
1642     return ret;
1643   }
1644
1645   path += 4;
1646
1647   /* Drop drive letter, if it's "C:". */
1648   if (STRCASEEQLEN (path, "c:", 2))
1649     path += 2;
1650
1651   if (!*path) {
1652     ret = strdup ("/");
1653     if (ret == NULL)
1654       perror ("strdup");
1655     return ret;
1656   }
1657
1658   ret = strdup (path);
1659   if (ret == NULL) {
1660     perror ("strdup");
1661     return NULL;
1662   }
1663
1664   /* Blindly convert any backslashes into forward slashes.  Is this good? */
1665   for (i = 0; i < strlen (ret); ++i)
1666     if (ret[i] == '\\')
1667       ret[i] = '/';
1668
1669   char *t = guestfs_case_sensitive_path (g, ret);
1670   free (ret);
1671   ret = t;
1672
1673   return ret;
1674 }
1675
1676 /* Resolve the special FileIn paths ("-" or "-<<END" or filename).
1677  * The caller (cmds.c) will call free_file_in after the command has
1678  * run which should clean up resources.
1679  */
1680 static char *file_in_heredoc (const char *endmarker);
1681 static char *file_in_tmpfile = NULL;
1682
1683 char *
1684 file_in (const char *arg)
1685 {
1686   char *ret;
1687
1688   if (STREQ (arg, "-")) {
1689     ret = strdup ("/dev/stdin");
1690     if (!ret) {
1691       perror ("strdup");
1692       return NULL;
1693     }
1694   }
1695   else if (STRPREFIX (arg, "-<<")) {
1696     const char *endmarker = &arg[3];
1697     if (*endmarker == '\0') {
1698       fprintf (stderr, "%s: missing end marker in -<< expression\n",
1699                program_name);
1700       return NULL;
1701     }
1702     ret = file_in_heredoc (endmarker);
1703     if (ret == NULL)
1704       return NULL;
1705   }
1706   else {
1707     ret = strdup (arg);
1708     if (!ret) {
1709       perror ("strdup");
1710       return NULL;
1711     }
1712   }
1713
1714   return ret;
1715 }
1716
1717 static char *
1718 file_in_heredoc (const char *endmarker)
1719 {
1720   static const char template[] = "/tmp/heredocXXXXXX";
1721   file_in_tmpfile = strdup (template);
1722   if (file_in_tmpfile == NULL) {
1723     perror ("strdup");
1724     return NULL;
1725   }
1726
1727   int fd = mkstemp (file_in_tmpfile);
1728   if (fd == -1) {
1729     perror ("mkstemp");
1730     goto error1;
1731   }
1732
1733   size_t markerlen = strlen (endmarker);
1734
1735   char buffer[BUFSIZ];
1736   int write_error = 0;
1737   while (fgets (buffer, sizeof buffer, stdin) != NULL) {
1738     /* Look for "END"<EOF> or "END\n" in input. */
1739     size_t blen = strlen (buffer);
1740     if (STREQLEN (buffer, endmarker, markerlen) &&
1741         (blen == markerlen ||
1742          (blen == markerlen+1 && buffer[markerlen] == '\n')))
1743       goto found_end;
1744
1745     if (xwrite (fd, buffer, blen) == -1) {
1746       if (!write_error) perror ("write");
1747       write_error = 1;
1748       /* continue reading up to the end marker */
1749     }
1750   }
1751
1752   /* Reached EOF of stdin without finding the end marker, which
1753    * is likely to be an error.
1754    */
1755   fprintf (stderr, "%s: end of input reached without finding '%s'\n",
1756            program_name, endmarker);
1757   goto error2;
1758
1759  found_end:
1760   if (write_error) {
1761     close (fd);
1762     goto error2;
1763   }
1764
1765   if (close (fd) == -1) {
1766     perror ("close");
1767     goto error2;
1768   }
1769
1770   return file_in_tmpfile;
1771
1772  error2:
1773   unlink (file_in_tmpfile);
1774
1775  error1:
1776   free (file_in_tmpfile);
1777   file_in_tmpfile = NULL;
1778   return NULL;
1779 }
1780
1781 void
1782 free_file_in (char *s)
1783 {
1784   if (file_in_tmpfile) {
1785     if (unlink (file_in_tmpfile) == -1)
1786       perror (file_in_tmpfile);
1787     file_in_tmpfile = NULL;
1788   }
1789
1790   /* Free the device or file name which was strdup'd in file_in().
1791    * Note it's not immediately clear, but for -<< heredocs,
1792    * s == file_in_tmpfile, so this frees up that buffer.
1793    */
1794   free (s);
1795 }
1796
1797 /* Resolve the special FileOut paths ("-" or filename).
1798  * The caller (cmds.c) will call free (str) after the command has run.
1799  */
1800 char *
1801 file_out (const char *arg)
1802 {
1803   char *ret;
1804
1805   if (STREQ (arg, "-"))
1806     ret = strdup ("/dev/stdout");
1807   else
1808     ret = strdup (arg);
1809
1810   if (!ret) {
1811     perror ("strdup");
1812     return NULL;
1813   }
1814   return ret;
1815 }
1816
1817 /* Read a passphrase ('Key') from /dev/tty with echo off.
1818  * The caller (cmds.c) will call free on the string afterwards.
1819  * Based on the code in cryptsetup file lib/utils.c.
1820  */
1821 char *
1822 read_key (const char *param)
1823 {
1824   FILE *infp, *outfp;
1825   struct termios orig, temp;
1826   char *ret = NULL;
1827
1828   /* Read and write to /dev/tty if available. */
1829   if (keys_from_stdin ||
1830       (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) {
1831     infp = stdin;
1832     outfp = stdout;
1833   }
1834
1835   /* Print the prompt and set no echo. */
1836   int tty = isatty (fileno (infp));
1837   int tcset = 0;
1838   if (tty) {
1839     fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param);
1840
1841     if (tcgetattr (fileno (infp), &orig) == -1) {
1842       perror ("tcgetattr");
1843       goto error;
1844     }
1845     memcpy (&temp, &orig, sizeof temp);
1846     temp.c_lflag &= ~ECHO;
1847
1848     tcsetattr (fileno (infp), TCSAFLUSH, &temp);
1849     tcset = 1;
1850   }
1851
1852   size_t n = 0;
1853   ssize_t len;
1854   len = getline (&ret, &n, infp);
1855   if (len == -1) {
1856     perror ("getline");
1857     ret = NULL;
1858     goto error;
1859   }
1860
1861   /* Remove the terminating \n if there is one. */
1862   if (len > 0 && ret[len-1] == '\n')
1863     ret[len-1] = '\0';
1864
1865  error:
1866   /* Restore echo, close file descriptor. */
1867   if (tty && tcset) {
1868     printf ("\n");
1869     tcsetattr (fileno (infp), TCSAFLUSH, &orig);
1870   }
1871
1872   if (infp != stdin)
1873     fclose (infp); /* outfp == infp, so this is closed also */
1874
1875   return ret;
1876 }