inspect: Abstract the wrapper that sets up Augeas.
[libguestfs.git] / src / inspect.c
1 /* libguestfs
2  * Copyright (C) 2010 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; 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 <unistd.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #ifdef HAVE_PCRE
30 #include <pcre.h>
31 #endif
32
33 #ifdef HAVE_HIVEX
34 #include <hivex.h>
35 #endif
36
37 #include "c-ctype.h"
38 #include "ignore-value.h"
39 #include "xstrtol.h"
40
41 #include "guestfs.h"
42 #include "guestfs-internal.h"
43 #include "guestfs-internal-actions.h"
44 #include "guestfs_protocol.h"
45
46 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
47
48 /* Compile all the regular expressions once when the shared library is
49  * loaded.  PCRE is thread safe so we're supposedly OK here if
50  * multiple threads call into the libguestfs API functions below
51  * simultaneously.
52  */
53 static pcre *re_fedora;
54 static pcre *re_rhel_old;
55 static pcre *re_rhel;
56 static pcre *re_rhel_no_minor;
57 static pcre *re_major_minor;
58 static pcre *re_aug_seq;
59 static pcre *re_xdev;
60 static pcre *re_windows_version;
61
62 static void compile_regexps (void) __attribute__((constructor));
63 static void free_regexps (void) __attribute__((destructor));
64
65 static void
66 compile_regexps (void)
67 {
68   const char *err;
69   int offset;
70
71 #define COMPILE(re,pattern,options)                                     \
72   do {                                                                  \
73     re = pcre_compile ((pattern), (options), &err, &offset, NULL);      \
74     if (re == NULL) {                                                   \
75       ignore_value (write (2, err, strlen (err)));                      \
76       abort ();                                                         \
77     }                                                                   \
78   } while (0)
79
80   COMPILE (re_fedora, "Fedora release (\\d+)", 0);
81   COMPILE (re_rhel_old,
82            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0);
83   COMPILE (re_rhel,
84            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0);
85   COMPILE (re_rhel_no_minor,
86            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)", 0);
87   COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
88   COMPILE (re_aug_seq, "/\\d+$", 0);
89   COMPILE (re_xdev, "^/dev/(?:h|s|v|xv)d([a-z]\\d*)$", 0);
90   COMPILE (re_windows_version, "^(\\d+)\\.(\\d+)", 0);
91 }
92
93 static void
94 free_regexps (void)
95 {
96   pcre_free (re_fedora);
97   pcre_free (re_rhel_old);
98   pcre_free (re_rhel);
99   pcre_free (re_rhel_no_minor);
100   pcre_free (re_major_minor);
101   pcre_free (re_aug_seq);
102   pcre_free (re_xdev);
103   pcre_free (re_windows_version);
104 }
105
106 /* The main inspection code. */
107 static int check_for_filesystem_on (guestfs_h *g, const char *device);
108
109 char **
110 guestfs__inspect_os (guestfs_h *g)
111 {
112   /* Remove any information previously stored in the handle. */
113   guestfs___free_inspect_info (g);
114
115   if (guestfs_umount_all (g) == -1)
116     return NULL;
117
118   /* Iterate over all possible devices.  Try to mount each
119    * (read-only).  Examine ones which contain filesystems and add that
120    * information to the handle.
121    */
122   /* Look to see if any devices directly contain filesystems (RHBZ#590167). */
123   char **devices;
124   devices = guestfs_list_devices (g);
125   if (devices == NULL)
126     return NULL;
127
128   size_t i;
129   for (i = 0; devices[i] != NULL; ++i) {
130     if (check_for_filesystem_on (g, devices[i]) == -1) {
131       guestfs___free_string_list (devices);
132       guestfs___free_inspect_info (g);
133       return NULL;
134     }
135   }
136   guestfs___free_string_list (devices);
137
138   /* Look at all partitions. */
139   char **partitions;
140   partitions = guestfs_list_partitions (g);
141   if (partitions == NULL) {
142     guestfs___free_inspect_info (g);
143     return NULL;
144   }
145
146   for (i = 0; partitions[i] != NULL; ++i) {
147     if (check_for_filesystem_on (g, partitions[i]) == -1) {
148       guestfs___free_string_list (partitions);
149       guestfs___free_inspect_info (g);
150       return NULL;
151     }
152   }
153   guestfs___free_string_list (partitions);
154
155   /* Look at all LVs. */
156   if (guestfs___feature_available (g, "lvm2")) {
157     char **lvs;
158     lvs = guestfs_lvs (g);
159     if (lvs == NULL) {
160       guestfs___free_inspect_info (g);
161       return NULL;
162     }
163
164     for (i = 0; lvs[i] != NULL; ++i) {
165       if (check_for_filesystem_on (g, lvs[i]) == -1) {
166         guestfs___free_string_list (lvs);
167         guestfs___free_inspect_info (g);
168         return NULL;
169       }
170     }
171     guestfs___free_string_list (lvs);
172   }
173
174   /* At this point we have, in the handle, a list of all filesystems
175    * found and data about each one.  Now we assemble the list of
176    * filesystems which are root devices and return that to the user.
177    */
178   size_t count = 0;
179   for (i = 0; i < g->nr_fses; ++i)
180     if (g->fses[i].is_root)
181       count++;
182
183   char **ret = calloc (count+1, sizeof (char *));
184   if (ret == NULL) {
185     perrorf (g, "calloc");
186     guestfs___free_inspect_info (g);
187     return NULL;
188   }
189
190   count = 0;
191   for (i = 0; i < g->nr_fses; ++i) {
192     if (g->fses[i].is_root) {
193       ret[count] = safe_strdup (g, g->fses[i].device);
194       count++;
195     }
196   }
197   ret[count] = NULL;
198
199   return ret;
200 }
201
202 /* Find out if 'device' contains a filesystem.  If it does, add
203  * another entry in g->fses.
204  */
205 static int check_filesystem (guestfs_h *g, const char *device);
206 static int check_linux_root (guestfs_h *g, struct inspect_fs *fs);
207 static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
208 static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
209 static int check_windows_root (guestfs_h *g, struct inspect_fs *fs);
210 static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
211 static int check_windows_registry (guestfs_h *g, struct inspect_fs *fs);
212 static char *resolve_windows_path_silently (guestfs_h *g, const char *);
213 static int extend_fses (guestfs_h *g);
214 static int parse_unsigned_int (guestfs_h *g, const char *str);
215 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
216                             const char *spec, const char *mp);
217 static char *resolve_fstab_device (guestfs_h *g, const char *spec);
218 static int download_to_tmp (guestfs_h *g, const char *filename, char *localtmp, int64_t max_size);
219 static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename, int (*f) (guestfs_h *, struct inspect_fs *));
220
221 static int
222 check_for_filesystem_on (guestfs_h *g, const char *device)
223 {
224   /* Get vfs-type in order to check if it's a Linux(?) swap device.
225    * If there's an error we should ignore it, so to do that we have to
226    * temporarily replace the error handler with a null one.
227    */
228   guestfs_error_handler_cb old_error_cb = g->error_cb;
229   g->error_cb = NULL;
230   char *vfs_type = guestfs_vfs_type (g, device);
231   g->error_cb = old_error_cb;
232
233   int is_swap = vfs_type && STREQ (vfs_type, "swap");
234
235   if (g->verbose)
236     fprintf (stderr, "check_for_filesystem_on: %s (%s)\n",
237              device, vfs_type ? vfs_type : "failed to get vfs type");
238
239   if (is_swap) {
240     free (vfs_type);
241     if (extend_fses (g) == -1)
242       return -1;
243     g->fses[g->nr_fses-1].is_swap = 1;
244     return 0;
245   }
246
247   /* Try mounting the device.  As above, ignore errors. */
248   g->error_cb = NULL;
249   int r = guestfs_mount_ro (g, device, "/");
250   if (r == -1 && vfs_type && STREQ (vfs_type, "ufs")) /* Hack for the *BSDs. */
251     r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", device, "/");
252   free (vfs_type);
253   g->error_cb = old_error_cb;
254   if (r == -1)
255     return 0;
256
257   /* Do the rest of the checks. */
258   r = check_filesystem (g, device);
259
260   /* Unmount the filesystem. */
261   if (guestfs_umount_all (g) == -1)
262     return -1;
263
264   return r;
265 }
266
267 static int
268 check_filesystem (guestfs_h *g, const char *device)
269 {
270   if (extend_fses (g) == -1)
271     return -1;
272
273   struct inspect_fs *fs = &g->fses[g->nr_fses-1];
274
275   fs->device = safe_strdup (g, device);
276   fs->is_mountable = 1;
277
278   /* Grub /boot? */
279   if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
280       guestfs_is_file (g, "/grub/grub.conf") > 0)
281     fs->content = FS_CONTENT_LINUX_BOOT;
282   /* Linux root? */
283   else if (guestfs_is_dir (g, "/etc") > 0 &&
284            guestfs_is_dir (g, "/bin") > 0 &&
285            guestfs_is_file (g, "/etc/fstab") > 0) {
286     fs->is_root = 1;
287     fs->content = FS_CONTENT_LINUX_ROOT;
288     if (check_linux_root (g, fs) == -1)
289       return -1;
290   }
291   /* Linux /usr/local? */
292   else if (guestfs_is_dir (g, "/etc") > 0 &&
293            guestfs_is_dir (g, "/bin") > 0 &&
294            guestfs_is_dir (g, "/share") > 0 &&
295            guestfs_exists (g, "/local") == 0 &&
296            guestfs_is_file (g, "/etc/fstab") == 0)
297     fs->content = FS_CONTENT_LINUX_USR_LOCAL;
298   /* Linux /usr? */
299   else if (guestfs_is_dir (g, "/etc") > 0 &&
300            guestfs_is_dir (g, "/bin") > 0 &&
301            guestfs_is_dir (g, "/share") > 0 &&
302            guestfs_exists (g, "/local") > 0 &&
303            guestfs_is_file (g, "/etc/fstab") == 0)
304     fs->content = FS_CONTENT_LINUX_USR;
305   /* Linux /var? */
306   else if (guestfs_is_dir (g, "/log") > 0 &&
307            guestfs_is_dir (g, "/run") > 0 &&
308            guestfs_is_dir (g, "/spool") > 0)
309     fs->content = FS_CONTENT_LINUX_VAR;
310   /* Windows root? */
311   else if (guestfs_is_file (g, "/AUTOEXEC.BAT") > 0 ||
312            guestfs_is_file (g, "/autoexec.bat") > 0 ||
313            guestfs_is_dir (g, "/Program Files") > 0 ||
314            guestfs_is_dir (g, "/WINDOWS") > 0 ||
315            guestfs_is_dir (g, "/Windows") > 0 ||
316            guestfs_is_dir (g, "/windows") > 0 ||
317            guestfs_is_dir (g, "/WIN32") > 0 ||
318            guestfs_is_dir (g, "/Win32") > 0 ||
319            guestfs_is_dir (g, "/WINNT") > 0 ||
320            guestfs_is_file (g, "/boot.ini") > 0 ||
321            guestfs_is_file (g, "/ntldr") > 0) {
322     fs->is_root = 1;
323     fs->content = FS_CONTENT_WINDOWS_ROOT;
324     if (check_windows_root (g, fs) == -1)
325       return -1;
326   }
327
328   return 0;
329 }
330
331 /* Set fs->product_name to the first line of the release file. */
332 static int
333 parse_release_file (guestfs_h *g, struct inspect_fs *fs,
334                     const char *release_filename)
335 {
336   char **product_name = guestfs_head_n (g, 1, release_filename);
337   if (product_name == NULL)
338     return -1;
339   if (product_name[0] == NULL) {
340     error (g, _("%s: file is empty"), release_filename);
341     guestfs___free_string_list (product_name);
342     return -1;
343   }
344
345   /* Note that this string becomes owned by the handle and will
346    * be freed by guestfs___free_inspect_info.
347    */
348   fs->product_name = product_name[0];
349   free (product_name);
350
351   return 0;
352 }
353
354 /* Parse generic MAJOR.MINOR from the fs->product_name string. */
355 static int
356 parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
357 {
358   char *major, *minor;
359
360   if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) {
361     fs->major_version = parse_unsigned_int (g, major);
362     free (major);
363     if (fs->major_version == -1) {
364       free (minor);
365       return -1;
366     }
367     fs->minor_version = parse_unsigned_int (g, minor);
368     free (minor);
369     if (fs->minor_version == -1)
370       return -1;
371   }
372   return 0;
373 }
374
375 /* Ubuntu has /etc/lsb-release containing:
376  *   DISTRIB_ID=Ubuntu                                # Distro
377  *   DISTRIB_RELEASE=10.04                            # Version
378  *   DISTRIB_CODENAME=lucid
379  *   DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"         # Product name
380  * In theory other distros could have this LSB file, but none do.
381  */
382 static int
383 parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
384 {
385   char **lines;
386   size_t i;
387   int r = 0;
388
389   lines = guestfs_head_n (g, 10, "/etc/lsb-release");
390   if (lines == NULL)
391     return -1;
392
393   for (i = 0; lines[i] != NULL; ++i) {
394     if (fs->distro == 0 &&
395         STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
396       fs->distro = OS_DISTRO_UBUNTU;
397       r = 1;
398     }
399     else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
400       char *major, *minor;
401       if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) {
402         fs->major_version = parse_unsigned_int (g, major);
403         free (major);
404         if (fs->major_version == -1) {
405           free (minor);
406           guestfs___free_string_list (lines);
407           return -1;
408         }
409         fs->minor_version = parse_unsigned_int (g, minor);
410         free (minor);
411         if (fs->minor_version == -1) {
412           guestfs___free_string_list (lines);
413           return -1;
414         }
415       }
416     }
417     else if (fs->product_name == NULL &&
418              (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
419               STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
420       size_t len = strlen (lines[i]) - 21 - 1;
421       fs->product_name = safe_strndup (g, &lines[i][21], len);
422       r = 1;
423     }
424     else if (fs->product_name == NULL &&
425              STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
426       size_t len = strlen (lines[i]) - 20;
427       fs->product_name = safe_strndup (g, &lines[i][20], len);
428       r = 1;
429     }
430   }
431
432   guestfs___free_string_list (lines);
433   return r;
434 }
435
436 /* The currently mounted device is known to be a Linux root.  Try to
437  * determine from this the distro, version, etc.  Also parse
438  * /etc/fstab to determine the arrangement of mountpoints and
439  * associated devices.
440  */
441 static int
442 check_linux_root (guestfs_h *g, struct inspect_fs *fs)
443 {
444   int r;
445
446   fs->type = OS_TYPE_LINUX;
447
448   if (guestfs_exists (g, "/etc/lsb-release") > 0) {
449     r = parse_lsb_release (g, fs);
450     if (r == -1)        /* error */
451       return -1;
452     if (r == 1)         /* ok - detected the release from this file */
453       goto skip_release_checks;
454   }
455
456   if (guestfs_exists (g, "/etc/redhat-release") > 0) {
457     fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */
458
459     if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
460       return -1;
461
462     char *major, *minor;
463     if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
464       fs->distro = OS_DISTRO_FEDORA;
465       fs->major_version = parse_unsigned_int (g, major);
466       free (major);
467       if (fs->major_version == -1)
468         return -1;
469     }
470     else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) ||
471              match2 (g, fs->product_name, re_rhel, &major, &minor)) {
472       fs->distro = OS_DISTRO_RHEL;
473       fs->major_version = parse_unsigned_int (g, major);
474       free (major);
475       if (fs->major_version == -1) {
476         free (minor);
477         return -1;
478       }
479       fs->minor_version = parse_unsigned_int (g, minor);
480       free (minor);
481       if (fs->minor_version == -1)
482         return -1;
483     }
484     else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) {
485       fs->distro = OS_DISTRO_RHEL;
486       fs->major_version = parse_unsigned_int (g, major);
487       free (major);
488       if (fs->major_version == -1)
489         return -1;
490       fs->minor_version = 0;
491     }
492   }
493   else if (guestfs_exists (g, "/etc/debian_version") > 0) {
494     fs->distro = OS_DISTRO_DEBIAN;
495
496     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
497       return -1;
498
499     if (parse_major_minor (g, fs) == -1)
500       return -1;
501   }
502   else if (guestfs_exists (g, "/etc/pardus-release") > 0) {
503     fs->distro = OS_DISTRO_PARDUS;
504
505     if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
506       return -1;
507
508     if (parse_major_minor (g, fs) == -1)
509       return -1;
510   }
511   else if (guestfs_exists (g, "/etc/arch-release") > 0) {
512     fs->distro = OS_DISTRO_ARCHLINUX;
513
514     /* /etc/arch-release file is empty and I can't see a way to
515      * determine the actual release or product string.
516      */
517   }
518   else if (guestfs_exists (g, "/etc/gentoo-release") > 0) {
519     fs->distro = OS_DISTRO_GENTOO;
520
521     if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
522       return -1;
523
524     if (parse_major_minor (g, fs) == -1)
525       return -1;
526   }
527   else if (guestfs_exists (g, "/etc/meego-release") > 0) {
528     fs->distro = OS_DISTRO_MEEGO;
529
530     if (parse_release_file (g, fs, "/etc/meego-release") == -1)
531       return -1;
532
533     if (parse_major_minor (g, fs) == -1)
534       return -1;
535   }
536
537  skip_release_checks:;
538
539   /* Determine the architecture. */
540   check_architecture (g, fs);
541
542   /* We already know /etc/fstab exists because it's part of the test
543    * for Linux root above.  We must now parse this file to determine
544    * which filesystems are used by the operating system and how they
545    * are mounted.
546    */
547   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
548     return -1;
549
550   return 0;
551 }
552
553 static void
554 check_architecture (guestfs_h *g, struct inspect_fs *fs)
555 {
556   const char *binaries[] =
557     { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" };
558   size_t i;
559
560   for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
561     if (guestfs_is_file (g, binaries[i]) > 0) {
562       /* Ignore errors from file_architecture call. */
563       guestfs_error_handler_cb old_error_cb = g->error_cb;
564       g->error_cb = NULL;
565       char *arch = guestfs_file_architecture (g, binaries[i]);
566       g->error_cb = old_error_cb;
567
568       if (arch) {
569         /* String will be owned by handle, freed by
570          * guestfs___free_inspect_info.
571          */
572         fs->arch = arch;
573         break;
574       }
575     }
576   }
577 }
578
579 static int
580 check_fstab (guestfs_h *g, struct inspect_fs *fs)
581 {
582   char **lines = guestfs_aug_ls (g, "/files/etc/fstab");
583   if (lines == NULL)
584     return -1;
585
586   if (lines[0] == NULL) {
587     error (g, _("could not parse /etc/fstab or empty file"));
588     guestfs___free_string_list (lines);
589     return -1;
590   }
591
592   size_t i;
593   char augpath[256];
594   for (i = 0; lines[i] != NULL; ++i) {
595     /* Ignore comments.  Only care about sequence lines which
596      * match m{/\d+$}.
597      */
598     if (match (g, lines[i], re_aug_seq)) {
599       snprintf (augpath, sizeof augpath, "%s/spec", lines[i]);
600       char *spec = guestfs_aug_get (g, augpath);
601       if (spec == NULL) {
602         guestfs___free_string_list (lines);
603         return -1;
604       }
605
606       snprintf (augpath, sizeof augpath, "%s/file", lines[i]);
607       char *mp = guestfs_aug_get (g, augpath);
608       if (mp == NULL) {
609         guestfs___free_string_list (lines);
610         free (spec);
611         return -1;
612       }
613
614       int r = add_fstab_entry (g, fs, spec, mp);
615       free (spec);
616       free (mp);
617
618       if (r == -1) {
619         guestfs___free_string_list (lines);
620         return -1;
621       }
622     }
623   }
624
625   guestfs___free_string_list (lines);
626   return 0;
627 }
628
629 /* Add a filesystem and possibly a mountpoint entry for
630  * the root filesystem 'fs'.
631  *
632  * 'spec' is the fstab spec field, which might be a device name or a
633  * pseudodevice or 'UUID=...' or 'LABEL=...'.
634  *
635  * 'mp' is the mount point, which could also be 'swap' or 'none'.
636  */
637 static int
638 add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
639                  const char *spec, const char *mp)
640 {
641   /* Ignore certain mountpoints. */
642   if (STRPREFIX (mp, "/dev/") ||
643       STREQ (mp, "/dev") ||
644       STRPREFIX (mp, "/media/") ||
645       STRPREFIX (mp, "/proc/") ||
646       STREQ (mp, "/proc") ||
647       STRPREFIX (mp, "/selinux/") ||
648       STREQ (mp, "/selinux") ||
649       STRPREFIX (mp, "/sys/") ||
650       STREQ (mp, "/sys"))
651     return 0;
652
653   /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */
654   if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) ||
655       STREQ (spec, "/dev/floppy") ||
656       STREQ (spec, "/dev/cdrom"))
657     return 0;
658
659   /* Resolve UUID= and LABEL= to the actual device. */
660   char *device = NULL;
661   if (STRPREFIX (spec, "UUID="))
662     device = guestfs_findfs_uuid (g, &spec[5]);
663   else if (STRPREFIX (spec, "LABEL="))
664     device = guestfs_findfs_label (g, &spec[6]);
665   /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */
666   else if (STRPREFIX (spec, "/dev/"))
667     /* Resolve guest block device names. */
668     device = resolve_fstab_device (g, spec);
669
670   /* If we haven't resolved the device successfully by this point,
671    * we don't care, just ignore it.
672    */
673   if (device == NULL)
674     return 0;
675
676   char *mountpoint = safe_strdup (g, mp);
677
678   /* Add this to the fstab entry in 'fs'.
679    * Note these are further filtered by guestfs_inspect_get_mountpoints
680    * and guestfs_inspect_get_filesystems.
681    */
682   size_t n = fs->nr_fstab + 1;
683   struct inspect_fstab_entry *p;
684
685   p = realloc (fs->fstab, n * sizeof (struct inspect_fstab_entry));
686   if (p == NULL) {
687     perrorf (g, "realloc");
688     free (device);
689     free (mountpoint);
690     return -1;
691   }
692
693   fs->fstab = p;
694   fs->nr_fstab = n;
695
696   /* These are owned by the handle and freed by guestfs___free_inspect_info. */
697   fs->fstab[n-1].device = device;
698   fs->fstab[n-1].mountpoint = mountpoint;
699
700   if (g->verbose)
701     fprintf (stderr, "fstab: device=%s mountpoint=%s\n", device, mountpoint);
702
703   return 0;
704 }
705
706 /* Resolve block device name to the libguestfs device name, eg.
707  * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV.  This
708  * assumes that disks were added in the same order as they appear to
709  * the real VM, which is a reasonable assumption to make.  Return
710  * anything we don't recognize unchanged.
711  */
712 static char *
713 resolve_fstab_device (guestfs_h *g, const char *spec)
714 {
715   char *a1;
716   char *device = NULL;
717
718   if (STRPREFIX (spec, "/dev/mapper/")) {
719     /* LVM2 does some strange munging on /dev/mapper paths for VGs and
720      * LVs which contain '-' character:
721      *
722      * ><fs> lvcreate LV--test VG--test 32
723      * ><fs> debug ls /dev/mapper
724      * VG----test-LV----test
725      *
726      * This makes it impossible to reverse those paths directly, so
727      * we have implemented lvm_canonical_lv_name in the daemon.
728      */
729     device = guestfs_lvm_canonical_lv_name (g, spec);
730   }
731   else if ((a1 = match1 (g, spec, re_xdev)) != NULL) {
732     char **devices = guestfs_list_devices (g);
733     if (devices == NULL)
734       return NULL;
735
736     size_t count;
737     for (count = 0; devices[count] != NULL; count++)
738       ;
739
740     size_t i = a1[0] - 'a'; /* a1[0] is always [a-z] because of regex. */
741     if (i < count) {
742       size_t len = strlen (devices[i]) + strlen (a1) + 16;
743       device = safe_malloc (g, len);
744       snprintf (device, len, "%s%s", devices[i], &a1[1]);
745     }
746
747     free (a1);
748     guestfs___free_string_list (devices);
749   }
750   else {
751     /* Didn't match device pattern, return original spec unchanged. */
752     device = safe_strdup (g, spec);
753   }
754
755   return device;
756 }
757
758 /* XXX Handling of boot.ini in the Perl version was pretty broken.  It
759  * essentially didn't do anything for modern Windows guests.
760  * Therefore I've omitted all that code.
761  */
762 static int
763 check_windows_root (guestfs_h *g, struct inspect_fs *fs)
764 {
765   fs->type = OS_TYPE_WINDOWS;
766   fs->distro = OS_DISTRO_WINDOWS;
767
768   /* Try to find Windows systemroot using some common locations. */
769   const char *systemroots[] =
770     { "/windows", "/winnt", "/win32", "/win" };
771   size_t i;
772   char *systemroot = NULL;
773   for (i = 0;
774        systemroot == NULL && i < sizeof systemroots / sizeof systemroots[0];
775        ++i) {
776     systemroot = resolve_windows_path_silently (g, systemroots[i]);
777   }
778
779   if (!systemroot) {
780     error (g, _("cannot resolve Windows %%SYSTEMROOT%%"));
781     return -1;
782   }
783
784   if (g->verbose)
785     fprintf (stderr, "windows %%SYSTEMROOT%% = %s", systemroot);
786
787   /* Freed by guestfs___free_inspect_info. */
788   fs->windows_systemroot = systemroot;
789
790   if (check_windows_arch (g, fs) == -1)
791     return -1;
792
793   if (check_windows_registry (g, fs) == -1)
794     return -1;
795
796   return 0;
797 }
798
799 static int
800 check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
801 {
802   size_t len = strlen (fs->windows_systemroot) + 32;
803   char cmd_exe[len];
804   snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
805
806   char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe);
807   if (!cmd_exe_path)
808     return 0;
809
810   char *arch = guestfs_file_architecture (g, cmd_exe_path);
811   free (cmd_exe_path);
812
813   if (arch)
814     fs->arch = arch;        /* freed by guestfs___free_inspect_info */
815
816   return 0;
817 }
818
819 /* At the moment, pull just the ProductName and version numbers from
820  * the registry.  In future there is a case for making many more
821  * registry fields available to callers.
822  */
823 static int
824 check_windows_registry (guestfs_h *g, struct inspect_fs *fs)
825 {
826   TMP_TEMPLATE_ON_STACK (software_local);
827
828   size_t len = strlen (fs->windows_systemroot) + 64;
829   char software[len];
830   snprintf (software, len, "%s/system32/config/software",
831             fs->windows_systemroot);
832
833   char *software_path = resolve_windows_path_silently (g, software);
834   if (!software_path)
835     /* If the software hive doesn't exist, just accept that we cannot
836      * find product_name etc.
837      */
838     return 0;
839
840   int ret = -1;
841   hive_h *h = NULL;
842   hive_value_h *values = NULL;
843
844   if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
845     goto out;
846
847   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
848   if (h == NULL) {
849     perrorf (g, "hivex_open");
850     goto out;
851   }
852
853   hive_node_h node = hivex_root (h);
854   const char *hivepath[] =
855     { "Microsoft", "Windows NT", "CurrentVersion" };
856   size_t i;
857   for (i = 0;
858        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
859        ++i) {
860     node = hivex_node_get_child (h, node, hivepath[i]);
861   }
862
863   if (node == 0) {
864     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
865     goto out;
866   }
867
868   values = hivex_node_values (h, node);
869
870   for (i = 0; values[i] != 0; ++i) {
871     char *key = hivex_value_key (h, values[i]);
872     if (key == NULL) {
873       perrorf (g, "hivex_value_key");
874       goto out;
875     }
876
877     if (STRCASEEQ (key, "ProductName")) {
878       fs->product_name = hivex_value_string (h, values[i]);
879       if (!fs->product_name) {
880         perrorf (g, "hivex_value_string");
881         free (key);
882         goto out;
883       }
884     }
885     else if (STRCASEEQ (key, "CurrentVersion")) {
886       char *version = hivex_value_string (h, values[i]);
887       if (!version) {
888         perrorf (g, "hivex_value_string");
889         free (key);
890         goto out;
891       }
892       char *major, *minor;
893       if (match2 (g, version, re_windows_version, &major, &minor)) {
894         fs->major_version = parse_unsigned_int (g, major);
895         free (major);
896         if (fs->major_version == -1) {
897           free (minor);
898           free (key);
899           free (version);
900           goto out;
901         }
902         fs->minor_version = parse_unsigned_int (g, minor);
903         free (minor);
904         if (fs->minor_version == -1) {
905           free (key);
906           free (version);
907           goto out;
908         }
909       }
910
911       free (version);
912     }
913
914     free (key);
915   }
916
917   ret = 0;
918
919  out:
920   if (h) hivex_close (h);
921   free (values);
922   free (software_path);
923
924   /* Free up the temporary file. */
925   unlink (software_local);
926 #undef software_local_len
927
928   return ret;
929 }
930
931 static char *
932 resolve_windows_path_silently (guestfs_h *g, const char *path)
933 {
934   guestfs_error_handler_cb old_error_cb = g->error_cb;
935   g->error_cb = NULL;
936   char *ret = guestfs_case_sensitive_path (g, path);
937   g->error_cb = old_error_cb;
938   return ret;
939 }
940
941 static int
942 extend_fses (guestfs_h *g)
943 {
944   size_t n = g->nr_fses + 1;
945   struct inspect_fs *p;
946
947   p = realloc (g->fses, n * sizeof (struct inspect_fs));
948   if (p == NULL) {
949     perrorf (g, "realloc");
950     return -1;
951   }
952
953   g->fses = p;
954   g->nr_fses = n;
955
956   memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
957
958   return 0;
959 }
960
961 /* Parse small, unsigned ints, as used in version numbers. */
962 static int
963 parse_unsigned_int (guestfs_h *g, const char *str)
964 {
965   long ret;
966   int r = xstrtol (str, NULL, 10, &ret, "");
967   if (r != LONGINT_OK) {
968     error (g, _("could not parse integer in version number: %s"), str);
969     return -1;
970   }
971   return ret;
972 }
973
974 static struct inspect_fs *
975 search_for_root (guestfs_h *g, const char *root)
976 {
977   if (g->nr_fses == 0) {
978     error (g, _("no inspection data: call guestfs_inspect_os first"));
979     return NULL;
980   }
981
982   size_t i;
983   struct inspect_fs *fs;
984   for (i = 0; i < g->nr_fses; ++i) {
985     fs = &g->fses[i];
986     if (fs->is_root && STREQ (root, fs->device))
987       return fs;
988   }
989
990   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
991          root);
992   return NULL;
993 }
994
995 char *
996 guestfs__inspect_get_type (guestfs_h *g, const char *root)
997 {
998   struct inspect_fs *fs = search_for_root (g, root);
999   if (!fs)
1000     return NULL;
1001
1002   char *ret;
1003   switch (fs->type) {
1004   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
1005   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
1006   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1007   }
1008
1009   return ret;
1010 }
1011
1012 char *
1013 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1014 {
1015   struct inspect_fs *fs = search_for_root (g, root);
1016   if (!fs)
1017     return NULL;
1018
1019   return safe_strdup (g, fs->arch ? : "unknown");
1020 }
1021
1022 char *
1023 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1024 {
1025   struct inspect_fs *fs = search_for_root (g, root);
1026   if (!fs)
1027     return NULL;
1028
1029   char *ret;
1030   switch (fs->distro) {
1031   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
1032   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
1033   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
1034   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
1035   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
1036   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
1037   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
1038   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
1039   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
1040   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
1041   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1042   }
1043
1044   return ret;
1045 }
1046
1047 int
1048 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1049 {
1050   struct inspect_fs *fs = search_for_root (g, root);
1051   if (!fs)
1052     return -1;
1053
1054   return fs->major_version;
1055 }
1056
1057 int
1058 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1059 {
1060   struct inspect_fs *fs = search_for_root (g, root);
1061   if (!fs)
1062     return -1;
1063
1064   return fs->minor_version;
1065 }
1066
1067 char *
1068 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1069 {
1070   struct inspect_fs *fs = search_for_root (g, root);
1071   if (!fs)
1072     return NULL;
1073
1074   return safe_strdup (g, fs->product_name ? : "unknown");
1075 }
1076
1077 char *
1078 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1079 {
1080   struct inspect_fs *fs = search_for_root (g, root);
1081   if (!fs)
1082     return NULL;
1083
1084   if (!fs->windows_systemroot) {
1085     error (g, _("not a Windows guest, or systemroot could not be determined"));
1086     return NULL;
1087   }
1088
1089   return safe_strdup (g, fs->windows_systemroot);
1090 }
1091
1092 char **
1093 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1094 {
1095   struct inspect_fs *fs = search_for_root (g, root);
1096   if (!fs)
1097     return NULL;
1098
1099   char **ret;
1100
1101   /* If no fstab information (Windows) return just the root. */
1102   if (fs->nr_fstab == 0) {
1103     ret = calloc (3, sizeof (char *));
1104     ret[0] = safe_strdup (g, "/");
1105     ret[1] = safe_strdup (g, root);
1106     ret[2] = NULL;
1107     return ret;
1108   }
1109
1110 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
1111   size_t i, count = 0;
1112   for (i = 0; i < fs->nr_fstab; ++i)
1113     if (CRITERION)
1114       count++;
1115
1116   /* Hashtables have 2N+1 entries. */
1117   ret = calloc (2*count+1, sizeof (char *));
1118   if (ret == NULL) {
1119     perrorf (g, "calloc");
1120     return NULL;
1121   }
1122
1123   count = 0;
1124   for (i = 0; i < fs->nr_fstab; ++i)
1125     if (CRITERION) {
1126       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
1127       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
1128       count++;
1129     }
1130 #undef CRITERION
1131
1132   return ret;
1133 }
1134
1135 char **
1136 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
1137 {
1138   struct inspect_fs *fs = search_for_root (g, root);
1139   if (!fs)
1140     return NULL;
1141
1142   char **ret;
1143
1144   /* If no fstab information (Windows) return just the root. */
1145   if (fs->nr_fstab == 0) {
1146     ret = calloc (2, sizeof (char *));
1147     ret[0] = safe_strdup (g, root);
1148     ret[1] = NULL;
1149     return ret;
1150   }
1151
1152   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
1153   if (ret == NULL) {
1154     perrorf (g, "calloc");
1155     return NULL;
1156   }
1157
1158   size_t i;
1159   for (i = 0; i < fs->nr_fstab; ++i)
1160     ret[i] = safe_strdup (g, fs->fstab[i].device);
1161
1162   return ret;
1163 }
1164
1165 /* Download to a guest file to a local temporary file.  Refuse to
1166  * download the guest file if it is larger than max_size.  The caller
1167  * is responsible for deleting the temporary file after use.
1168  */
1169 static int
1170 download_to_tmp (guestfs_h *g, const char *filename,
1171                  char *localtmp, int64_t max_size)
1172 {
1173   int fd;
1174   char buf[32];
1175   int64_t size;
1176
1177   size = guestfs_filesize (g, filename);
1178   if (size == -1)
1179     /* guestfs_filesize failed and has already set error in handle */
1180     return -1;
1181   if (size > max_size) {
1182     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1183            filename, size);
1184     return -1;
1185   }
1186
1187   fd = mkstemp (localtmp);
1188   if (fd == -1) {
1189     perrorf (g, "mkstemp");
1190     return -1;
1191   }
1192
1193   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
1194
1195   if (guestfs_download (g, filename, buf) == -1) {
1196     close (fd);
1197     unlink (localtmp);
1198     return -1;
1199   }
1200
1201   if (close (fd) == -1) {
1202     perrorf (g, "close: %s", localtmp);
1203     unlink (localtmp);
1204     return -1;
1205   }
1206
1207   return 0;
1208 }
1209
1210 /* Call 'f' with Augeas opened and having parsed 'filename' (this file
1211  * must exist).  As a security measure, this bails if the file is too
1212  * large for a reasonable configuration file.  After the call to 'f'
1213  * Augeas is closed.
1214  */
1215 static int
1216 inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
1217                      int (*f) (guestfs_h *, struct inspect_fs *))
1218 {
1219   /* Security: Refuse to do this if filename is too large. */
1220   int64_t size = guestfs_filesize (g, filename);
1221   if (size == -1)
1222     /* guestfs_filesize failed and has already set error in handle */
1223     return -1;
1224   if (size > 100000) {
1225     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1226            filename, size);
1227     return -1;
1228   }
1229
1230   /* If !feature_available (g, "augeas") then the next call will fail.
1231    * Arguably we might want to fall back to a non-Augeas method in
1232    * this case.
1233    */
1234   if (guestfs_aug_init (g, "/", 16|32) == -1)
1235     return -1;
1236
1237   int r = -1;
1238
1239   /* Tell Augeas to only load one file (thanks RaphaĆ«l Pinson). */
1240   char buf[strlen (filename) + 64];
1241   snprintf (buf, strlen (filename) + 64, "/augeas/load//incl[. != \"%s\"]",
1242             filename);
1243   if (guestfs_aug_rm (g, buf) == -1)
1244     goto out;
1245
1246   if (guestfs_aug_load (g) == -1)
1247     goto out;
1248
1249   r = f (g, fs);
1250
1251  out:
1252   guestfs_aug_close (g);
1253
1254   return r;
1255 }
1256
1257 #else /* no PCRE or hivex at compile time */
1258
1259 /* XXX These functions should be in an optgroup. */
1260
1261 #define NOT_IMPL(r)                                                     \
1262   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
1263   return r
1264
1265 char **
1266 guestfs__inspect_os (guestfs_h *g)
1267 {
1268   NOT_IMPL(NULL);
1269 }
1270
1271 char **
1272 guestfs__inspect_get_roots (guestfs_h *g)
1273 {
1274   NOT_IMPL(NULL);
1275 }
1276
1277 char *
1278 guestfs__inspect_get_type (guestfs_h *g, const char *root)
1279 {
1280   NOT_IMPL(NULL);
1281 }
1282
1283 char *
1284 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1285 {
1286   NOT_IMPL(NULL);
1287 }
1288
1289 char *
1290 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1291 {
1292   NOT_IMPL(NULL);
1293 }
1294
1295 int
1296 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1297 {
1298   NOT_IMPL(-1);
1299 }
1300
1301 int
1302 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1303 {
1304   NOT_IMPL(-1);
1305 }
1306
1307 char *
1308 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1309 {
1310   NOT_IMPL(NULL);
1311 }
1312
1313 char *
1314 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1315 {
1316   NOT_IMPL(NULL);
1317 }
1318
1319 char **
1320 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1321 {
1322   NOT_IMPL(NULL);
1323 }
1324
1325 char **
1326 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
1327 {
1328   NOT_IMPL(NULL);
1329 }
1330
1331 #endif /* no PCRE or hivex at compile time */
1332
1333 void
1334 guestfs___free_inspect_info (guestfs_h *g)
1335 {
1336   size_t i;
1337   for (i = 0; i < g->nr_fses; ++i) {
1338     free (g->fses[i].device);
1339     free (g->fses[i].product_name);
1340     free (g->fses[i].arch);
1341     free (g->fses[i].windows_systemroot);
1342     size_t j;
1343     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
1344       free (g->fses[i].fstab[j].device);
1345       free (g->fses[i].fstab[j].mountpoint);
1346     }
1347     free (g->fses[i].fstab);
1348   }
1349   free (g->fses);
1350   g->nr_fses = 0;
1351   g->fses = NULL;
1352 }
1353
1354 /* In the Perl code this is a public function. */
1355 int
1356 guestfs___feature_available (guestfs_h *g, const char *feature)
1357 {
1358   /* If there's an error we should ignore it, so to do that we have to
1359    * temporarily replace the error handler with a null one.
1360    */
1361   guestfs_error_handler_cb old_error_cb = g->error_cb;
1362   g->error_cb = NULL;
1363
1364   const char *groups[] = { feature, NULL };
1365   int r = guestfs_available (g, (char * const *) groups);
1366
1367   g->error_cb = old_error_cb;
1368
1369   return r == 0 ? 1 : 0;
1370 }
1371
1372 #ifdef HAVE_PCRE
1373
1374 /* Match a regular expression which contains no captures.  Returns
1375  * true if it matches or false if it doesn't.
1376  */
1377 int
1378 guestfs___match (guestfs_h *g, const char *str, const pcre *re)
1379 {
1380   size_t len = strlen (str);
1381   int vec[30], r;
1382
1383   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
1384   if (r == PCRE_ERROR_NOMATCH)
1385     return 0;
1386   if (r != 1) {
1387     /* Internal error -- should not happen. */
1388     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1389              __FILE__, __func__, r, str);
1390     return 0;
1391   }
1392
1393   return 1;
1394 }
1395
1396 /* Match a regular expression which contains exactly one capture.  If
1397  * the string matches, return the capture, otherwise return NULL.  The
1398  * caller must free the result.
1399  */
1400 char *
1401 guestfs___match1 (guestfs_h *g, const char *str, const pcre *re)
1402 {
1403   size_t len = strlen (str);
1404   int vec[30], r;
1405
1406   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
1407   if (r == PCRE_ERROR_NOMATCH)
1408     return NULL;
1409   if (r != 2) {
1410     /* Internal error -- should not happen. */
1411     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1412              __FILE__, __func__, r, str);
1413     return NULL;
1414   }
1415
1416   return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
1417 }
1418
1419 /* Match a regular expression which contains exactly two captures. */
1420 int
1421 guestfs___match2 (guestfs_h *g, const char *str, const pcre *re,
1422                   char **ret1, char **ret2)
1423 {
1424   size_t len = strlen (str);
1425   int vec[30], r;
1426
1427   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
1428   if (r == PCRE_ERROR_NOMATCH)
1429     return 0;
1430   if (r != 3) {
1431     /* Internal error -- should not happen. */
1432     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1433              __FILE__, __func__, r, str);
1434     return 0;
1435   }
1436
1437   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
1438   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
1439
1440   return 1;
1441 }
1442
1443 /* Match a regular expression which contains exactly three captures. */
1444 int
1445 guestfs___match3 (guestfs_h *g, const char *str, const pcre *re,
1446                   char **ret1, char **ret2, char **ret3)
1447 {
1448   size_t len = strlen (str);
1449   int vec[30], r;
1450
1451   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
1452   if (r == PCRE_ERROR_NOMATCH)
1453     return 0;
1454   if (r != 4) {
1455     /* Internal error -- should not happen. */
1456     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
1457              __FILE__, __func__, r, str);
1458     return 0;
1459   }
1460
1461   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
1462   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
1463   *ret3 = safe_strndup (g, &str[vec[6]], vec[7]-vec[6]);
1464
1465   return 1;
1466 }
1467
1468 #endif /* HAVE_PCRE */