Version 1.15.7.
[libguestfs.git] / src / inspect_apps.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 <fcntl.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <endian.h>
31
32 #include <pcre.h>
33
34 #ifdef HAVE_HIVEX
35 #include <hivex.h>
36 #endif
37
38 #include "c-ctype.h"
39 #include "ignore-value.h"
40 #include "xstrtol.h"
41
42 #include "guestfs.h"
43 #include "guestfs-internal.h"
44 #include "guestfs-internal-actions.h"
45 #include "guestfs_protocol.h"
46
47 #if defined(HAVE_HIVEX)
48
49 #ifdef DB_DUMP
50 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
51 #endif
52 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
53 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
54 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);
55 static void sort_applications (struct guestfs_application_list *);
56
57 /* Unlike the simple inspect-get-* calls, this one assumes that the
58  * disks are mounted up, and reads files from the mounted disks.
59  */
60 struct guestfs_application_list *
61 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
62 {
63   struct inspect_fs *fs = guestfs___search_for_root (g, root);
64   if (!fs)
65     return NULL;
66
67   struct guestfs_application_list *ret = NULL;
68
69   /* Presently we can only list applications for installed disks.  It
70    * is possible in future to get lists of packages from installers.
71    */
72   if (fs->format == OS_FORMAT_INSTALLED) {
73     switch (fs->type) {
74     case OS_TYPE_LINUX:
75     case OS_TYPE_HURD:
76       switch (fs->package_format) {
77       case OS_PACKAGE_FORMAT_RPM:
78 #ifdef DB_DUMP
79         ret = list_applications_rpm (g, fs);
80         if (ret == NULL)
81           return NULL;
82 #endif
83         break;
84
85       case OS_PACKAGE_FORMAT_DEB:
86         ret = list_applications_deb (g, fs);
87         if (ret == NULL)
88           return NULL;
89         break;
90
91       case OS_PACKAGE_FORMAT_PACMAN:
92       case OS_PACKAGE_FORMAT_EBUILD:
93       case OS_PACKAGE_FORMAT_PISI:
94       case OS_PACKAGE_FORMAT_PKGSRC:
95       case OS_PACKAGE_FORMAT_UNKNOWN:
96       default:
97         /* nothing - keep GCC happy */;
98       }
99       break;
100
101     case OS_TYPE_WINDOWS:
102       ret = list_applications_windows (g, fs);
103       if (ret == NULL)
104         return NULL;
105       break;
106
107     case OS_TYPE_FREEBSD:
108     case OS_TYPE_NETBSD:
109     case OS_TYPE_UNKNOWN:
110     default:
111       /* nothing - keep GCC happy */;
112     }
113   }
114
115   if (ret == NULL) {
116     /* Don't know how to do inspection.  Not an error, return an
117      * empty list.
118      */
119     ret = safe_malloc (g, sizeof *ret);
120     ret->len = 0;
121     ret->val = NULL;
122   }
123
124   sort_applications (ret);
125
126   return ret;
127 }
128
129 #ifdef DB_DUMP
130
131 /* This data comes from the Name database, and contains the application
132  * names and the first 4 bytes of the link field.
133  */
134 struct rpm_names_list {
135   struct rpm_name *names;
136   size_t len;
137 };
138 struct rpm_name {
139   char *name;
140   char link[4];
141 };
142
143 static void
144 free_rpm_names_list (struct rpm_names_list *list)
145 {
146   size_t i;
147
148   for (i = 0; i < list->len; ++i)
149     free (list->names[i].name);
150   free (list->names);
151 }
152
153 static int
154 compare_links (const void *av, const void *bv)
155 {
156   const struct rpm_name *a = av;
157   const struct rpm_name *b = bv;
158   return memcmp (a->link, b->link, 4);
159 }
160
161 static int
162 read_rpm_name (guestfs_h *g,
163                const unsigned char *key, size_t keylen,
164                const unsigned char *value, size_t valuelen,
165                void *listv)
166 {
167   struct rpm_names_list *list = listv;
168   char *name;
169
170   /* Ignore bogus entries. */
171   if (keylen == 0 || valuelen < 4)
172     return 0;
173
174   /* The name (key) field won't be NUL-terminated, so we must do that. */
175   name = safe_malloc (g, keylen+1);
176   memcpy (name, key, keylen);
177   name[keylen] = '\0';
178
179   list->names = safe_realloc (g, list->names,
180                               (list->len + 1) * sizeof (struct rpm_name));
181   list->names[list->len].name = name;
182   memcpy (list->names[list->len].link, value, 4);
183   list->len++;
184
185   return 0;
186 }
187
188 struct read_package_data {
189   struct rpm_names_list *list;
190   struct guestfs_application_list *apps;
191 };
192
193 static int
194 read_package (guestfs_h *g,
195               const unsigned char *key, size_t keylen,
196               const unsigned char *value, size_t valuelen,
197               void *datav)
198 {
199   struct read_package_data *data = datav;
200   struct rpm_name nkey, *entry;
201   char *p;
202   size_t len;
203   ssize_t max;
204   char *nul_name_nul, *version, *release;
205
206   /* This function reads one (key, value) pair from the Packages
207    * database.  The key is the link field (see struct rpm_name).  The
208    * value is a long binary string, but we can extract the version
209    * number from it as below.  First we have to look up the link field
210    * in the list of links (which is sorted by link field).
211    */
212
213   /* Ignore bogus entries. */
214   if (keylen < 4 || valuelen == 0)
215     return 0;
216
217   /* Look up the link (key) in the list. */
218   memcpy (nkey.link, key, 4);
219   entry = bsearch (&nkey, data->list->names, data->list->len,
220                    sizeof (struct rpm_name), compare_links);
221   if (!entry)
222     return 0;                   /* Not found - ignore it. */
223
224   /* We found a matching link entry, so that gives us the application
225    * name (entry->name).  Now we can get other data for this
226    * application out of the binary value string.  XXX This is a real
227    * hack.
228    */
229
230   /* Look for \0<name>\0 */
231   len = strlen (entry->name);
232   nul_name_nul = safe_malloc (g, len + 2);
233   nul_name_nul[0] = '\0';
234   memcpy (&nul_name_nul[1], entry->name, len);
235   nul_name_nul[len+1] = '\0';
236   p = memmem (value, valuelen, nul_name_nul, len+2);
237   free (nul_name_nul);
238   if (!p)
239     return 0;
240
241   /* Following that are \0-delimited version and release fields. */
242   p += len + 2; /* Note we have to skip \0 + name + \0. */
243   max = valuelen - (p - (char *) value);
244   if (max < 0)
245     max = 0;
246   version = safe_strndup (g, p, max);
247
248   len = strlen (version);
249   p += len + 1;
250   max = valuelen - (p - (char *) value);
251   if (max < 0)
252     max = 0;
253   release = safe_strndup (g, p, max);
254
255   /* Add the application and what we know. */
256   add_application (g, data->apps, entry->name, "", 0, version, release,
257                    "", "", "", "");
258
259   free (version);
260   free (release);
261
262   return 0;
263 }
264
265 static struct guestfs_application_list *
266 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
267 {
268   char *Name = NULL, *Packages = NULL;
269   struct rpm_names_list list = { .names = NULL, .len = 0 };
270   struct guestfs_application_list *apps = NULL;
271
272   Name = guestfs___download_to_tmp (g, fs,
273                                     "/var/lib/rpm/Name", "rpm_Name",
274                                     MAX_PKG_DB_SIZE);
275   if (Name == NULL)
276     goto error;
277
278   Packages = guestfs___download_to_tmp (g, fs,
279                                         "/var/lib/rpm/Packages", "rpm_Packages",
280                                         MAX_PKG_DB_SIZE);
281   if (Packages == NULL)
282     goto error;
283
284   /* Read Name database. */
285   if (guestfs___read_db_dump (g, Name, &list, read_rpm_name) == -1)
286     goto error;
287
288   /* Sort the names by link field for fast searching. */
289   qsort (list.names, list.len, sizeof (struct rpm_name), compare_links);
290
291   /* Allocate 'apps' list. */
292   apps = safe_malloc (g, sizeof *apps);
293   apps->len = 0;
294   apps->val = NULL;
295
296   /* Read Packages database. */
297   struct read_package_data data = { .list = &list, .apps = apps };
298   if (guestfs___read_db_dump (g, Packages, &data, read_package) == -1)
299     goto error;
300
301   free (Name);
302   free (Packages);
303   free_rpm_names_list (&list);
304
305   return apps;
306
307  error:
308   free (Name);
309   free (Packages);
310   free_rpm_names_list (&list);
311   if (apps != NULL)
312     guestfs_free_application_list (apps);
313
314   return NULL;
315 }
316
317 #endif /* defined DB_DUMP */
318
319 static struct guestfs_application_list *
320 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
321 {
322   char *status = NULL;
323   status = guestfs___download_to_tmp (g, fs, "/var/lib/dpkg/status", "status",
324                                       MAX_PKG_DB_SIZE);
325   if (status == NULL)
326     return NULL;
327
328   struct guestfs_application_list *apps = NULL, *ret = NULL;
329   FILE *fp = NULL;
330   char line[1024];
331   size_t len;
332   char *name = NULL, *version = NULL, *release = NULL;
333   int installed_flag = 0;
334
335   fp = fopen (status, "r");
336   if (fp == NULL) {
337     perrorf (g, "fopen: %s", status);
338     goto out;
339   }
340
341   /* Allocate 'apps' list. */
342   apps = safe_malloc (g, sizeof *apps);
343   apps->len = 0;
344   apps->val = NULL;
345
346   /* Read the temporary file.  Each package entry is separated by
347    * a blank line.
348    * XXX Strictly speaking this is in mailbox header format, so it
349    * would be possible for fields to spread across multiple lines,
350    * although for the short fields that we are concerned about this is
351    * unlikely and not seen in practice.
352    */
353   while (fgets (line, sizeof line, fp) != NULL) {
354     len = strlen (line);
355     if (len > 0 && line[len-1] == '\n') {
356       line[len-1] = '\0';
357       len--;
358     }
359
360     if (STRPREFIX (line, "Package: ")) {
361       free (name);
362       name = safe_strdup (g, &line[9]);
363     }
364     else if (STRPREFIX (line, "Status: ")) {
365       installed_flag = strstr (&line[8], "installed") != NULL;
366     }
367     else if (STRPREFIX (line, "Version: ")) {
368       free (version);
369       free (release);
370       char *p = strchr (&line[9], '-');
371       if (p) {
372         *p = '\0';
373         version = safe_strdup (g, &line[9]);
374         release = safe_strdup (g, p+1);
375       } else {
376         version = safe_strdup (g, &line[9]);
377         release = NULL;
378       }
379     }
380     else if (STREQ (line, "")) {
381       if (installed_flag && name && version)
382         add_application (g, apps, name, "", 0, version, release ? : "",
383                          "", "", "", "");
384       free (name);
385       free (version);
386       free (release);
387       name = version = release = NULL;
388       installed_flag = 0;
389     }
390   }
391
392   if (fclose (fp) == -1) {
393     perrorf (g, "fclose: %s", status);
394     goto out;
395   }
396   fp = NULL;
397
398   ret = apps;
399
400  out:
401   if (ret == NULL && apps != NULL)
402     guestfs_free_application_list (apps);
403   if (fp)
404     fclose (fp);
405   free (name);
406   free (version);
407   free (release);
408   free (status);
409   return ret;
410 }
411
412 static void list_applications_windows_from_path (guestfs_h *g, hive_h *h, struct guestfs_application_list *apps, const char **path, size_t path_len);
413
414 static struct guestfs_application_list *
415 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
416 {
417   size_t len = strlen (fs->windows_systemroot) + 64;
418   char software[len];
419   snprintf (software, len, "%s/system32/config/software",
420             fs->windows_systemroot);
421
422   char *software_path = guestfs___case_sensitive_path_silently (g, software);
423   if (!software_path) {
424     /* Missing software hive is a problem. */
425     error (g, "no HKLM\\SOFTWARE hive found in the guest");
426     return NULL;
427   }
428
429   char *software_hive = NULL;
430   struct guestfs_application_list *ret = NULL;
431   hive_h *h = NULL;
432
433   software_hive = guestfs___download_to_tmp (g, fs, software_path, "software",
434                                              MAX_REGISTRY_SIZE);
435   if (software_hive == NULL)
436     goto out;
437
438   free (software_path);
439   software_path = NULL;
440
441   h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
442   if (h == NULL) {
443     perrorf (g, "hivex_open");
444     goto out;
445   }
446
447   /* Allocate apps list. */
448   ret = safe_malloc (g, sizeof *ret);
449   ret->len = 0;
450   ret->val = NULL;
451
452   /* Ordinary native applications. */
453   const char *hivepath[] =
454     { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
455   list_applications_windows_from_path (g, h, ret, hivepath,
456                                        sizeof hivepath / sizeof hivepath[0]);
457
458   /* 32-bit emulated Windows apps running on the WOW64 emulator.
459    * http://support.microsoft.com/kb/896459 (RHBZ#692545).
460    */
461   const char *hivepath2[] =
462     { "WOW6432node", "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
463   list_applications_windows_from_path (g, h, ret, hivepath2,
464                                        sizeof hivepath2 / sizeof hivepath2[0]);
465
466  out:
467   if (h) hivex_close (h);
468   free (software_path);
469   free (software_hive);
470
471   return ret;
472 }
473
474 static void
475 list_applications_windows_from_path (guestfs_h *g, hive_h *h,
476                                      struct guestfs_application_list *apps,
477                                      const char **path, size_t path_len)
478 {
479   hive_node_h *children = NULL;
480   hive_node_h node;
481   size_t i;
482
483   node = hivex_root (h);
484
485   for (i = 0; node != 0 && i < path_len; ++i)
486     node = hivex_node_get_child (h, node, path[i]);
487
488   if (node == 0)
489     return;
490
491   children = hivex_node_children (h, node);
492   if (children == NULL)
493     return;
494
495   /* Consider any child node that has a DisplayName key.
496    * See also:
497    * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
498    */
499   for (i = 0; children[i] != 0; ++i) {
500     hive_value_h value;
501     char *name = NULL;
502     char *display_name = NULL;
503     char *version = NULL;
504     char *install_path = NULL;
505     char *publisher = NULL;
506     char *url = NULL;
507     char *comments = NULL;
508
509     /* Use the node name as a proxy for the package name in Linux.  The
510      * display name is not language-independent, so it cannot be used.
511      */
512     name = hivex_node_name (h, children[i]);
513     if (name == NULL)
514       continue;
515
516     value = hivex_node_get_value (h, children[i], "DisplayName");
517     if (value) {
518       display_name = hivex_value_string (h, value);
519       if (display_name) {
520         value = hivex_node_get_value (h, children[i], "DisplayVersion");
521         if (value)
522           version = hivex_value_string (h, value);
523         value = hivex_node_get_value (h, children[i], "InstallLocation");
524         if (value)
525           install_path = hivex_value_string (h, value);
526         value = hivex_node_get_value (h, children[i], "Publisher");
527         if (value)
528           publisher = hivex_value_string (h, value);
529         value = hivex_node_get_value (h, children[i], "URLInfoAbout");
530         if (value)
531           url = hivex_value_string (h, value);
532         value = hivex_node_get_value (h, children[i], "Comments");
533         if (value)
534           comments = hivex_value_string (h, value);
535
536         add_application (g, apps, name, display_name, 0,
537                          version ? : "",
538                          "",
539                          install_path ? : "",
540                          publisher ? : "",
541                          url ? : "",
542                          comments ? : "");
543       }
544     }
545
546     free (name);
547     free (display_name);
548     free (version);
549     free (install_path);
550     free (publisher);
551     free (url);
552     free (comments);
553   }
554
555   free (children);
556 }
557
558 static void
559 add_application (guestfs_h *g, struct guestfs_application_list *apps,
560                  const char *name, const char *display_name, int32_t epoch,
561                  const char *version, const char *release,
562                  const char *install_path,
563                  const char *publisher, const char *url,
564                  const char *description)
565 {
566   apps->len++;
567   apps->val = safe_realloc (g, apps->val,
568                             apps->len * sizeof (struct guestfs_application));
569   apps->val[apps->len-1].app_name = safe_strdup (g, name);
570   apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
571   apps->val[apps->len-1].app_epoch = epoch;
572   apps->val[apps->len-1].app_version = safe_strdup (g, version);
573   apps->val[apps->len-1].app_release = safe_strdup (g, release);
574   apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
575   /* XXX Translated path is not implemented yet. */
576   apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
577   apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
578   apps->val[apps->len-1].app_url = safe_strdup (g, url);
579   /* XXX The next two are not yet implemented for any package
580    * format, but we could easily support them for rpm and deb.
581    */
582   apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
583   apps->val[apps->len-1].app_summary = safe_strdup (g, "");
584   apps->val[apps->len-1].app_description = safe_strdup (g, description);
585 }
586
587 /* Sort applications by name before returning the list. */
588 static int
589 compare_applications (const void *vp1, const void *vp2)
590 {
591   const struct guestfs_application *v1 = vp1;
592   const struct guestfs_application *v2 = vp2;
593
594   return strcmp (v1->app_name, v2->app_name);
595 }
596
597 static void
598 sort_applications (struct guestfs_application_list *apps)
599 {
600   if (apps && apps->val)
601     qsort (apps->val, apps->len, sizeof (struct guestfs_application),
602            compare_applications);
603 }
604
605 #else /* no hivex at compile time */
606
607 /* XXX These functions should be in an optgroup. */
608
609 #define NOT_IMPL(r)                                                     \
610   error (g, _("inspection API not available since this version of libguestfs was compiled without the hivex library")); \
611   return r
612
613 struct guestfs_application_list *
614 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
615 {
616   NOT_IMPL(NULL);
617 }
618
619 #endif /* no hivex at compile time */