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