do_equal does not need to explicitly check for NEED_ROOT/ABS_PATH.
[libguestfs.git] / test-tool / test-tool.c
1 /* libguestfs-test-tool
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 <stdint.h>
24 #include <inttypes.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <getopt.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32
33 #include <guestfs.h>
34
35 #ifdef HAVE_GETTEXT
36 #include "gettext.h"
37 #define _(str) dgettext(PACKAGE, (str))
38 #define N_(str) dgettext(PACKAGE, (str))
39 #else
40 #define _(str) str
41 #define N_(str) str
42 #endif
43
44 #define DEFAULT_TIMEOUT 120
45
46 static const char *helper = DEFAULT_HELPER;
47 static int timeout = DEFAULT_TIMEOUT;
48 static char tmpf[] = "/tmp/libguestfs-test-tool-sda-XXXXXX";
49 static char isof[] = "/tmp/libguestfs-test-tool-iso-XXXXXX";
50 static guestfs_h *g;
51
52 static void preruncheck (void);
53 static void make_files (void);
54 static void set_qemu (const char *path, int use_wrapper);
55
56 static void
57 usage (void)
58 {
59   printf (_("libguestfs-test-tool: interactive test tool\n"
60             "Copyright (C) 2009 Red Hat Inc.\n"
61             "Usage:\n"
62             "  libguestfs-test-tool [--options]\n"
63             "Options:\n"
64             "  --help         Display usage\n"
65             "  --helper libguestfs-test-tool-helper\n"
66             "                 Helper program (default: %s)\n"
67             "  --qemudir dir  Specify QEMU source directory\n"
68             "  --qemu qemu    Specify QEMU binary\n"
69             "  --timeout n\n"
70             "  -t n           Set launch timeout (default: %d seconds)\n"
71             ),
72           DEFAULT_HELPER, DEFAULT_TIMEOUT);
73 }
74
75 int
76 main (int argc, char *argv[])
77 {
78   static const char *options = "?";
79   static const struct option long_options[] = {
80     { "help", 0, 0, '?' },
81     { "helper", 1, 0, 0 },
82     { "qemu", 1, 0, 0 },
83     { "qemudir", 1, 0, 0 },
84     { "timeout", 1, 0, 't' },
85     { 0, 0, 0, 0 }
86   };
87   int c;
88   int option_index;
89   extern char **environ;
90   int i;
91   struct guestfs_version *vers;
92   char *sfdisk_lines[] = { ",", NULL };
93   char *str;
94   /* XXX This is wrong if the user renames the helper. */
95   char *helper_args[] = { "/iso/libguestfs-test-tool-helper", NULL };
96
97   for (;;) {
98     c = getopt_long (argc, argv, options, long_options, &option_index);
99     if (c == -1) break;
100
101     switch (c) {
102     case 0:                     /* options which are long only */
103       if (strcmp (long_options[option_index].name, "helper") == 0)
104         helper = optarg;
105       else if (strcmp (long_options[option_index].name, "qemu") == 0)
106         set_qemu (optarg, 0);
107       else if (strcmp (long_options[option_index].name, "qemudir") == 0)
108         set_qemu (optarg, 1);
109       else {
110         fprintf (stderr,
111                  _("libguestfs-test-tool: unknown long option: %s (%d)\n"),
112                  long_options[option_index].name, option_index);
113         exit (1);
114       }
115       break;
116
117     case 't':
118       if (sscanf (optarg, "%d", &timeout) != 1 || timeout < 0) {
119         fprintf (stderr,
120                  _("libguestfs-test-tool: invalid timeout: %s\n"),
121                  optarg);
122         exit (1);
123       }
124       break;
125
126     case '?':
127       usage ();
128       exit (0);
129
130     default:
131       fprintf (stderr,
132                _("libguestfs-test-tool: unexpected command line option 0x%x\n"),
133                c);
134       exit (1);
135     }
136   }
137
138   preruncheck ();
139   make_files ();
140
141   printf ("===== Test starts here =====\n");
142
143   /* Must set LIBGUESTFS_DEBUG=1 */
144   setenv ("LIBGUESTFS_DEBUG", "1", 1);
145
146   /* Print out any environment variables which may relate to this test. */
147   for (i = 0; environ[i] != NULL; ++i)
148     if (strncmp (environ[i], "LIBGUESTFS_", 11) == 0)
149       printf ("%s\n", environ[i]);
150
151   /* Create the handle and configure it. */
152   g = guestfs_create ();
153   if (g == NULL) {
154     fprintf (stderr,
155              _("libguestfs-test-tool: failed to create libguestfs handle\n"));
156     exit (1);
157   }
158   if (guestfs_add_drive (g, tmpf) == -1) {
159     fprintf (stderr,
160              _("libguestfs-test-tool: failed to add drive '%s'\n"),
161              tmpf);
162     exit (1);
163   }
164   if (guestfs_add_drive (g, isof) == -1) {
165     fprintf (stderr,
166              _("libguestfs-test-tool: failed to add drive '%s'\n"),
167              isof);
168     exit (1);
169   }
170
171   /* Print any version info etc. */
172   vers = guestfs_version (g);
173   if (vers == NULL) {
174     fprintf (stderr, _("libguestfs-test-tool: guestfs_version failed\n"));
175     exit (1);
176   }
177   printf ("library version: %"PRIi64".%"PRIi64".%"PRIi64"%s\n",
178           vers->major, vers->minor, vers->release, vers->extra);
179   guestfs_free_version (vers);
180
181   printf ("guestfs_get_append: %s\n", guestfs_get_append (g) ? : "(null)");
182   printf ("guestfs_get_autosync: %d\n", guestfs_get_autosync (g));
183   printf ("guestfs_get_memsize: %d\n", guestfs_get_memsize (g));
184   printf ("guestfs_get_path: %s\n", guestfs_get_path (g));
185   printf ("guestfs_get_qemu: %s\n", guestfs_get_qemu (g));
186   printf ("guestfs_get_verbose: %d\n", guestfs_get_verbose (g));
187
188   /* Launch the guest handle. */
189   if (guestfs_launch (g) == -1) {
190     fprintf (stderr,
191              _("libguestfs-test-tool: failed to launch appliance\n"));
192     exit (1);
193   }
194
195   printf ("Launching appliance, timeout set to %d seconds.\n", timeout);
196   fflush (stdout);
197
198   alarm (timeout);
199
200   if (guestfs_wait_ready (g) == -1) {
201     fprintf (stderr,
202              _("libguestfs-test-tool: failed or timed out in 'wait_ready'\n"));
203     exit (1);
204   }
205
206   alarm (0);
207
208   printf ("Guest launched OK.\n");
209   fflush (stdout);
210
211   /* Create the filesystem and mount everything. */
212   if (guestfs_sfdiskM (g, "/dev/sda", sfdisk_lines) == -1) {
213     fprintf (stderr,
214              _("libguestfs-test-tool: failed to run sfdisk\n"));
215     exit (1);
216   }
217
218   if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1) {
219     fprintf (stderr,
220              _("libguestfs-test-tool: failed to mkfs.ext2\n"));
221     exit (1);
222   }
223
224   if (guestfs_mount (g, "/dev/sda1", "/") == -1) {
225     fprintf (stderr,
226              _("libguestfs-test-tool: failed to mount /dev/sda1 on /\n"));
227     exit (1);
228   }
229
230   if (guestfs_mkdir (g, "/iso") == -1) {
231     fprintf (stderr,
232              _("libguestfs-test-tool: failed to mkdir /iso\n"));
233     exit (1);
234   }
235
236   if (guestfs_mount (g, "/dev/sdb", "/iso") == -1) {
237     fprintf (stderr,
238              _("libguestfs-test-tool: failed to mount /dev/sdb on /iso\n"));
239     exit (1);
240   }
241
242   /* Let's now run some simple tests using the helper program. */
243   str = guestfs_command (g, helper_args);
244   if (str == NULL) {
245     fprintf (stderr,
246              _("libguestfs-test-tool: could not run helper program, or helper failed\n"));
247     exit (1);
248   }
249   free (str);
250
251   printf ("===== TEST FINISHED OK =====\n");
252   exit (0);
253 }
254
255 static char qemuwrapper[] = "/tmp/libguestfs-test-tool-wrapper-XXXXXX";
256
257 static void
258 cleanup_wrapper (void)
259 {
260   unlink (qemuwrapper);
261 }
262
263 /* Handle the --qemu and --qemudir parameters.  use_wrapper is true
264  * in the --qemudir (source directory) case, where we have to create
265  * a wrapper shell script.
266  */
267 static void
268 set_qemu (const char *path, int use_wrapper)
269 {
270   char buffer[PATH_MAX];
271   struct stat statbuf;
272   int fd;
273   FILE *fp;
274
275   if (getenv ("LIBGUESTFS_QEMU")) {
276     fprintf (stderr,
277     _("LIBGUESTFS_QEMU environment variable is already set, so\n"
278       "--qemu/--qemudir options cannot be used.\n"));
279     exit (1);
280   }
281
282   if (!use_wrapper) {
283     if (access (path, X_OK) == -1) {
284       fprintf (stderr,
285                _("Binary '%s' does not exist or is not executable\n"),
286                path);
287       exit (1);
288     }
289
290     setenv ("LIBGUESTFS_QEMU", path, 1);
291     return;
292   }
293
294   /* This should be a source directory, so check it. */
295   snprintf (buffer, sizeof buffer, "%s/pc-bios", path);
296   if (stat (buffer, &statbuf) == -1 ||
297       !S_ISDIR (statbuf.st_mode)) {
298     fprintf (stderr,
299              _("%s: does not look like a qemu source directory\n"),
300              path);
301     exit (1);
302   }
303
304   /* Make a wrapper script. */
305   fd = mkstemp (qemuwrapper);
306   if (fd == -1) {
307     perror (qemuwrapper);
308     exit (1);
309   }
310
311   fchmod (fd, 0700);
312
313   fp = fdopen (fd, "w");
314   fprintf (fp,
315            "#!/bin/sh -\n"
316            "qemudir='%s'\n"
317            "\"$qemudir\"/",
318            path);
319
320   /* Select the right qemu binary for the wrapper script. */
321 #ifdef __i386__
322   fprintf (fp, "i386-softmmu/qemu");
323 #else
324   fprintf (fp, host_cpu "-softmmu/qemu-system-" host_cpu);
325 #endif
326
327   fprintf (fp, " -L \"$qemudir\"/pc-bios \"$@\"\n");
328
329   fclose (fp);
330
331   setenv ("LIBGUESTFS_QEMU", qemuwrapper, 1);
332   atexit (cleanup_wrapper);
333 }
334
335 /* After getting the command line args, but before running
336  * anything, we check everything is in place to do the tests.
337  */
338 static void
339 preruncheck (void)
340 {
341   int r;
342   FILE *fp;
343   char cmd[256];
344   char buffer[1024];
345
346   if (access (helper, R_OK) == -1) {
347     fprintf (stderr,
348     _("Test tool helper program 'libguestfs-test-tool-helper' is not\n"
349       "available.  Expected to find it in '%s'\n"
350       "\n"
351       "Use the --helper option to specify the location of this program.\n"),
352              helper);
353     exit (1);
354   }
355
356   snprintf (cmd, sizeof cmd, "file '%s'", helper);
357   fp = popen (cmd, "r");
358   if (fp == NULL) {
359     perror (cmd);
360     exit (1);
361   }
362   r = fread (buffer, 1, sizeof buffer - 1, fp);
363   if (r == 0) {
364     fprintf (stderr, _("command failed: %s"), cmd);
365     exit (1);
366   }
367   pclose (fp);
368   buffer[r] = '\0';
369
370   if (strstr (buffer, "statically linked") == NULL) {
371     fprintf (stderr,
372     _("Test tool helper program %s\n"
373       "is not statically linked.  This is a build error when this test tool\n"
374       "was built.\n"),
375              helper);
376     exit (1);
377   }
378 }
379
380 static void
381 cleanup_tmpfiles (void)
382 {
383   unlink (tmpf);
384   unlink (isof);
385 }
386
387 static void
388 make_files (void)
389 {
390   int fd, r;
391   char cmd[256];
392
393   /* Make the ISO which will contain the helper program. */
394   fd = mkstemp (isof);
395   if (fd == -1) {
396     perror (isof);
397     exit (1);
398   }
399   close (fd);
400
401   snprintf (cmd, sizeof cmd, "mkisofs -quiet -rJT -o '%s' '%s'",
402             isof, helper);
403   r = system (cmd);
404   if (r == -1 || WEXITSTATUS(r) != 0) {
405     fprintf (stderr,
406              _("mkisofs command failed: %s\n"), cmd);
407     exit (1);
408   }
409
410   /* Allocate the sparse file for /dev/sda. */
411   fd = mkstemp (tmpf);
412   if (fd == -1) {
413     perror (tmpf);
414     unlink (isof);
415     exit (1);
416   }
417
418   if (lseek (fd, 100 * 1024 * 1024 - 1, SEEK_SET) == -1) {
419     perror ("lseek");
420     close (fd);
421     unlink (tmpf);
422     unlink (isof);
423     exit (1);
424   }
425
426   if (write (fd, "\0", 1) == -1) {
427     perror ("write");
428     close (fd);
429     unlink (tmpf);
430     unlink (isof);
431     exit (1);
432   }
433
434   close (fd);
435
436   atexit (cleanup_tmpfiles);    /* Removes tmpf and isof. */
437 }