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