daemon: debug segv correct use of dereferencing NULL.
[libguestfs.git] / test-tool / test-tool.c
1 /* libguestfs-test-tool
2  * Copyright (C) 2009-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 #include <locale.h>
33 #include <limits.h>
34
35 #include <guestfs.h>
36
37 #ifdef HAVE_GETTEXT
38 #include "gettext.h"
39 #define _(str) dgettext(PACKAGE, (str))
40 //#define N_(str) dgettext(PACKAGE, (str))
41 #else
42 #define _(str) str
43 //#define N_(str) str
44 #endif
45
46 #if !ENABLE_NLS
47 #undef textdomain
48 #define textdomain(Domainname) /* empty */
49 #undef bindtextdomain
50 #define bindtextdomain(Domainname, Dirname) /* empty */
51 #endif
52
53 #define STREQ(a,b) (strcmp((a),(b)) == 0)
54 //#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
55 //#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
56 //#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
57 #define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
58 //#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
59 //#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
60 //#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
61 //#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
62
63 #ifndef P_tmpdir
64 #define P_tmpdir "/tmp"
65 #endif
66
67 #define DEFAULT_TIMEOUT 600
68
69 static int timeout = DEFAULT_TIMEOUT;
70 static char tmpf[] = P_tmpdir "/libguestfs-test-tool-sda-XXXXXX";
71 static guestfs_h *g;
72
73 static void make_files (void);
74 static void set_qemu (const char *path, int use_wrapper);
75
76 static void
77 usage (void)
78 {
79   printf (_("libguestfs-test-tool: interactive test tool\n"
80             "Copyright (C) 2009-2011 Red Hat Inc.\n"
81             "Usage:\n"
82             "  libguestfs-test-tool [--options]\n"
83             "Options:\n"
84             "  --help         Display usage\n"
85             "  --qemudir dir  Specify QEMU source directory\n"
86             "  --qemu qemu    Specify QEMU binary\n"
87             "  --timeout n\n"
88             "  -t n           Set launch timeout (default: %d seconds)\n"
89             ),
90           DEFAULT_TIMEOUT);
91 }
92
93 extern char **environ;
94
95 int
96 main (int argc, char *argv[])
97 {
98   setlocale (LC_ALL, "");
99   bindtextdomain (PACKAGE, LOCALEBASEDIR);
100   textdomain (PACKAGE);
101
102   static const char *options = "t:?";
103   static const struct option long_options[] = {
104     { "help", 0, 0, '?' },
105     { "qemu", 1, 0, 0 },
106     { "qemudir", 1, 0, 0 },
107     { "timeout", 1, 0, 't' },
108     { 0, 0, 0, 0 }
109   };
110   int c;
111   int option_index;
112   int i;
113   struct guestfs_version *vers;
114   char *p;
115
116   for (;;) {
117     c = getopt_long (argc, argv, options, long_options, &option_index);
118     if (c == -1) break;
119
120     switch (c) {
121     case 0:                     /* options which are long only */
122       if (STREQ (long_options[option_index].name, "qemu"))
123         set_qemu (optarg, 0);
124       else if (STREQ (long_options[option_index].name, "qemudir"))
125         set_qemu (optarg, 1);
126       else {
127         fprintf (stderr,
128                  _("libguestfs-test-tool: unknown long option: %s (%d)\n"),
129                  long_options[option_index].name, option_index);
130         exit (EXIT_FAILURE);
131       }
132       break;
133
134     case 't':
135       if (sscanf (optarg, "%d", &timeout) != 1 || timeout < 0) {
136         fprintf (stderr,
137                  _("libguestfs-test-tool: invalid timeout: %s\n"),
138                  optarg);
139         exit (EXIT_FAILURE);
140       }
141       break;
142
143     case '?':
144       usage ();
145       exit (EXIT_SUCCESS);
146
147     default:
148       fprintf (stderr,
149                _("libguestfs-test-tool: unexpected command line option 0x%x\n"),
150                c);
151       exit (EXIT_FAILURE);
152     }
153   }
154
155   make_files ();
156
157   printf ("===== Test starts here =====\n");
158
159   /* Must set LIBGUESTFS_DEBUG=1 */
160   setenv ("LIBGUESTFS_DEBUG", "1", 1);
161
162   /* Print out any environment variables which may relate to this test. */
163   for (i = 0; environ[i] != NULL; ++i)
164     if (STREQLEN (environ[i], "LIBGUESTFS_", 11))
165       printf ("%s\n", environ[i]);
166   for (i = 0; environ[i] != NULL; ++i)
167     if (STREQLEN (environ[i], "FEBOOTSTRAP_", 12))
168       printf ("%s\n", environ[i]);
169   printf ("TMPDIR=%s\n", getenv ("TMPDIR") ? : "(not set)");
170
171   /* Create the handle and configure it. */
172   g = guestfs_create ();
173   if (g == NULL) {
174     fprintf (stderr,
175              _("libguestfs-test-tool: failed to create libguestfs handle\n"));
176     exit (EXIT_FAILURE);
177   }
178   if (guestfs_add_drive_opts (g, tmpf,
179                               GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
180                               -1) == -1) {
181     fprintf (stderr,
182              _("libguestfs-test-tool: failed to add drive '%s'\n"),
183              tmpf);
184     exit (EXIT_FAILURE);
185   }
186
187   /* Print any version info etc. */
188   vers = guestfs_version (g);
189   if (vers == NULL) {
190     fprintf (stderr, _("libguestfs-test-tool: guestfs_version failed\n"));
191     exit (EXIT_FAILURE);
192   }
193   printf ("library version: %"PRIi64".%"PRIi64".%"PRIi64"%s\n",
194           vers->major, vers->minor, vers->release, vers->extra);
195   guestfs_free_version (vers);
196
197   printf ("guestfs_get_append: %s\n", guestfs_get_append (g) ? : "(null)");
198   p = guestfs_get_attach_method (g);
199   printf ("guestfs_get_attach_method: %s\n", p ? : "(null)");
200   free (p);
201   printf ("guestfs_get_autosync: %d\n", guestfs_get_autosync (g));
202   printf ("guestfs_get_direct: %d\n", guestfs_get_direct (g));
203   printf ("guestfs_get_memsize: %d\n", guestfs_get_memsize (g));
204   printf ("guestfs_get_network: %d\n", guestfs_get_network (g));
205   printf ("guestfs_get_path: %s\n", guestfs_get_path (g));
206   printf ("guestfs_get_pgroup: %d\n", guestfs_get_pgroup (g));
207   printf ("guestfs_get_qemu: %s\n", guestfs_get_qemu (g));
208   printf ("guestfs_get_recovery_proc: %d\n", guestfs_get_recovery_proc (g));
209   printf ("guestfs_get_selinux: %d\n", guestfs_get_selinux (g));
210   printf ("guestfs_get_smp: %d\n", guestfs_get_smp (g));
211   printf ("guestfs_get_trace: %d\n", guestfs_get_trace (g));
212   printf ("guestfs_get_verbose: %d\n", guestfs_get_verbose (g));
213
214   printf ("host_cpu: %s\n", host_cpu);
215
216   /* Launch the guest handle. */
217   printf ("Launching appliance, timeout set to %d seconds.\n", timeout);
218   fflush (stdout);
219
220   alarm (timeout);
221
222   if (guestfs_launch (g) == -1) {
223     fprintf (stderr,
224              _("libguestfs-test-tool: failed to launch appliance\n"));
225     exit (EXIT_FAILURE);
226   }
227
228   alarm (0);
229
230   printf ("Guest launched OK.\n");
231   fflush (stdout);
232
233   /* Create the filesystem and mount everything. */
234   if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1) {
235     fprintf (stderr,
236              _("libguestfs-test-tool: failed to run part-disk\n"));
237     exit (EXIT_FAILURE);
238   }
239
240   if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1) {
241     fprintf (stderr,
242              _("libguestfs-test-tool: failed to mkfs.ext2\n"));
243     exit (EXIT_FAILURE);
244   }
245
246   if (guestfs_mount_options (g, "", "/dev/sda1", "/") == -1) {
247     fprintf (stderr,
248              _("libguestfs-test-tool: failed to mount /dev/sda1 on /\n"));
249     exit (EXIT_FAILURE);
250   }
251
252   /* Touch a file. */
253   if (guestfs_touch (g, "/hello") == -1) {
254     fprintf (stderr,
255              _("libguestfs-test-tool: failed to touch file\n"));
256     exit (EXIT_FAILURE);
257   }
258
259   /* Close the handle. */
260   guestfs_close (g);
261
262   /* Booted and performed some simple operations -- success! */
263   printf ("===== TEST FINISHED OK =====\n");
264   exit (EXIT_SUCCESS);
265 }
266
267 static char qemuwrapper[] = P_tmpdir "/libguestfs-test-tool-wrapper-XXXXXX";
268
269 static void
270 cleanup_wrapper (void)
271 {
272   unlink (qemuwrapper);
273 }
274
275 /* Handle the --qemu and --qemudir parameters.  use_wrapper is true
276  * in the --qemudir (source directory) case, where we have to create
277  * a wrapper shell script.
278  */
279 static void
280 set_qemu (const char *path, int use_wrapper)
281 {
282   char buffer[PATH_MAX];
283   struct stat statbuf;
284   int fd;
285   FILE *fp;
286
287   if (getenv ("LIBGUESTFS_QEMU")) {
288     fprintf (stderr,
289     _("LIBGUESTFS_QEMU environment variable is already set, so\n"
290       "--qemu/--qemudir options cannot be used.\n"));
291     exit (EXIT_FAILURE);
292   }
293
294   if (!use_wrapper) {
295     if (access (path, X_OK) == -1) {
296       fprintf (stderr,
297                _("Binary '%s' does not exist or is not executable\n"),
298                path);
299       exit (EXIT_FAILURE);
300     }
301
302     setenv ("LIBGUESTFS_QEMU", path, 1);
303     return;
304   }
305
306   /* This should be a source directory, so check it. */
307   snprintf (buffer, sizeof buffer, "%s/pc-bios", path);
308   if (stat (buffer, &statbuf) == -1 ||
309       !S_ISDIR (statbuf.st_mode)) {
310     fprintf (stderr,
311              _("%s: does not look like a qemu source directory\n"),
312              path);
313     exit (EXIT_FAILURE);
314   }
315
316   /* Make a wrapper script. */
317   fd = mkstemp (qemuwrapper);
318   if (fd == -1) {
319     perror (qemuwrapper);
320     exit (EXIT_FAILURE);
321   }
322
323   fchmod (fd, 0700);
324
325   fp = fdopen (fd, "w");
326   fprintf (fp,
327            "#!/bin/sh -\n"
328            "qemudir='%s'\n"
329            "\"$qemudir\"/",
330            path);
331
332   /* Select the right qemu binary for the wrapper script. */
333 #ifdef __i386__
334   fprintf (fp, "i386-softmmu/qemu");
335 #else
336   fprintf (fp, host_cpu "-softmmu/qemu-system-" host_cpu);
337 #endif
338
339   fprintf (fp, " -L \"$qemudir\"/pc-bios \"$@\"\n");
340
341   fclose (fp);
342
343   setenv ("LIBGUESTFS_QEMU", qemuwrapper, 1);
344   atexit (cleanup_wrapper);
345 }
346
347 static void
348 cleanup_tmpfiles (void)
349 {
350   unlink (tmpf);
351 }
352
353 static void
354 make_files (void)
355 {
356   int fd;
357
358   /* Allocate the sparse file for /dev/sda. */
359   fd = mkstemp (tmpf);
360   if (fd == -1) {
361     perror (tmpf);
362     exit (EXIT_FAILURE);
363   }
364
365   if (lseek (fd, 100 * 1024 * 1024 - 1, SEEK_SET) == -1) {
366     perror ("lseek");
367     close (fd);
368     unlink (tmpf);
369     exit (EXIT_FAILURE);
370   }
371
372   if (write (fd, "\0", 1) == -1) {
373     perror ("write");
374     close (fd);
375     unlink (tmpf);
376     exit (EXIT_FAILURE);
377   }
378
379   close (fd);
380
381   atexit (cleanup_tmpfiles);    /* Removes tmpf. */
382 }