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