inspect: Add function to read the first line of a file, with safety checks.
[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_first_partition;
61 static pcre *re_freebsd;
62 static pcre *re_windows_version;
63
64 static void compile_regexps (void) __attribute__((constructor));
65 static void free_regexps (void) __attribute__((destructor));
66
67 static void
68 compile_regexps (void)
69 {
70   const char *err;
71   int offset;
72
73 #define COMPILE(re,pattern,options)                                     \
74   do {                                                                  \
75     re = pcre_compile ((pattern), (options), &err, &offset, NULL);      \
76     if (re == NULL) {                                                   \
77       ignore_value (write (2, err, strlen (err)));                      \
78       abort ();                                                         \
79     }                                                                   \
80   } while (0)
81
82   COMPILE (re_fedora, "Fedora release (\\d+)", 0);
83   COMPILE (re_rhel_old,
84            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0);
85   COMPILE (re_rhel,
86            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0);
87   COMPILE (re_rhel_no_minor,
88            "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)", 0);
89   COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0);
90   COMPILE (re_aug_seq, "/\\d+$", 0);
91   COMPILE (re_xdev, "^/dev/(?:h|s|v|xv)d([a-z]\\d*)$", 0);
92   COMPILE (re_first_partition, "^/dev/(?:h|s|v)d.1$", 0);
93   COMPILE (re_freebsd, "^/dev/ad(\\d+)s(\\d+)([a-z])$", 0);
94   COMPILE (re_windows_version, "^(\\d+)\\.(\\d+)", 0);
95 }
96
97 static void
98 free_regexps (void)
99 {
100   pcre_free (re_fedora);
101   pcre_free (re_rhel_old);
102   pcre_free (re_rhel);
103   pcre_free (re_rhel_no_minor);
104   pcre_free (re_major_minor);
105   pcre_free (re_aug_seq);
106   pcre_free (re_xdev);
107   pcre_free (re_first_partition);
108   pcre_free (re_freebsd);
109   pcre_free (re_windows_version);
110 }
111
112 /* The main inspection code. */
113 static int check_for_filesystem_on (guestfs_h *g, const char *device);
114
115 char **
116 guestfs__inspect_os (guestfs_h *g)
117 {
118   /* Remove any information previously stored in the handle. */
119   guestfs___free_inspect_info (g);
120
121   if (guestfs_umount_all (g) == -1)
122     return NULL;
123
124   /* Iterate over all possible devices.  Try to mount each
125    * (read-only).  Examine ones which contain filesystems and add that
126    * information to the handle.
127    */
128   /* Look to see if any devices directly contain filesystems (RHBZ#590167). */
129   char **devices;
130   devices = guestfs_list_devices (g);
131   if (devices == NULL)
132     return NULL;
133
134   size_t i;
135   for (i = 0; devices[i] != NULL; ++i) {
136     if (check_for_filesystem_on (g, devices[i]) == -1) {
137       guestfs___free_string_list (devices);
138       guestfs___free_inspect_info (g);
139       return NULL;
140     }
141   }
142   guestfs___free_string_list (devices);
143
144   /* Look at all partitions. */
145   char **partitions;
146   partitions = guestfs_list_partitions (g);
147   if (partitions == NULL) {
148     guestfs___free_inspect_info (g);
149     return NULL;
150   }
151
152   for (i = 0; partitions[i] != NULL; ++i) {
153     if (check_for_filesystem_on (g, partitions[i]) == -1) {
154       guestfs___free_string_list (partitions);
155       guestfs___free_inspect_info (g);
156       return NULL;
157     }
158   }
159   guestfs___free_string_list (partitions);
160
161   /* Look at all LVs. */
162   if (guestfs___feature_available (g, "lvm2")) {
163     char **lvs;
164     lvs = guestfs_lvs (g);
165     if (lvs == NULL) {
166       guestfs___free_inspect_info (g);
167       return NULL;
168     }
169
170     for (i = 0; lvs[i] != NULL; ++i) {
171       if (check_for_filesystem_on (g, lvs[i]) == -1) {
172         guestfs___free_string_list (lvs);
173         guestfs___free_inspect_info (g);
174         return NULL;
175       }
176     }
177     guestfs___free_string_list (lvs);
178   }
179
180   /* At this point we have, in the handle, a list of all filesystems
181    * found and data about each one.  Now we assemble the list of
182    * filesystems which are root devices and return that to the user.
183    * Fall through to guestfs__inspect_get_roots to do that.
184    */
185   char **ret = guestfs__inspect_get_roots (g);
186   if (ret == NULL)
187     guestfs___free_inspect_info (g);
188   return ret;
189 }
190
191 /* Find out if 'device' contains a filesystem.  If it does, add
192  * another entry in g->fses.
193  */
194 static int check_filesystem (guestfs_h *g, const char *device);
195 static int check_linux_root (guestfs_h *g, struct inspect_fs *fs);
196 static int check_freebsd_root (guestfs_h *g, struct inspect_fs *fs);
197 static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
198 static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
199 static int check_windows_root (guestfs_h *g, struct inspect_fs *fs);
200 static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
201 static int check_windows_registry (guestfs_h *g, struct inspect_fs *fs);
202 static char *resolve_windows_path_silently (guestfs_h *g, const char *);
203 static int extend_fses (guestfs_h *g);
204 static int parse_unsigned_int (guestfs_h *g, const char *str);
205 static int add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
206                             const char *spec, const char *mp);
207 static char *resolve_fstab_device (guestfs_h *g, const char *spec);
208 static void check_package_format (guestfs_h *g, struct inspect_fs *fs);
209 static void check_package_management (guestfs_h *g, struct inspect_fs *fs);
210 static int download_to_tmp (guestfs_h *g, const char *filename, char *localtmp, int64_t max_size);
211 static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename, int (*f) (guestfs_h *, struct inspect_fs *));
212 static char *first_line_of_file (guestfs_h *g, const char *filename);
213
214 static int
215 check_for_filesystem_on (guestfs_h *g, const char *device)
216 {
217   /* Get vfs-type in order to check if it's a Linux(?) swap device.
218    * If there's an error we should ignore it, so to do that we have to
219    * temporarily replace the error handler with a null one.
220    */
221   guestfs_error_handler_cb old_error_cb = g->error_cb;
222   g->error_cb = NULL;
223   char *vfs_type = guestfs_vfs_type (g, device);
224   g->error_cb = old_error_cb;
225
226   int is_swap = vfs_type && STREQ (vfs_type, "swap");
227
228   if (g->verbose)
229     fprintf (stderr, "check_for_filesystem_on: %s (%s)\n",
230              device, vfs_type ? vfs_type : "failed to get vfs type");
231
232   if (is_swap) {
233     free (vfs_type);
234     if (extend_fses (g) == -1)
235       return -1;
236     g->fses[g->nr_fses-1].is_swap = 1;
237     return 0;
238   }
239
240   /* Try mounting the device.  As above, ignore errors. */
241   g->error_cb = NULL;
242   int r = guestfs_mount_ro (g, device, "/");
243   if (r == -1 && vfs_type && STREQ (vfs_type, "ufs")) /* Hack for the *BSDs. */
244     r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", device, "/");
245   free (vfs_type);
246   g->error_cb = old_error_cb;
247   if (r == -1)
248     return 0;
249
250   /* Do the rest of the checks. */
251   r = check_filesystem (g, device);
252
253   /* Unmount the filesystem. */
254   if (guestfs_umount_all (g) == -1)
255     return -1;
256
257   return r;
258 }
259
260 static int
261 check_filesystem (guestfs_h *g, const char *device)
262 {
263   if (extend_fses (g) == -1)
264     return -1;
265
266   struct inspect_fs *fs = &g->fses[g->nr_fses-1];
267
268   fs->device = safe_strdup (g, device);
269   fs->is_mountable = 1;
270
271   /* Optimize some of the tests by avoiding multiple tests of the same thing. */
272   int is_dir_etc = guestfs_is_dir (g, "/etc") > 0;
273   int is_dir_bin = guestfs_is_dir (g, "/bin") > 0;
274   int is_dir_share = guestfs_is_dir (g, "/share") > 0;
275
276   /* Grub /boot? */
277   if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
278       guestfs_is_file (g, "/grub/grub.conf") > 0)
279     fs->content = FS_CONTENT_LINUX_BOOT;
280   /* FreeBSD root? */
281   else if (is_dir_etc &&
282            is_dir_bin &&
283            guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 &&
284            guestfs_is_file (g, "/etc/fstab") > 0) {
285     /* Ignore /dev/sda1 which is a shadow of the real root filesystem
286      * that is probably /dev/sda5 (see:
287      * http://www.freebsd.org/doc/handbook/disk-organization.html)
288      */
289     if (match (g, device, re_first_partition))
290       return 0;
291
292     fs->is_root = 1;
293     fs->content = FS_CONTENT_FREEBSD_ROOT;
294     if (check_freebsd_root (g, fs) == -1)
295       return -1;
296   }
297   /* Linux root? */
298   else if (is_dir_etc &&
299            is_dir_bin &&
300            guestfs_is_file (g, "/etc/fstab") > 0) {
301     fs->is_root = 1;
302     fs->content = FS_CONTENT_LINUX_ROOT;
303     if (check_linux_root (g, fs) == -1)
304       return -1;
305   }
306   /* Linux /usr/local? */
307   else if (is_dir_etc &&
308            is_dir_bin &&
309            is_dir_share &&
310            guestfs_exists (g, "/local") == 0 &&
311            guestfs_is_file (g, "/etc/fstab") == 0)
312     fs->content = FS_CONTENT_LINUX_USR_LOCAL;
313   /* Linux /usr? */
314   else if (is_dir_etc &&
315            is_dir_bin &&
316            is_dir_share &&
317            guestfs_exists (g, "/local") > 0 &&
318            guestfs_is_file (g, "/etc/fstab") == 0)
319     fs->content = FS_CONTENT_LINUX_USR;
320   /* Linux /var? */
321   else if (guestfs_is_dir (g, "/log") > 0 &&
322            guestfs_is_dir (g, "/run") > 0 &&
323            guestfs_is_dir (g, "/spool") > 0)
324     fs->content = FS_CONTENT_LINUX_VAR;
325   /* Windows root? */
326   else if (guestfs_is_file (g, "/AUTOEXEC.BAT") > 0 ||
327            guestfs_is_file (g, "/autoexec.bat") > 0 ||
328            guestfs_is_dir (g, "/Program Files") > 0 ||
329            guestfs_is_dir (g, "/WINDOWS") > 0 ||
330            guestfs_is_dir (g, "/Windows") > 0 ||
331            guestfs_is_dir (g, "/windows") > 0 ||
332            guestfs_is_dir (g, "/WIN32") > 0 ||
333            guestfs_is_dir (g, "/Win32") > 0 ||
334            guestfs_is_dir (g, "/WINNT") > 0 ||
335            guestfs_is_file (g, "/boot.ini") > 0 ||
336            guestfs_is_file (g, "/ntldr") > 0) {
337     fs->is_root = 1;
338     fs->content = FS_CONTENT_WINDOWS_ROOT;
339     if (check_windows_root (g, fs) == -1)
340       return -1;
341   }
342
343   return 0;
344 }
345
346 /* Set fs->product_name to the first line of the release file. */
347 static int
348 parse_release_file (guestfs_h *g, struct inspect_fs *fs,
349                     const char *release_filename)
350 {
351   fs->product_name = first_line_of_file (g, release_filename);
352   if (fs->product_name == NULL)
353     return -1;
354   return 0;
355 }
356
357 /* Parse generic MAJOR.MINOR from the fs->product_name string. */
358 static int
359 parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
360 {
361   char *major, *minor;
362
363   if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) {
364     fs->major_version = parse_unsigned_int (g, major);
365     free (major);
366     if (fs->major_version == -1) {
367       free (minor);
368       return -1;
369     }
370     fs->minor_version = parse_unsigned_int (g, minor);
371     free (minor);
372     if (fs->minor_version == -1)
373       return -1;
374   }
375   return 0;
376 }
377
378 /* Ubuntu has /etc/lsb-release containing:
379  *   DISTRIB_ID=Ubuntu                                # Distro
380  *   DISTRIB_RELEASE=10.04                            # Version
381  *   DISTRIB_CODENAME=lucid
382  *   DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"         # Product name
383  *
384  * [Ubuntu-derived ...] Linux Mint was found to have this:
385  *   DISTRIB_ID=LinuxMint
386  *   DISTRIB_RELEASE=10
387  *   DISTRIB_CODENAME=julia
388  *   DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
389  * Linux Mint also has /etc/linuxmint/info with more information,
390  * but we can use the LSB file.
391  *
392  * Mandriva has:
393  *   LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
394  *   DISTRIB_ID=MandrivaLinux
395  *   DISTRIB_RELEASE=2010.1
396  *   DISTRIB_CODENAME=Henry_Farman
397  *   DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
398  * Mandriva also has a normal release file called /etc/mandriva-release.
399  */
400 static int
401 parse_lsb_release (guestfs_h *g, struct inspect_fs *fs)
402 {
403   char **lines;
404   size_t i;
405   int r = 0;
406
407   lines = guestfs_head_n (g, 10, "/etc/lsb-release");
408   if (lines == NULL)
409     return -1;
410
411   for (i = 0; lines[i] != NULL; ++i) {
412     if (fs->distro == 0 &&
413         STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
414       fs->distro = OS_DISTRO_UBUNTU;
415       r = 1;
416     }
417     else if (fs->distro == 0 &&
418              STREQ (lines[i], "DISTRIB_ID=LinuxMint")) {
419       fs->distro = OS_DISTRO_LINUX_MINT;
420       r = 1;
421     }
422     else if (fs->distro == 0 &&
423              STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) {
424       fs->distro = OS_DISTRO_MANDRIVA;
425       r = 1;
426     }
427     else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
428       char *major, *minor;
429       if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) {
430         fs->major_version = parse_unsigned_int (g, major);
431         free (major);
432         if (fs->major_version == -1) {
433           free (minor);
434           guestfs___free_string_list (lines);
435           return -1;
436         }
437         fs->minor_version = parse_unsigned_int (g, minor);
438         free (minor);
439         if (fs->minor_version == -1) {
440           guestfs___free_string_list (lines);
441           return -1;
442         }
443       }
444     }
445     else if (fs->product_name == NULL &&
446              (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
447               STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
448       size_t len = strlen (lines[i]) - 21 - 1;
449       fs->product_name = safe_strndup (g, &lines[i][21], len);
450       r = 1;
451     }
452     else if (fs->product_name == NULL &&
453              STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
454       size_t len = strlen (lines[i]) - 20;
455       fs->product_name = safe_strndup (g, &lines[i][20], len);
456       r = 1;
457     }
458   }
459
460   guestfs___free_string_list (lines);
461   return r;
462 }
463
464 /* The currently mounted device is known to be a Linux root.  Try to
465  * determine from this the distro, version, etc.  Also parse
466  * /etc/fstab to determine the arrangement of mountpoints and
467  * associated devices.
468  */
469 static int
470 check_linux_root (guestfs_h *g, struct inspect_fs *fs)
471 {
472   int r;
473
474   fs->type = OS_TYPE_LINUX;
475
476   if (guestfs_exists (g, "/etc/lsb-release") > 0) {
477     r = parse_lsb_release (g, fs);
478     if (r == -1)        /* error */
479       return -1;
480     if (r == 1)         /* ok - detected the release from this file */
481       goto skip_release_checks;
482   }
483
484   if (guestfs_exists (g, "/etc/redhat-release") > 0) {
485     fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */
486
487     if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
488       return -1;
489
490     char *major, *minor;
491     if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
492       fs->distro = OS_DISTRO_FEDORA;
493       fs->major_version = parse_unsigned_int (g, major);
494       free (major);
495       if (fs->major_version == -1)
496         return -1;
497     }
498     else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) ||
499              match2 (g, fs->product_name, re_rhel, &major, &minor)) {
500       fs->distro = OS_DISTRO_RHEL;
501       fs->major_version = parse_unsigned_int (g, major);
502       free (major);
503       if (fs->major_version == -1) {
504         free (minor);
505         return -1;
506       }
507       fs->minor_version = parse_unsigned_int (g, minor);
508       free (minor);
509       if (fs->minor_version == -1)
510         return -1;
511     }
512     else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) {
513       fs->distro = OS_DISTRO_RHEL;
514       fs->major_version = parse_unsigned_int (g, major);
515       free (major);
516       if (fs->major_version == -1)
517         return -1;
518       fs->minor_version = 0;
519     }
520   }
521   else if (guestfs_exists (g, "/etc/debian_version") > 0) {
522     fs->distro = OS_DISTRO_DEBIAN;
523
524     if (parse_release_file (g, fs, "/etc/debian_version") == -1)
525       return -1;
526
527     if (parse_major_minor (g, fs) == -1)
528       return -1;
529   }
530   else if (guestfs_exists (g, "/etc/pardus-release") > 0) {
531     fs->distro = OS_DISTRO_PARDUS;
532
533     if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
534       return -1;
535
536     if (parse_major_minor (g, fs) == -1)
537       return -1;
538   }
539   else if (guestfs_exists (g, "/etc/arch-release") > 0) {
540     fs->distro = OS_DISTRO_ARCHLINUX;
541
542     /* /etc/arch-release file is empty and I can't see a way to
543      * determine the actual release or product string.
544      */
545   }
546   else if (guestfs_exists (g, "/etc/gentoo-release") > 0) {
547     fs->distro = OS_DISTRO_GENTOO;
548
549     if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
550       return -1;
551
552     if (parse_major_minor (g, fs) == -1)
553       return -1;
554   }
555   else if (guestfs_exists (g, "/etc/meego-release") > 0) {
556     fs->distro = OS_DISTRO_MEEGO;
557
558     if (parse_release_file (g, fs, "/etc/meego-release") == -1)
559       return -1;
560
561     if (parse_major_minor (g, fs) == -1)
562       return -1;
563   }
564
565  skip_release_checks:;
566
567   /* If distro test above was successful, work out the package format. */
568   check_package_format (g, fs);
569   check_package_management (g, fs);
570
571   /* Determine the architecture. */
572   check_architecture (g, fs);
573
574   /* We already know /etc/fstab exists because it's part of the test
575    * for Linux root above.  We must now parse this file to determine
576    * which filesystems are used by the operating system and how they
577    * are mounted.
578    */
579   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
580     return -1;
581
582   return 0;
583 }
584
585 /* The currently mounted device is known to be a FreeBSD root. */
586 static int
587 check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
588 {
589   int r;
590
591   fs->type = OS_TYPE_FREEBSD;
592
593   /* FreeBSD has no authoritative version file.  The version number is
594    * in /etc/motd, which the system administrator might edit, but
595    * we'll use that anyway.
596    */
597
598   if (guestfs_exists (g, "/etc/motd") > 0) {
599     if (parse_release_file (g, fs, "/etc/motd") == -1)
600       return -1;
601
602     if (parse_major_minor (g, fs) == -1)
603       return -1;
604   }
605
606   /* Determine the architecture. */
607   check_architecture (g, fs);
608
609   /* We already know /etc/fstab exists because it's part of the test above. */
610   if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
611     return -1;
612
613   return 0;
614 }
615
616 static void
617 check_architecture (guestfs_h *g, struct inspect_fs *fs)
618 {
619   const char *binaries[] =
620     { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" };
621   size_t i;
622
623   for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
624     if (guestfs_is_file (g, binaries[i]) > 0) {
625       /* Ignore errors from file_architecture call. */
626       guestfs_error_handler_cb old_error_cb = g->error_cb;
627       g->error_cb = NULL;
628       char *arch = guestfs_file_architecture (g, binaries[i]);
629       g->error_cb = old_error_cb;
630
631       if (arch) {
632         /* String will be owned by handle, freed by
633          * guestfs___free_inspect_info.
634          */
635         fs->arch = arch;
636         break;
637       }
638     }
639   }
640 }
641
642 static int
643 check_fstab (guestfs_h *g, struct inspect_fs *fs)
644 {
645   char **lines = guestfs_aug_ls (g, "/files/etc/fstab");
646   if (lines == NULL)
647     return -1;
648
649   if (lines[0] == NULL) {
650     error (g, _("could not parse /etc/fstab or empty file"));
651     guestfs___free_string_list (lines);
652     return -1;
653   }
654
655   size_t i;
656   char augpath[256];
657   for (i = 0; lines[i] != NULL; ++i) {
658     /* Ignore comments.  Only care about sequence lines which
659      * match m{/\d+$}.
660      */
661     if (match (g, lines[i], re_aug_seq)) {
662       snprintf (augpath, sizeof augpath, "%s/spec", lines[i]);
663       char *spec = guestfs_aug_get (g, augpath);
664       if (spec == NULL) {
665         guestfs___free_string_list (lines);
666         return -1;
667       }
668
669       snprintf (augpath, sizeof augpath, "%s/file", lines[i]);
670       char *mp = guestfs_aug_get (g, augpath);
671       if (mp == NULL) {
672         guestfs___free_string_list (lines);
673         free (spec);
674         return -1;
675       }
676
677       int r = add_fstab_entry (g, fs, spec, mp);
678       free (spec);
679       free (mp);
680
681       if (r == -1) {
682         guestfs___free_string_list (lines);
683         return -1;
684       }
685     }
686   }
687
688   guestfs___free_string_list (lines);
689   return 0;
690 }
691
692 /* Add a filesystem and possibly a mountpoint entry for
693  * the root filesystem 'fs'.
694  *
695  * 'spec' is the fstab spec field, which might be a device name or a
696  * pseudodevice or 'UUID=...' or 'LABEL=...'.
697  *
698  * 'mp' is the mount point, which could also be 'swap' or 'none'.
699  */
700 static int
701 add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
702                  const char *spec, const char *mp)
703 {
704   /* Ignore certain mountpoints. */
705   if (STRPREFIX (mp, "/dev/") ||
706       STREQ (mp, "/dev") ||
707       STRPREFIX (mp, "/media/") ||
708       STRPREFIX (mp, "/proc/") ||
709       STREQ (mp, "/proc") ||
710       STRPREFIX (mp, "/selinux/") ||
711       STREQ (mp, "/selinux") ||
712       STRPREFIX (mp, "/sys/") ||
713       STREQ (mp, "/sys"))
714     return 0;
715
716   /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */
717   if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) ||
718       STREQ (spec, "/dev/floppy") ||
719       STREQ (spec, "/dev/cdrom"))
720     return 0;
721
722   /* Resolve UUID= and LABEL= to the actual device. */
723   char *device = NULL;
724   if (STRPREFIX (spec, "UUID="))
725     device = guestfs_findfs_uuid (g, &spec[5]);
726   else if (STRPREFIX (spec, "LABEL="))
727     device = guestfs_findfs_label (g, &spec[6]);
728   /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */
729   else if (STRPREFIX (spec, "/dev/"))
730     /* Resolve guest block device names. */
731     device = resolve_fstab_device (g, spec);
732
733   /* If we haven't resolved the device successfully by this point,
734    * we don't care, just ignore it.
735    */
736   if (device == NULL)
737     return 0;
738
739   char *mountpoint = safe_strdup (g, mp);
740
741   /* Add this to the fstab entry in 'fs'.
742    * Note these are further filtered by guestfs_inspect_get_mountpoints
743    * and guestfs_inspect_get_filesystems.
744    */
745   size_t n = fs->nr_fstab + 1;
746   struct inspect_fstab_entry *p;
747
748   p = realloc (fs->fstab, n * sizeof (struct inspect_fstab_entry));
749   if (p == NULL) {
750     perrorf (g, "realloc");
751     free (device);
752     free (mountpoint);
753     return -1;
754   }
755
756   fs->fstab = p;
757   fs->nr_fstab = n;
758
759   /* These are owned by the handle and freed by guestfs___free_inspect_info. */
760   fs->fstab[n-1].device = device;
761   fs->fstab[n-1].mountpoint = mountpoint;
762
763   if (g->verbose)
764     fprintf (stderr, "fstab: device=%s mountpoint=%s\n", device, mountpoint);
765
766   return 0;
767 }
768
769 /* Resolve block device name to the libguestfs device name, eg.
770  * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV.  This
771  * assumes that disks were added in the same order as they appear to
772  * the real VM, which is a reasonable assumption to make.  Return
773  * anything we don't recognize unchanged.
774  */
775 static char *
776 resolve_fstab_device (guestfs_h *g, const char *spec)
777 {
778   char *a1;
779   char *device = NULL;
780   char *bsddisk, *bsdslice, *bsdpart;
781
782   if (STRPREFIX (spec, "/dev/mapper/")) {
783     /* LVM2 does some strange munging on /dev/mapper paths for VGs and
784      * LVs which contain '-' character:
785      *
786      * ><fs> lvcreate LV--test VG--test 32
787      * ><fs> debug ls /dev/mapper
788      * VG----test-LV----test
789      *
790      * This makes it impossible to reverse those paths directly, so
791      * we have implemented lvm_canonical_lv_name in the daemon.
792      */
793     device = guestfs_lvm_canonical_lv_name (g, spec);
794   }
795   else if ((a1 = match1 (g, spec, re_xdev)) != NULL) {
796     char **devices = guestfs_list_devices (g);
797     if (devices == NULL)
798       return NULL;
799
800     size_t count;
801     for (count = 0; devices[count] != NULL; count++)
802       ;
803
804     size_t i = a1[0] - 'a'; /* a1[0] is always [a-z] because of regex. */
805     if (i < count) {
806       size_t len = strlen (devices[i]) + strlen (a1) + 16;
807       device = safe_malloc (g, len);
808       snprintf (device, len, "%s%s", devices[i], &a1[1]);
809     }
810
811     free (a1);
812     guestfs___free_string_list (devices);
813   }
814   else if (match3 (g, spec, re_freebsd, &bsddisk, &bsdslice, &bsdpart)) {
815     /* FreeBSD disks are organized quite differently.  See:
816      * http://www.freebsd.org/doc/handbook/disk-organization.html
817      * FreeBSD "partitions" are exposed as quasi-extended partitions
818      * numbered from 5 in Linux.  I have no idea what happens when you
819      * have multiple "slices" (the FreeBSD term for MBR partitions).
820      */
821     int disk = parse_unsigned_int (g, bsddisk);
822     int slice = parse_unsigned_int (g, bsdslice);
823     int part = bsdpart[0] - 'a' /* counting from 0 */;
824     free (bsddisk);
825     free (bsdslice);
826     free (bsdpart);
827
828     if (disk == -1 || disk > 26 ||
829         slice <= 0 || slice > 1 /* > 4 .. see comment above */ ||
830         part < 0 || part >= 26)
831       goto out;
832
833     device = safe_asprintf (g, "/dev/sd%c%d", disk + 'a', part + 5);
834   }
835
836  out:
837   /* Didn't match device pattern, return original spec unchanged. */
838   if (device == NULL)
839     device = safe_strdup (g, spec);
840
841   return device;
842 }
843
844 /* XXX Handling of boot.ini in the Perl version was pretty broken.  It
845  * essentially didn't do anything for modern Windows guests.
846  * Therefore I've omitted all that code.
847  */
848 static int
849 check_windows_root (guestfs_h *g, struct inspect_fs *fs)
850 {
851   fs->type = OS_TYPE_WINDOWS;
852   fs->distro = OS_DISTRO_WINDOWS;
853
854   /* Try to find Windows systemroot using some common locations. */
855   const char *systemroots[] =
856     { "/windows", "/winnt", "/win32", "/win" };
857   size_t i;
858   char *systemroot = NULL;
859   for (i = 0;
860        systemroot == NULL && i < sizeof systemroots / sizeof systemroots[0];
861        ++i) {
862     systemroot = resolve_windows_path_silently (g, systemroots[i]);
863   }
864
865   if (!systemroot) {
866     error (g, _("cannot resolve Windows %%SYSTEMROOT%%"));
867     return -1;
868   }
869
870   if (g->verbose)
871     fprintf (stderr, "windows %%SYSTEMROOT%% = %s", systemroot);
872
873   /* Freed by guestfs___free_inspect_info. */
874   fs->windows_systemroot = systemroot;
875
876   if (check_windows_arch (g, fs) == -1)
877     return -1;
878
879   if (check_windows_registry (g, fs) == -1)
880     return -1;
881
882   check_package_format (g, fs);
883   check_package_management (g, fs);
884
885   return 0;
886 }
887
888 static int
889 check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
890 {
891   size_t len = strlen (fs->windows_systemroot) + 32;
892   char cmd_exe[len];
893   snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
894
895   char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe);
896   if (!cmd_exe_path)
897     return 0;
898
899   char *arch = guestfs_file_architecture (g, cmd_exe_path);
900   free (cmd_exe_path);
901
902   if (arch)
903     fs->arch = arch;        /* freed by guestfs___free_inspect_info */
904
905   return 0;
906 }
907
908 /* At the moment, pull just the ProductName and version numbers from
909  * the registry.  In future there is a case for making many more
910  * registry fields available to callers.
911  */
912 static int
913 check_windows_registry (guestfs_h *g, struct inspect_fs *fs)
914 {
915   TMP_TEMPLATE_ON_STACK (software_local);
916
917   size_t len = strlen (fs->windows_systemroot) + 64;
918   char software[len];
919   snprintf (software, len, "%s/system32/config/software",
920             fs->windows_systemroot);
921
922   char *software_path = resolve_windows_path_silently (g, software);
923   if (!software_path)
924     /* If the software hive doesn't exist, just accept that we cannot
925      * find product_name etc.
926      */
927     return 0;
928
929   int ret = -1;
930   hive_h *h = NULL;
931   hive_value_h *values = NULL;
932
933   if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
934     goto out;
935
936   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
937   if (h == NULL) {
938     perrorf (g, "hivex_open");
939     goto out;
940   }
941
942   hive_node_h node = hivex_root (h);
943   const char *hivepath[] =
944     { "Microsoft", "Windows NT", "CurrentVersion" };
945   size_t i;
946   for (i = 0;
947        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
948        ++i) {
949     node = hivex_node_get_child (h, node, hivepath[i]);
950   }
951
952   if (node == 0) {
953     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
954     goto out;
955   }
956
957   values = hivex_node_values (h, node);
958
959   for (i = 0; values[i] != 0; ++i) {
960     char *key = hivex_value_key (h, values[i]);
961     if (key == NULL) {
962       perrorf (g, "hivex_value_key");
963       goto out;
964     }
965
966     if (STRCASEEQ (key, "ProductName")) {
967       fs->product_name = hivex_value_string (h, values[i]);
968       if (!fs->product_name) {
969         perrorf (g, "hivex_value_string");
970         free (key);
971         goto out;
972       }
973     }
974     else if (STRCASEEQ (key, "CurrentVersion")) {
975       char *version = hivex_value_string (h, values[i]);
976       if (!version) {
977         perrorf (g, "hivex_value_string");
978         free (key);
979         goto out;
980       }
981       char *major, *minor;
982       if (match2 (g, version, re_windows_version, &major, &minor)) {
983         fs->major_version = parse_unsigned_int (g, major);
984         free (major);
985         if (fs->major_version == -1) {
986           free (minor);
987           free (key);
988           free (version);
989           goto out;
990         }
991         fs->minor_version = parse_unsigned_int (g, minor);
992         free (minor);
993         if (fs->minor_version == -1) {
994           free (key);
995           free (version);
996           goto out;
997         }
998       }
999
1000       free (version);
1001     }
1002
1003     free (key);
1004   }
1005
1006   ret = 0;
1007
1008  out:
1009   if (h) hivex_close (h);
1010   free (values);
1011   free (software_path);
1012
1013   /* Free up the temporary file. */
1014   unlink (software_local);
1015 #undef software_local_len
1016
1017   return ret;
1018 }
1019
1020 static char *
1021 resolve_windows_path_silently (guestfs_h *g, const char *path)
1022 {
1023   guestfs_error_handler_cb old_error_cb = g->error_cb;
1024   g->error_cb = NULL;
1025   char *ret = guestfs_case_sensitive_path (g, path);
1026   g->error_cb = old_error_cb;
1027   return ret;
1028 }
1029
1030 static int
1031 extend_fses (guestfs_h *g)
1032 {
1033   size_t n = g->nr_fses + 1;
1034   struct inspect_fs *p;
1035
1036   p = realloc (g->fses, n * sizeof (struct inspect_fs));
1037   if (p == NULL) {
1038     perrorf (g, "realloc");
1039     return -1;
1040   }
1041
1042   g->fses = p;
1043   g->nr_fses = n;
1044
1045   memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
1046
1047   return 0;
1048 }
1049
1050 /* Parse small, unsigned ints, as used in version numbers. */
1051 static int
1052 parse_unsigned_int (guestfs_h *g, const char *str)
1053 {
1054   long ret;
1055   int r = xstrtol (str, NULL, 10, &ret, "");
1056   if (r != LONGINT_OK) {
1057     error (g, _("could not parse integer in version number: %s"), str);
1058     return -1;
1059   }
1060   return ret;
1061 }
1062
1063 /* At the moment, package format and package management is just a
1064  * simple function of the distro and major_version fields, so these
1065  * can never return an error.  We might be cleverer in future.
1066  */
1067 static void
1068 check_package_format (guestfs_h *g, struct inspect_fs *fs)
1069 {
1070   switch (fs->distro) {
1071   case OS_DISTRO_FEDORA:
1072   case OS_DISTRO_MEEGO:
1073   case OS_DISTRO_REDHAT_BASED:
1074   case OS_DISTRO_RHEL:
1075   case OS_DISTRO_MANDRIVA:
1076     fs->package_format = OS_PACKAGE_FORMAT_RPM;
1077     break;
1078
1079   case OS_DISTRO_DEBIAN:
1080   case OS_DISTRO_UBUNTU:
1081   case OS_DISTRO_LINUX_MINT:
1082     fs->package_format = OS_PACKAGE_FORMAT_DEB;
1083     break;
1084
1085   case OS_DISTRO_ARCHLINUX:
1086     fs->package_format = OS_PACKAGE_FORMAT_PACMAN;
1087     break;
1088   case OS_DISTRO_GENTOO:
1089     fs->package_format = OS_PACKAGE_FORMAT_EBUILD;
1090     break;
1091   case OS_DISTRO_PARDUS:
1092     fs->package_format = OS_PACKAGE_FORMAT_PISI;
1093     break;
1094
1095   case OS_DISTRO_WINDOWS:
1096   case OS_DISTRO_UNKNOWN:
1097   default:
1098     fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN;
1099     break;
1100   }
1101 }
1102
1103 static void
1104 check_package_management (guestfs_h *g, struct inspect_fs *fs)
1105 {
1106   switch (fs->distro) {
1107   case OS_DISTRO_FEDORA:
1108   case OS_DISTRO_MEEGO:
1109     fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1110     break;
1111
1112   case OS_DISTRO_REDHAT_BASED:
1113   case OS_DISTRO_RHEL:
1114     if (fs->major_version >= 5)
1115       fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
1116     else
1117       fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE;
1118     break;
1119
1120   case OS_DISTRO_DEBIAN:
1121   case OS_DISTRO_UBUNTU:
1122   case OS_DISTRO_LINUX_MINT:
1123     fs->package_management = OS_PACKAGE_MANAGEMENT_APT;
1124     break;
1125
1126   case OS_DISTRO_ARCHLINUX:
1127     fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN;
1128     break;
1129   case OS_DISTRO_GENTOO:
1130     fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE;
1131     break;
1132   case OS_DISTRO_PARDUS:
1133     fs->package_management = OS_PACKAGE_MANAGEMENT_PISI;
1134     break;
1135   case OS_DISTRO_MANDRIVA:
1136     fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI;
1137     break;
1138
1139   case OS_DISTRO_WINDOWS:
1140   case OS_DISTRO_UNKNOWN:
1141   default:
1142     fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
1143     break;
1144   }
1145 }
1146
1147 static struct inspect_fs *
1148 search_for_root (guestfs_h *g, const char *root)
1149 {
1150   if (g->nr_fses == 0) {
1151     error (g, _("no inspection data: call guestfs_inspect_os first"));
1152     return NULL;
1153   }
1154
1155   size_t i;
1156   struct inspect_fs *fs;
1157   for (i = 0; i < g->nr_fses; ++i) {
1158     fs = &g->fses[i];
1159     if (fs->is_root && STREQ (root, fs->device))
1160       return fs;
1161   }
1162
1163   error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"),
1164          root);
1165   return NULL;
1166 }
1167
1168 char **
1169 guestfs__inspect_get_roots (guestfs_h *g)
1170 {
1171   /* NB. Doesn't matter if g->nr_fses == 0.  We just return an empty
1172    * list in this case.
1173    */
1174
1175   size_t i;
1176   size_t count = 0;
1177   for (i = 0; i < g->nr_fses; ++i)
1178     if (g->fses[i].is_root)
1179       count++;
1180
1181   char **ret = calloc (count+1, sizeof (char *));
1182   if (ret == NULL) {
1183     perrorf (g, "calloc");
1184     return NULL;
1185   }
1186
1187   count = 0;
1188   for (i = 0; i < g->nr_fses; ++i) {
1189     if (g->fses[i].is_root) {
1190       ret[count] = safe_strdup (g, g->fses[i].device);
1191       count++;
1192     }
1193   }
1194   ret[count] = NULL;
1195
1196   return ret;
1197 }
1198
1199 char *
1200 guestfs__inspect_get_type (guestfs_h *g, const char *root)
1201 {
1202   struct inspect_fs *fs = search_for_root (g, root);
1203   if (!fs)
1204     return NULL;
1205
1206   char *ret;
1207   switch (fs->type) {
1208   case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
1209   case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
1210   case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
1211   case OS_TYPE_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1212   }
1213
1214   return ret;
1215 }
1216
1217 char *
1218 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
1219 {
1220   struct inspect_fs *fs = search_for_root (g, root);
1221   if (!fs)
1222     return NULL;
1223
1224   return safe_strdup (g, fs->arch ? : "unknown");
1225 }
1226
1227 char *
1228 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
1229 {
1230   struct inspect_fs *fs = search_for_root (g, root);
1231   if (!fs)
1232     return NULL;
1233
1234   char *ret;
1235   switch (fs->distro) {
1236   case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
1237   case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
1238   case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
1239   case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
1240   case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
1241   case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
1242   case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
1243   case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
1244   case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
1245   case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
1246   case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
1247   case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
1248   case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break;
1249   }
1250
1251   return ret;
1252 }
1253
1254 int
1255 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
1256 {
1257   struct inspect_fs *fs = search_for_root (g, root);
1258   if (!fs)
1259     return -1;
1260
1261   return fs->major_version;
1262 }
1263
1264 int
1265 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
1266 {
1267   struct inspect_fs *fs = search_for_root (g, root);
1268   if (!fs)
1269     return -1;
1270
1271   return fs->minor_version;
1272 }
1273
1274 char *
1275 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
1276 {
1277   struct inspect_fs *fs = search_for_root (g, root);
1278   if (!fs)
1279     return NULL;
1280
1281   return safe_strdup (g, fs->product_name ? : "unknown");
1282 }
1283
1284 char *
1285 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
1286 {
1287   struct inspect_fs *fs = search_for_root (g, root);
1288   if (!fs)
1289     return NULL;
1290
1291   if (!fs->windows_systemroot) {
1292     error (g, _("not a Windows guest, or systemroot could not be determined"));
1293     return NULL;
1294   }
1295
1296   return safe_strdup (g, fs->windows_systemroot);
1297 }
1298
1299 char **
1300 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
1301 {
1302   struct inspect_fs *fs = search_for_root (g, root);
1303   if (!fs)
1304     return NULL;
1305
1306   char **ret;
1307
1308   /* If no fstab information (Windows) return just the root. */
1309   if (fs->nr_fstab == 0) {
1310     ret = calloc (3, sizeof (char *));
1311     ret[0] = safe_strdup (g, "/");
1312     ret[1] = safe_strdup (g, root);
1313     ret[2] = NULL;
1314     return ret;
1315   }
1316
1317 #define CRITERION fs->fstab[i].mountpoint[0] == '/'
1318   size_t i, count = 0;
1319   for (i = 0; i < fs->nr_fstab; ++i)
1320     if (CRITERION)
1321       count++;
1322
1323   /* Hashtables have 2N+1 entries. */
1324   ret = calloc (2*count+1, sizeof (char *));
1325   if (ret == NULL) {
1326     perrorf (g, "calloc");
1327     return NULL;
1328   }
1329
1330   count = 0;
1331   for (i = 0; i < fs->nr_fstab; ++i)
1332     if (CRITERION) {
1333       ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
1334       ret[2*count+1] = safe_strdup (g, fs->fstab[i].device);
1335       count++;
1336     }
1337 #undef CRITERION
1338
1339   return ret;
1340 }
1341
1342 char **
1343 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
1344 {
1345   struct inspect_fs *fs = search_for_root (g, root);
1346   if (!fs)
1347     return NULL;
1348
1349   char **ret;
1350
1351   /* If no fstab information (Windows) return just the root. */
1352   if (fs->nr_fstab == 0) {
1353     ret = calloc (2, sizeof (char *));
1354     ret[0] = safe_strdup (g, root);
1355     ret[1] = NULL;
1356     return ret;
1357   }
1358
1359   ret = calloc (fs->nr_fstab + 1, sizeof (char *));
1360   if (ret == NULL) {
1361     perrorf (g, "calloc");
1362     return NULL;
1363   }
1364
1365   size_t i;
1366   for (i = 0; i < fs->nr_fstab; ++i)
1367     ret[i] = safe_strdup (g, fs->fstab[i].device);
1368
1369   return ret;
1370 }
1371
1372 char *
1373 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
1374 {
1375   struct inspect_fs *fs = search_for_root (g, root);
1376   if (!fs)
1377     return NULL;
1378
1379   char *ret;
1380   switch (fs->package_format) {
1381   case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
1382   case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
1383   case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
1384   case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
1385   case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
1386   case OS_PACKAGE_FORMAT_UNKNOWN:
1387   default:
1388     ret = safe_strdup (g, "unknown");
1389     break;
1390   }
1391
1392   return ret;
1393 }
1394
1395 char *
1396 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
1397 {
1398   struct inspect_fs *fs = search_for_root (g, root);
1399   if (!fs)
1400     return NULL;
1401
1402   char *ret;
1403   switch (fs->package_management) {
1404   case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
1405   case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
1406   case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
1407   case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
1408   case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
1409   case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
1410   case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
1411   case OS_PACKAGE_MANAGEMENT_UNKNOWN:
1412   default:
1413     ret = safe_strdup (g, "unknown");
1414     break;
1415   }
1416
1417   return ret;
1418 }
1419
1420 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
1421 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
1422 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
1423 static void add_application (guestfs_h *g, struct guestfs_application_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *install_path, const char *publisher, const char *url, const char *description);
1424 static void sort_applications (struct guestfs_application_list *);
1425
1426 /* Unlike the simple inspect-get-* calls, this one assumes that the
1427  * disks are mounted up, and reads files from the mounted disks.
1428  */
1429 struct guestfs_application_list *
1430 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
1431 {
1432   struct inspect_fs *fs = search_for_root (g, root);
1433   if (!fs)
1434     return NULL;
1435
1436   struct guestfs_application_list *ret = NULL;
1437
1438   switch (fs->type) {
1439   case OS_TYPE_LINUX:
1440     switch (fs->package_format) {
1441     case OS_PACKAGE_FORMAT_RPM:
1442       ret = list_applications_rpm (g, fs);
1443       if (ret == NULL)
1444         return NULL;
1445       break;
1446
1447     case OS_PACKAGE_FORMAT_DEB:
1448       ret = list_applications_deb (g, fs);
1449       if (ret == NULL)
1450         return NULL;
1451       break;
1452
1453     case OS_PACKAGE_FORMAT_PACMAN:
1454     case OS_PACKAGE_FORMAT_EBUILD:
1455     case OS_PACKAGE_FORMAT_PISI:
1456     case OS_PACKAGE_FORMAT_UNKNOWN:
1457     default:
1458       /* nothing - keep GCC happy */;
1459     }
1460     break;
1461
1462   case OS_TYPE_WINDOWS:
1463     ret = list_applications_windows (g, fs);
1464     if (ret == NULL)
1465       return NULL;
1466     break;
1467
1468   case OS_TYPE_FREEBSD:
1469   case OS_TYPE_UNKNOWN:
1470   default:
1471       /* nothing - keep GCC happy */;
1472   }
1473
1474   if (ret == NULL) {
1475     /* Don't know how to do inspection.  Not an error, return an
1476      * empty list.
1477      */
1478     ret = safe_malloc (g, sizeof *ret);
1479     ret->len = 0;
1480     ret->val = NULL;
1481   }
1482
1483   sort_applications (ret);
1484
1485   return ret;
1486 }
1487
1488 static struct guestfs_application_list *
1489 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
1490 {
1491   TMP_TEMPLATE_ON_STACK (tmpfile);
1492
1493   if (download_to_tmp (g, "/var/lib/rpm/Name", tmpfile, 10000000) == -1)
1494     return NULL;
1495
1496   struct guestfs_application_list *apps = NULL, *ret = NULL;
1497 #define cmd_len (strlen (tmpfile) + 64)
1498   char cmd[cmd_len];
1499   FILE *pp = NULL;
1500   char line[1024];
1501   size_t len;
1502
1503   snprintf (cmd, cmd_len, "db_dump -p '%s'", tmpfile);
1504
1505   if (g->verbose)
1506     fprintf (stderr, "list_applications_rpm: %s\n", cmd);
1507
1508   pp = popen (cmd, "r");
1509   if (pp == NULL) {
1510     perrorf (g, "popen: %s", cmd);
1511     goto out;
1512   }
1513
1514   /* Ignore everything to end-of-header marker. */
1515   for (;;) {
1516     if (fgets (line, sizeof line, pp) == NULL) {
1517       error (g, _("unexpected end of output from db_dump command"));
1518       goto out;
1519     }
1520
1521     len = strlen (line);
1522     if (len > 0 && line[len-1] == '\n') {
1523       line[len-1] = '\0';
1524       len--;
1525     }
1526
1527     if (STREQ (line, "HEADER=END"))
1528       break;
1529   }
1530
1531   /* Allocate 'apps' list. */
1532   apps = safe_malloc (g, sizeof *apps);
1533   apps->len = 0;
1534   apps->val = NULL;
1535
1536   /* Read alternate lines until end of data marker. */
1537   for (;;) {
1538     if (fgets (line, sizeof line, pp) == NULL) {
1539       error (g, _("unexpected end of output from db_dump command"));
1540       goto out;
1541     }
1542
1543     len = strlen (line);
1544     if (len > 0 && line[len-1] == '\n') {
1545       line[len-1] = '\0';
1546       len--;
1547     }
1548
1549     if (STREQ (line, "DATA=END"))
1550       break;
1551
1552     char *p = line;
1553     if (len > 0 && line[0] == ' ')
1554       p = line+1;
1555     /* Ignore any application name that contains non-printable chars.
1556      * In the db_dump output these would be escaped with backslash, so
1557      * we can just ignore any such line.
1558      */
1559     if (strchr (p, '\\') == NULL)
1560       add_application (g, apps, p, "", 0, "", "", "", "", "", "");
1561
1562     /* Discard next line. */
1563     if (fgets (line, sizeof line, pp) == NULL) {
1564       error (g, _("unexpected end of output from db_dump command"));
1565       goto out;
1566     }
1567   }
1568
1569   /* Catch errors from the db_dump command. */
1570   if (pclose (pp) == -1) {
1571     perrorf (g, "pclose: %s", cmd);
1572     goto out;
1573   }
1574   pp = NULL;
1575
1576   ret = apps;
1577
1578  out:
1579   if (ret == NULL && apps != NULL)
1580     guestfs_free_application_list (apps);
1581   if (pp)
1582     pclose (pp);
1583   unlink (tmpfile);
1584 #undef cmd_len
1585
1586   return ret;
1587 }
1588
1589 static struct guestfs_application_list *
1590 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
1591 {
1592   TMP_TEMPLATE_ON_STACK (tmpfile);
1593
1594   if (download_to_tmp (g, "/var/lib/dpkg/status", tmpfile, 10000000) == -1)
1595     return NULL;
1596
1597   struct guestfs_application_list *apps = NULL, *ret = NULL;
1598   FILE *fp = NULL;
1599   char line[1024];
1600   size_t len;
1601   char *name = NULL, *version = NULL, *release = NULL;
1602   int installed_flag = 0;
1603
1604   fp = fopen (tmpfile, "r");
1605   if (fp == NULL) {
1606     perrorf (g, "fopen: %s", tmpfile);
1607     goto out;
1608   }
1609
1610   /* Allocate 'apps' list. */
1611   apps = safe_malloc (g, sizeof *apps);
1612   apps->len = 0;
1613   apps->val = NULL;
1614
1615   /* Read the temporary file.  Each package entry is separated by
1616    * a blank line.
1617    * XXX Strictly speaking this is in mailbox header format, so it
1618    * would be possible for fields to spread across multiple lines,
1619    * although for the short fields that we are concerned about this is
1620    * unlikely and not seen in practice.
1621    */
1622   while (fgets (line, sizeof line, fp) != NULL) {
1623     len = strlen (line);
1624     if (len > 0 && line[len-1] == '\n') {
1625       line[len-1] = '\0';
1626       len--;
1627     }
1628
1629     if (STRPREFIX (line, "Package: ")) {
1630       free (name);
1631       name = safe_strdup (g, &line[9]);
1632     }
1633     else if (STRPREFIX (line, "Status: ")) {
1634       installed_flag = strstr (&line[8], "installed") != NULL;
1635     }
1636     else if (STRPREFIX (line, "Version: ")) {
1637       free (version);
1638       free (release);
1639       char *p = strchr (&line[9], '-');
1640       if (p) {
1641         *p = '\0';
1642         version = safe_strdup (g, &line[9]);
1643         release = safe_strdup (g, p+1);
1644       } else {
1645         version = safe_strdup (g, &line[9]);
1646         release = NULL;
1647       }
1648     }
1649     else if (STREQ (line, "")) {
1650       if (installed_flag && name && version)
1651         add_application (g, apps, name, "", 0, version, release ? : "",
1652                          "", "", "", "");
1653       free (name);
1654       free (version);
1655       free (release);
1656       name = version = release = NULL;
1657       installed_flag = 0;
1658     }
1659   }
1660
1661   if (fclose (fp) == -1) {
1662     perrorf (g, "fclose: %s", tmpfile);
1663     goto out;
1664   }
1665   fp = NULL;
1666
1667   ret = apps;
1668
1669  out:
1670   if (ret == NULL && apps != NULL)
1671     guestfs_free_application_list (apps);
1672   if (fp)
1673     fclose (fp);
1674   free (name);
1675   free (version);
1676   free (release);
1677   unlink (tmpfile);
1678   return ret;
1679 }
1680
1681 /* XXX We already download the SOFTWARE hive when doing general
1682  * inspection.  We could avoid this second download of the same file
1683  * by caching these entries in the handle.
1684  */
1685 static struct guestfs_application_list *
1686 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
1687 {
1688   TMP_TEMPLATE_ON_STACK (software_local);
1689
1690   size_t len = strlen (fs->windows_systemroot) + 64;
1691   char software[len];
1692   snprintf (software, len, "%s/system32/config/software",
1693             fs->windows_systemroot);
1694
1695   char *software_path = resolve_windows_path_silently (g, software);
1696   if (!software_path)
1697     /* If the software hive doesn't exist, just accept that we cannot
1698      * find product_name etc.
1699      */
1700     return 0;
1701
1702   struct guestfs_application_list *apps = NULL, *ret = NULL;
1703   hive_h *h = NULL;
1704   hive_node_h *children = NULL;
1705
1706   if (download_to_tmp (g, software_path, software_local, 100000000) == -1)
1707     goto out;
1708
1709   h = hivex_open (software_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
1710   if (h == NULL) {
1711     perrorf (g, "hivex_open");
1712     goto out;
1713   }
1714
1715   hive_node_h node = hivex_root (h);
1716   const char *hivepath[] =
1717     { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
1718   size_t i;
1719   for (i = 0;
1720        node != 0 && i < sizeof hivepath / sizeof hivepath[0];
1721        ++i) {
1722     node = hivex_node_get_child (h, node, hivepath[i]);
1723   }
1724
1725   if (node == 0) {
1726     perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
1727     goto out;
1728   }
1729
1730   children = hivex_node_children (h, node);
1731   if (children == NULL) {
1732     perrorf (g, "hivex_node_children");
1733     goto out;
1734   }
1735
1736   /* Allocate 'apps' list. */
1737   apps = safe_malloc (g, sizeof *apps);
1738   apps->len = 0;
1739   apps->val = NULL;
1740
1741   /* Consider any child node that has a DisplayName key.
1742    * See also:
1743    * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
1744    */
1745   for (i = 0; children[i] != 0; ++i) {
1746     hive_value_h value;
1747     char *name = NULL;
1748     char *display_name = NULL;
1749     char *version = NULL;
1750     char *install_path = NULL;
1751     char *publisher = NULL;
1752     char *url = NULL;
1753     char *comments = NULL;
1754
1755     /* Use the node name as a proxy for the package name in Linux.  The
1756      * display name is not language-independent, so it cannot be used.
1757      */
1758     name = hivex_node_name (h, children[i]);
1759     if (name == NULL) {
1760       perrorf (g, "hivex_node_get_name");
1761       goto out;
1762     }
1763
1764     value = hivex_node_get_value (h, children[i], "DisplayName");
1765     if (value) {
1766       display_name = hivex_value_string (h, value);
1767       if (display_name) {
1768         value = hivex_node_get_value (h, children[i], "DisplayVersion");
1769         if (value)
1770           version = hivex_value_string (h, value);
1771         value = hivex_node_get_value (h, children[i], "InstallLocation");
1772         if (value)
1773           install_path = hivex_value_string (h, value);
1774         value = hivex_node_get_value (h, children[i], "Publisher");
1775         if (value)
1776           publisher = hivex_value_string (h, value);
1777         value = hivex_node_get_value (h, children[i], "URLInfoAbout");
1778         if (value)
1779           url = hivex_value_string (h, value);
1780         value = hivex_node_get_value (h, children[i], "Comments");
1781         if (value)
1782           comments = hivex_value_string (h, value);
1783
1784         add_application (g, apps, name, display_name, 0,
1785                          version ? : "",
1786                          "",
1787                          install_path ? : "",
1788                          publisher ? : "",
1789                          url ? : "",
1790                          comments ? : "");
1791       }
1792     }
1793
1794     free (name);
1795     free (display_name);
1796     free (version);
1797     free (install_path);
1798     free (publisher);
1799     free (url);
1800     free (comments);
1801   }
1802
1803   ret = apps;
1804
1805  out:
1806   if (ret == NULL && apps != NULL)
1807     guestfs_free_application_list (apps);
1808   if (h) hivex_close (h);
1809   free (children);
1810   free (software_path);
1811
1812   /* Free up the temporary file. */
1813   unlink (software_local);
1814 #undef software_local_len
1815
1816   return ret;
1817 }
1818
1819 static void
1820 add_application (guestfs_h *g, struct guestfs_application_list *apps,
1821                  const char *name, const char *display_name, int32_t epoch,
1822                  const char *version, const char *release,
1823                  const char *install_path,
1824                  const char *publisher, const char *url,
1825                  const char *description)
1826 {
1827   apps->len++;
1828   apps->val = safe_realloc (g, apps->val,
1829                             apps->len * sizeof (struct guestfs_application));
1830   apps->val[apps->len-1].app_name = safe_strdup (g, name);
1831   apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
1832   apps->val[apps->len-1].app_epoch = epoch;
1833   apps->val[apps->len-1].app_version = safe_strdup (g, version);
1834   apps->val[apps->len-1].app_release = safe_strdup (g, release);
1835   apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
1836   /* XXX Translated path is not implemented yet. */
1837   apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
1838   apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
1839   apps->val[apps->len-1].app_url = safe_strdup (g, url);
1840   /* XXX The next two are not yet implemented for any package
1841    * format, but we could easily support them for rpm and deb.
1842    */
1843   apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
1844   apps->val[apps->len-1].app_summary = safe_strdup (g, "");
1845   apps->val[apps->len-1].app_description = safe_strdup (g, description);
1846 }
1847
1848 /* Sort applications by name before returning the list. */
1849 static int
1850 compare_applications (const void *vp1, const void *vp2)
1851 {
1852   const struct guestfs_application *v1 = vp1;
1853   const struct guestfs_application *v2 = vp2;
1854
1855   return strcmp (v1->app_name, v2->app_name);
1856 }
1857
1858 static void
1859 sort_applications (struct guestfs_application_list *apps)
1860 {
1861   if (apps && apps->val)
1862     qsort (apps->val, apps->len, sizeof (struct guestfs_application),
1863            compare_applications);
1864 }
1865
1866 /* Download to a guest file to a local temporary file.  Refuse to
1867  * download the guest file if it is larger than max_size.  The caller
1868  * is responsible for deleting the temporary file after use.
1869  */
1870 static int
1871 download_to_tmp (guestfs_h *g, const char *filename,
1872                  char *localtmp, int64_t max_size)
1873 {
1874   int fd;
1875   char buf[32];
1876   int64_t size;
1877
1878   size = guestfs_filesize (g, filename);
1879   if (size == -1)
1880     /* guestfs_filesize failed and has already set error in handle */
1881     return -1;
1882   if (size > max_size) {
1883     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1884            filename, size);
1885     return -1;
1886   }
1887
1888   fd = mkstemp (localtmp);
1889   if (fd == -1) {
1890     perrorf (g, "mkstemp");
1891     return -1;
1892   }
1893
1894   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
1895
1896   if (guestfs_download (g, filename, buf) == -1) {
1897     close (fd);
1898     unlink (localtmp);
1899     return -1;
1900   }
1901
1902   if (close (fd) == -1) {
1903     perrorf (g, "close: %s", localtmp);
1904     unlink (localtmp);
1905     return -1;
1906   }
1907
1908   return 0;
1909 }
1910
1911 /* Call 'f' with Augeas opened and having parsed 'filename' (this file
1912  * must exist).  As a security measure, this bails if the file is too
1913  * large for a reasonable configuration file.  After the call to 'f'
1914  * Augeas is closed.
1915  */
1916 static int
1917 inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char *filename,
1918                      int (*f) (guestfs_h *, struct inspect_fs *))
1919 {
1920   /* Security: Refuse to do this if filename is too large. */
1921   int64_t size = guestfs_filesize (g, filename);
1922   if (size == -1)
1923     /* guestfs_filesize failed and has already set error in handle */
1924     return -1;
1925   if (size > 100000) {
1926     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1927            filename, size);
1928     return -1;
1929   }
1930
1931   /* If !feature_available (g, "augeas") then the next call will fail.
1932    * Arguably we might want to fall back to a non-Augeas method in
1933    * this case.
1934    */
1935   if (guestfs_aug_init (g, "/", 16|32) == -1)
1936     return -1;
1937
1938   int r = -1;
1939
1940   /* Tell Augeas to only load one file (thanks Raphaël Pinson). */
1941   char buf[strlen (filename) + 64];
1942   snprintf (buf, strlen (filename) + 64, "/augeas/load//incl[. != \"%s\"]",
1943             filename);
1944   if (guestfs_aug_rm (g, buf) == -1)
1945     goto out;
1946
1947   if (guestfs_aug_load (g) == -1)
1948     goto out;
1949
1950   r = f (g, fs);
1951
1952  out:
1953   guestfs_aug_close (g);
1954
1955   return r;
1956 }
1957
1958 /* Get the first line of a small file, without any trailing newline
1959  * character.
1960  */
1961 static char *
1962 first_line_of_file (guestfs_h *g, const char *filename)
1963 {
1964   char **lines;
1965   int64_t size;
1966   char *ret;
1967
1968   /* Don't trust guestfs_head_n not to break with very large files.
1969    * Check the file size is something reasonable first.
1970    */
1971   size = guestfs_filesize (g, filename);
1972   if (size == -1)
1973     /* guestfs_filesize failed and has already set error in handle */
1974     return NULL;
1975   if (size > 1000000) {
1976     error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
1977            filename, size);
1978     return NULL;
1979   }
1980
1981   lines = guestfs_head_n (g, 1, filename);
1982   if (lines == NULL)
1983     return NULL;
1984   if (lines[0] == NULL) {
1985     error (g, _("%s: file is empty"), filename);
1986     guestfs___free_string_list (lines);
1987     return NULL;
1988   }
1989   /* lines[1] should be NULL because of '1' argument above ... */
1990
1991   ret = lines[0];               /* caller frees */
1992   free (lines);                 /* free the array */
1993
1994   return ret;
1995 }
1996
1997 #else /* no PCRE or hivex at compile time */
1998
1999 /* XXX These functions should be in an optgroup. */
2000
2001 #define NOT_IMPL(r)                                                     \
2002   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
2003   return r
2004
2005 char **
2006 guestfs__inspect_os (guestfs_h *g)
2007 {
2008   NOT_IMPL(NULL);
2009 }
2010
2011 char **
2012 guestfs__inspect_get_roots (guestfs_h *g)
2013 {
2014   NOT_IMPL(NULL);
2015 }
2016
2017 char *
2018 guestfs__inspect_get_type (guestfs_h *g, const char *root)
2019 {
2020   NOT_IMPL(NULL);
2021 }
2022
2023 char *
2024 guestfs__inspect_get_arch (guestfs_h *g, const char *root)
2025 {
2026   NOT_IMPL(NULL);
2027 }
2028
2029 char *
2030 guestfs__inspect_get_distro (guestfs_h *g, const char *root)
2031 {
2032   NOT_IMPL(NULL);
2033 }
2034
2035 int
2036 guestfs__inspect_get_major_version (guestfs_h *g, const char *root)
2037 {
2038   NOT_IMPL(-1);
2039 }
2040
2041 int
2042 guestfs__inspect_get_minor_version (guestfs_h *g, const char *root)
2043 {
2044   NOT_IMPL(-1);
2045 }
2046
2047 char *
2048 guestfs__inspect_get_product_name (guestfs_h *g, const char *root)
2049 {
2050   NOT_IMPL(NULL);
2051 }
2052
2053 char *
2054 guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root)
2055 {
2056   NOT_IMPL(NULL);
2057 }
2058
2059 char **
2060 guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root)
2061 {
2062   NOT_IMPL(NULL);
2063 }
2064
2065 char **
2066 guestfs__inspect_get_filesystems (guestfs_h *g, const char *root)
2067 {
2068   NOT_IMPL(NULL);
2069 }
2070
2071 char *
2072 guestfs__inspect_get_package_format (guestfs_h *g, const char *root)
2073 {
2074   NOT_IMPL(NULL);
2075 }
2076
2077 char *
2078 guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
2079 {
2080   NOT_IMPL(NULL);
2081 }
2082
2083 struct guestfs_application_list *
2084 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
2085 {
2086   NOT_IMPL(NULL);
2087 }
2088
2089 #endif /* no PCRE or hivex at compile time */
2090
2091 void
2092 guestfs___free_inspect_info (guestfs_h *g)
2093 {
2094   size_t i;
2095   for (i = 0; i < g->nr_fses; ++i) {
2096     free (g->fses[i].device);
2097     free (g->fses[i].product_name);
2098     free (g->fses[i].arch);
2099     free (g->fses[i].windows_systemroot);
2100     size_t j;
2101     for (j = 0; j < g->fses[i].nr_fstab; ++j) {
2102       free (g->fses[i].fstab[j].device);
2103       free (g->fses[i].fstab[j].mountpoint);
2104     }
2105     free (g->fses[i].fstab);
2106   }
2107   free (g->fses);
2108   g->nr_fses = 0;
2109   g->fses = NULL;
2110 }
2111
2112 /* In the Perl code this is a public function. */
2113 int
2114 guestfs___feature_available (guestfs_h *g, const char *feature)
2115 {
2116   /* If there's an error we should ignore it, so to do that we have to
2117    * temporarily replace the error handler with a null one.
2118    */
2119   guestfs_error_handler_cb old_error_cb = g->error_cb;
2120   g->error_cb = NULL;
2121
2122   const char *groups[] = { feature, NULL };
2123   int r = guestfs_available (g, (char * const *) groups);
2124
2125   g->error_cb = old_error_cb;
2126
2127   return r == 0 ? 1 : 0;
2128 }
2129
2130 #ifdef HAVE_PCRE
2131
2132 /* Match a regular expression which contains no captures.  Returns
2133  * true if it matches or false if it doesn't.
2134  */
2135 int
2136 guestfs___match (guestfs_h *g, const char *str, const pcre *re)
2137 {
2138   size_t len = strlen (str);
2139   int vec[30], r;
2140
2141   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
2142   if (r == PCRE_ERROR_NOMATCH)
2143     return 0;
2144   if (r != 1) {
2145     /* Internal error -- should not happen. */
2146     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2147              __FILE__, __func__, r, str);
2148     return 0;
2149   }
2150
2151   return 1;
2152 }
2153
2154 /* Match a regular expression which contains exactly one capture.  If
2155  * the string matches, return the capture, otherwise return NULL.  The
2156  * caller must free the result.
2157  */
2158 char *
2159 guestfs___match1 (guestfs_h *g, const char *str, const pcre *re)
2160 {
2161   size_t len = strlen (str);
2162   int vec[30], r;
2163
2164   r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]);
2165   if (r == PCRE_ERROR_NOMATCH)
2166     return NULL;
2167   if (r != 2) {
2168     /* Internal error -- should not happen. */
2169     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2170              __FILE__, __func__, r, str);
2171     return NULL;
2172   }
2173
2174   return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2175 }
2176
2177 /* Match a regular expression which contains exactly two captures. */
2178 int
2179 guestfs___match2 (guestfs_h *g, const char *str, const pcre *re,
2180                   char **ret1, char **ret2)
2181 {
2182   size_t len = strlen (str);
2183   int vec[30], r;
2184
2185   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
2186   if (r == PCRE_ERROR_NOMATCH)
2187     return 0;
2188   if (r != 3) {
2189     /* Internal error -- should not happen. */
2190     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2191              __FILE__, __func__, r, str);
2192     return 0;
2193   }
2194
2195   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2196   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
2197
2198   return 1;
2199 }
2200
2201 /* Match a regular expression which contains exactly three captures. */
2202 int
2203 guestfs___match3 (guestfs_h *g, const char *str, const pcre *re,
2204                   char **ret1, char **ret2, char **ret3)
2205 {
2206   size_t len = strlen (str);
2207   int vec[30], r;
2208
2209   r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30);
2210   if (r == PCRE_ERROR_NOMATCH)
2211     return 0;
2212   if (r != 4) {
2213     /* Internal error -- should not happen. */
2214     fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n",
2215              __FILE__, __func__, r, str);
2216     return 0;
2217   }
2218
2219   *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]);
2220   *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]);
2221   *ret3 = safe_strndup (g, &str[vec[6]], vec[7]-vec[6]);
2222
2223   return 1;
2224 }
2225
2226 #endif /* HAVE_PCRE */