a25251621566ea8dcbca98c87de61cb332ee6a18
[libguestfs.git] / fish / fish.c
1 /* guestfish - the filesystem interactive shell
2  * Copyright (C) 2009 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 <string.h>
24 #include <unistd.h>
25 #include <getopt.h>
26
27 #include <guestfs.h>
28
29 #include "fish.h"
30
31 struct mp {
32   struct mp *next;
33   char *device;
34   char *mountpoint;
35 };
36
37 static void mount_mps (struct mp *mp);
38 static void interactive (void);
39 static void shell_script (void);
40 static void script (int prompt);
41 static void cmdline (char *argv[], int optind, int argc);
42 static void issue_command (const char *cmd, char *argv[]);
43
44 /* Currently open libguestfs handle. */
45 guestfs_h *g;
46 int g_launched = 0;
47
48 int quit = 0;
49
50 void
51 launch (void)
52 {
53   if (!g_launched) {
54     if (guestfs_launch (g) == -1)
55       exit (1);
56     if (guestfs_wait_ready (g) == -1)
57       exit (1);
58     g_launched = 1;
59   }
60 }
61
62 static void
63 usage (void)
64 {
65   fprintf (stderr,
66            "guestfish: guest filesystem shell\n"
67            "guestfish lets you edit virtual machine filesystems\n"
68            "Copyright (C) 2009 Red Hat Inc.\n"
69            "Usage:\n"
70            "  guestfish [--options] cmd [: cmd ...]\n"
71            "or for interactive use:\n"
72            "  guestfish\n"
73            "or from a shell script:\n"
74            "  guestfish <<EOF\n"
75            "  cmd\n"
76            "  ...\n"
77            "  EOF\n"
78            "Options:\n"
79            "  -h|--cmd-help       List available commands\n"
80            "  -h|--cmd-help cmd   Display detailed help on 'cmd'\n"
81            "  -a image            Add image\n"
82            "  -m dev[:mnt]        Mount dev on mnt (if omitted, /)\n"
83            /*"  --ro|-r             All mounts are read-only\n"*/
84            "  -v|--verbose        Verbose messages\n"
85            "For more information, see the manpage guestfish(1).\n");
86 }
87
88 int
89 main (int argc, char *argv[])
90 {
91   static const char *options = "a:h::m:v?";
92   static struct option long_options[] = {
93     { "add", 1, 0, 'a' },
94     { "cmd-help", 2, 0, 'h' },
95     { "help", 0, 0, '?' },
96     { "mount", 1, 0, 'm' },
97     { "verbose", 0, 0, 'v' },
98     { 0, 0, 0, 0 }
99   };
100   struct mp *mps = NULL;
101   struct mp *mp;
102   char *p;
103   int c;
104
105   /* guestfs_create is meant to be a lightweight operation, so
106    * it's OK to do it early here.
107    */
108   g = guestfs_create ();
109   if (g == NULL) {
110     fprintf (stderr, "guestfs_create: failed to create handle\n");
111     exit (1);
112   }
113
114   for (;;) {
115     c = getopt_long (argc, argv, options, long_options, NULL);
116     if (c == -1) break;
117
118     switch (c) {
119     case 'a':
120       if (access (optarg, R_OK) != 0) {
121         perror (optarg);
122         exit (1);
123       }
124       if (guestfs_add_drive (g, optarg) == -1)
125         exit (1);
126       break;
127
128     case 'h':
129       if (optarg)
130         display_command (optarg);
131       else if (argv[optind] && argv[optind][0] != '-')
132         display_command (argv[optind++]);
133       else
134         list_commands ();
135       exit (0);
136
137     case 'm':
138       mp = malloc (sizeof (struct mp));
139       if (!mp) {
140         perror ("malloc");
141         exit (1);
142       }
143       p = strchr (optarg, ':');
144       if (p) {
145         *p = '\0';
146         mp->mountpoint = p+1;
147       } else
148         mp->mountpoint = "/";
149       mp->device = optarg;
150       mp->next = mps;
151       mps = mp->next;
152       break;
153
154     case '?':
155       usage ();
156       exit (0);
157
158     default:
159       fprintf (stderr, "guestfish: unexpected command line option 0x%x\n", c);
160       exit (1);
161     }
162   }
163
164   /* If we've got mountpoints, we must launch the guest and mount them. */
165   if (mps != NULL) {
166     launch ();
167     mount_mps (mps);
168   }
169
170   /* Interactive, shell script, or command(s) on the command line? */
171   if (optind >= argc) {
172     if (isatty (0))
173       interactive ();
174     else
175       shell_script ();
176   }
177   else
178     cmdline (argv, optind, argc);
179
180   exit (0);
181 }
182
183 void
184 pod2text (const char *heading, const char *str)
185 {
186   FILE *fp;
187
188   fp = popen ("pod2text", "w");
189   if (fp == NULL) {
190     /* pod2text failed, maybe not found, so let's just print the
191      * source instead, since that's better than doing nothing.
192      */
193     printf ("%s\n\n%s\n", heading, str);
194     return;
195   }
196   fputs ("=head1 ", fp);
197   fputs (heading, fp);
198   fputs ("\n\n", fp);
199   fputs (str, fp);
200   pclose (fp);
201 }
202
203 /* List is built in reverse order, so mount them in reverse order. */
204 static void
205 mount_mps (struct mp *mp)
206 {
207   if (mp) {
208     mount_mps (mp->next);
209     if (guestfs_mount (g, mp->device, mp->mountpoint) == -1)
210       exit (1);
211   }
212 }
213
214 static void
215 interactive (void)
216 {
217   script (1);
218 }
219
220 static void
221 shell_script (void)
222 {
223   script (0);
224 }
225
226 static void
227 script (int prompt)
228 {
229   char buf[8192];
230   char *cmd;
231   char *argv[64];
232   int len, i;
233
234   if (prompt)
235     printf ("Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
236             "editing virtual machine filesystems.\n"
237             "\n"
238             "Type: 'help' for help with commands\n"
239             "      'quit' to quit the shell\n"
240             "\n");
241
242   while (!quit) {
243     if (prompt) printf ("><fs> ");
244     if (fgets (buf, sizeof buf, stdin) == NULL) {
245       quit = 1;
246       break;
247     }
248
249     len = strlen (buf);
250     if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
251
252     /* Split the buffer up at whitespace. */
253     cmd = strtok (buf, " \t");
254     if (cmd == NULL)
255       continue;
256
257     i = 0;
258     while (i < sizeof argv / sizeof argv[0] &&
259            (argv[i] = strtok (NULL, " \t")) != NULL)
260       i++;
261     if (i == sizeof argv / sizeof argv[0]) {
262       fprintf (stderr, "guestfish: too many arguments in command\n");
263       exit (1);
264     }
265
266     issue_command (cmd, argv);
267   }
268   if (prompt) printf ("\n");
269 }
270
271 static void
272 cmdline (char *argv[], int optind, int argc)
273 {
274   const char *cmd;
275   char **params;
276
277   if (optind >= argc) return;
278
279   cmd = argv[optind++];
280   if (strcmp (cmd, ":") == 0) {
281     fprintf (stderr, "guestfish: empty command on command line\n");
282     exit (1);
283   }
284   params = &argv[optind];
285
286   /* Search for end of command list or ":" ... */
287   while (optind < argc && strcmp (argv[optind], ":") != 0)
288     optind++;
289
290   if (optind == argc)
291     issue_command (cmd, params);
292   else {
293     argv[optind] = NULL;
294     issue_command (cmd, params);
295     cmdline (argv, optind+1, argc);
296   }
297 }
298
299 static void
300 issue_command (const char *cmd, char *argv[])
301 {
302   int i;
303
304   fprintf (stderr, "cmd = %s", cmd);
305   for (i = 0; argv[i] != NULL; ++i)
306     fprintf (stderr, ", arg[%d]=%s", i, argv[i]);
307   fprintf (stderr, "\n");
308
309
310
311
312
313 }