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