daemon: debug segv correct use of dereferencing NULL.
[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   char *Name = NULL, *Packages = NULL;
268   struct rpm_names_list list = { .names = NULL, .len = 0 };
269   struct guestfs_application_list *apps = NULL;
270
271   Name = guestfs___download_to_tmp (g, fs,
272                                     "/var/lib/rpm/Name", "rpm_Name",
273                                     MAX_PKG_DB_SIZE);
274   if (Name == NULL)
275     goto error;
276
277   Packages = guestfs___download_to_tmp (g, fs,
278                                         "/var/lib/rpm/Packages", "rpm_Packages",
279                                         MAX_PKG_DB_SIZE);
280   if (Packages == NULL)
281     goto error;
282
283   /* Read Name database. */
284   if (guestfs___read_db_dump (g, Name, &list, read_rpm_name) == -1)
285     goto error;
286
287   /* Sort the names by link field for fast searching. */
288   qsort (list.names, list.len, sizeof (struct rpm_name), compare_links);
289
290   /* Allocate 'apps' list. */
291   apps = safe_malloc (g, sizeof *apps);
292   apps->len = 0;
293   apps->val = NULL;
294
295   /* Read Packages database. */
296   struct read_package_data data = { .list = &list, .apps = apps };
297   if (guestfs___read_db_dump (g, Packages, &data, read_package) == -1)
298     goto error;
299
300   free (Name);
301   free (Packages);
302   free_rpm_names_list (&list);
303
304   return apps;
305
306  error:
307   free (Name);
308   free (Packages);
309   free_rpm_names_list (&list);
310   if (apps != NULL)
311     guestfs_free_application_list (apps);
312
313   return NULL;
314 }
315
316 #endif /* defined DB_DUMP */
317
318 static struct guestfs_application_list *
319 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
320 {
321   char *status = NULL;
322   status = guestfs___download_to_tmp (g, fs, "/var/lib/dpkg/status", "status",
323                                       MAX_PKG_DB_SIZE);
324   if (status == NULL)
325     return NULL;
326
327   struct guestfs_application_list *apps = NULL, *ret = NULL;
328   FILE *fp = NULL;
329   char line[1024];
330   size_t len;
331   char *name = NULL, *version = NULL, *release = NULL;
332   int installed_flag = 0;
333
334   fp = fopen (status, "r");
335   if (fp == NULL) {
336     perrorf (g, "fopen: %s", status);
337     goto out;
338   }
339
340   /* Allocate 'apps' list. */
341   apps = safe_malloc (g, sizeof *apps);
342   apps->len = 0;
343   apps->val = NULL;
344
345   /* Read the temporary file.  Each package entry is separated by
346    * a blank line.
347    * XXX Strictly speaking this is in mailbox header format, so it
348    * would be possible for fields to spread across multiple lines,
349    * although for the short fields that we are concerned about this is
350    * unlikely and not seen in practice.
351    */
352   while (fgets (line, sizeof line, fp) != NULL) {
353     len = strlen (line);
354     if (len > 0 && line[len-1] == '\n') {
355       line[len-1] = '\0';
356       len--;
357     }
358
359     if (STRPREFIX (line, "Package: ")) {
360       free (name);
361       name = safe_strdup (g, &line[9]);
362     }
363     else if (STRPREFIX (line, "Status: ")) {
364       installed_flag = strstr (&line[8], "installed") != NULL;
365     }
366     else if (STRPREFIX (line, "Version: ")) {
367       free (version);
368       free (release);
369       char *p = strchr (&line[9], '-');
370       if (p) {
371         *p = '\0';
372         version = safe_strdup (g, &line[9]);
373         release = safe_strdup (g, p+1);
374       } else {
375         version = safe_strdup (g, &line[9]);
376         release = NULL;
377       }
378     }
379     else if (STREQ (line, "")) {
380       if (installed_flag && name && version)
381         add_application (g, apps, name, "", 0, version, release ? : "",
382                          "", "", "", "");
383       free (name);
384       free (version);
385       free (release);
386       name = version = release = NULL;
387       installed_flag = 0;
388     }
389   }
390
391   if (fclose (fp) == -1) {
392     perrorf (g, "fclose: %s", status);
393     goto out;
394   }
395   fp = NULL;
396
397   ret = apps;
398
399  out:
400   if (ret == NULL && apps != NULL)
401     guestfs_free_application_list (apps);
402   if (fp)
403     fclose (fp);
404   free (name);
405   free (version);
406   free (release);
407   free (status);
408   return ret;
409 }
410
411 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);
412
413 static struct guestfs_application_list *
414 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
415 {
416   size_t len = strlen (fs->windows_systemroot) + 64;
417   char software[len];
418   snprintf (software, len, "%s/system32/config/software",
419             fs->windows_systemroot);
420
421   char *software_path = guestfs___case_sensitive_path_silently (g, software);
422   if (!software_path) {
423     /* Missing software hive is a problem. */
424     error (g, "no HKLM\\SOFTWARE hive found in the guest");
425     return NULL;
426   }
427
428   char *software_hive = NULL;
429   struct guestfs_application_list *ret = NULL;
430   hive_h *h = NULL;
431
432   software_hive = guestfs___download_to_tmp (g, fs, software_path, "software",
433                                              MAX_REGISTRY_SIZE);
434   if (software_hive == NULL)
435     goto out;
436
437   free (software_path);
438   software_path = NULL;
439
440   h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
441   if (h == NULL) {
442     perrorf (g, "hivex_open");
443     goto out;
444   }
445
446   /* Allocate apps list. */
447   ret = safe_malloc (g, sizeof *ret);
448   ret->len = 0;
449   ret->val = NULL;
450
451   /* Ordinary native applications. */
452   const char *hivepath[] =
453     { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
454   list_applications_windows_from_path (g, h, ret, hivepath,
455                                        sizeof hivepath / sizeof hivepath[0]);
456
457   /* 32-bit emulated Windows apps running on the WOW64 emulator.
458    * http://support.microsoft.com/kb/896459 (RHBZ#692545).
459    */
460   const char *hivepath2[] =
461     { "WOW6432node", "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
462   list_applications_windows_from_path (g, h, ret, hivepath2,
463                                        sizeof hivepath2 / sizeof hivepath2[0]);
464
465  out:
466   if (h) hivex_close (h);
467   free (software_path);
468   free (software_hive);
469
470   return ret;
471 }
472
473 static void
474 list_applications_windows_from_path (guestfs_h *g, hive_h *h,
475                                      struct guestfs_application_list *apps,
476                                      const char **path, size_t path_len)
477 {
478   hive_node_h *children = NULL;
479   hive_node_h node;
480   size_t i;
481
482   node = hivex_root (h);
483
484   for (i = 0; node != 0 && i < path_len; ++i)
485     node = hivex_node_get_child (h, node, path[i]);
486
487   if (node == 0)
488     return;
489
490   children = hivex_node_children (h, node);
491   if (children == NULL)
492     return;
493
494   /* Consider any child node that has a DisplayName key.
495    * See also:
496    * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
497    */
498   for (i = 0; children[i] != 0; ++i) {
499     hive_value_h value;
500     char *name = NULL;
501     char *display_name = NULL;
502     char *version = NULL;
503     char *install_path = NULL;
504     char *publisher = NULL;
505     char *url = NULL;
506     char *comments = NULL;
507
508     /* Use the node name as a proxy for the package name in Linux.  The
509      * display name is not language-independent, so it cannot be used.
510      */
511     name = hivex_node_name (h, children[i]);
512     if (name == NULL)
513       continue;
514
515     value = hivex_node_get_value (h, children[i], "DisplayName");
516     if (value) {
517       display_name = hivex_value_string (h, value);
518       if (display_name) {
519         value = hivex_node_get_value (h, children[i], "DisplayVersion");
520         if (value)
521           version = hivex_value_string (h, value);
522         value = hivex_node_get_value (h, children[i], "InstallLocation");
523         if (value)
524           install_path = hivex_value_string (h, value);
525         value = hivex_node_get_value (h, children[i], "Publisher");
526         if (value)
527           publisher = hivex_value_string (h, value);
528         value = hivex_node_get_value (h, children[i], "URLInfoAbout");
529         if (value)
530           url = hivex_value_string (h, value);
531         value = hivex_node_get_value (h, children[i], "Comments");
532         if (value)
533           comments = hivex_value_string (h, value);
534
535         add_application (g, apps, name, display_name, 0,
536                          version ? : "",
537                          "",
538                          install_path ? : "",
539                          publisher ? : "",
540                          url ? : "",
541                          comments ? : "");
542       }
543     }
544
545     free (name);
546     free (display_name);
547     free (version);
548     free (install_path);
549     free (publisher);
550     free (url);
551     free (comments);
552   }
553
554   free (children);
555 }
556
557 static void
558 add_application (guestfs_h *g, struct guestfs_application_list *apps,
559                  const char *name, const char *display_name, int32_t epoch,
560                  const char *version, const char *release,
561                  const char *install_path,
562                  const char *publisher, const char *url,
563                  const char *description)
564 {
565   apps->len++;
566   apps->val = safe_realloc (g, apps->val,
567                             apps->len * sizeof (struct guestfs_application));
568   apps->val[apps->len-1].app_name = safe_strdup (g, name);
569   apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
570   apps->val[apps->len-1].app_epoch = epoch;
571   apps->val[apps->len-1].app_version = safe_strdup (g, version);
572   apps->val[apps->len-1].app_release = safe_strdup (g, release);
573   apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
574   /* XXX Translated path is not implemented yet. */
575   apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
576   apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
577   apps->val[apps->len-1].app_url = safe_strdup (g, url);
578   /* XXX The next two are not yet implemented for any package
579    * format, but we could easily support them for rpm and deb.
580    */
581   apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
582   apps->val[apps->len-1].app_summary = safe_strdup (g, "");
583   apps->val[apps->len-1].app_description = safe_strdup (g, description);
584 }
585
586 /* Sort applications by name before returning the list. */
587 static int
588 compare_applications (const void *vp1, const void *vp2)
589 {
590   const struct guestfs_application *v1 = vp1;
591   const struct guestfs_application *v2 = vp2;
592
593   return strcmp (v1->app_name, v2->app_name);
594 }
595
596 static void
597 sort_applications (struct guestfs_application_list *apps)
598 {
599   if (apps && apps->val)
600     qsort (apps->val, apps->len, sizeof (struct guestfs_application),
601            compare_applications);
602 }
603
604 #else /* no PCRE or hivex at compile time */
605
606 /* XXX These functions should be in an optgroup. */
607
608 #define NOT_IMPL(r)                                                     \
609   error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
610   return r
611
612 struct guestfs_application_list *
613 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
614 {
615   NOT_IMPL(NULL);
616 }
617
618 #endif /* no PCRE or hivex at compile time */