X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Finspect_apps.c;h=a41c46c46c682371606e60955a13bfbbb1c49848;hb=daf55c07978cd29fe6675911bf76da0b056fdae1;hp=df99df9fdddec7f39983b99457e76a5d060bb764;hpb=3336b5448f12c9eb25bf7a0cecda1315c4cf07ea;p=libguestfs.git diff --git a/src/inspect_apps.c b/src/inspect_apps.c index df99df9..a41c46c 100644 --- a/src/inspect_apps.c +++ b/src/inspect_apps.c @@ -126,107 +126,197 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root) } #ifdef DB_DUMP -static struct guestfs_application_list * -list_applications_rpm (guestfs_h *g, struct inspect_fs *fs) + +/* This data comes from the Name database, and contains the application + * names and the first 4 bytes of the link field. + */ +struct rpm_names_list { + struct rpm_name *names; + size_t len; +}; +struct rpm_name { + char *name; + char link[4]; +}; + +static void +free_rpm_names_list (struct rpm_names_list *list) { - const char *basename = "rpm_Name"; - char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2]; - snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s", - g->tmpdir, basename); + size_t i; - if (guestfs___download_to_tmp (g, "/var/lib/rpm/Name", basename, - MAX_PKG_DB_SIZE) == -1) - return NULL; + for (i = 0; i < list->len; ++i) + free (list->names[i].name); + free (list->names); +} - struct guestfs_application_list *apps = NULL, *ret = NULL; -#define cmd_len (strlen (tmpdir_basename) + 64) - char cmd[cmd_len]; - FILE *pp = NULL; - char line[1024]; +static int +compare_links (const void *av, const void *bv) +{ + const struct rpm_name *a = av; + const struct rpm_name *b = bv; + return memcmp (a->link, b->link, 4); +} + +static int +read_rpm_name (guestfs_h *g, + const unsigned char *key, size_t keylen, + const unsigned char *value, size_t valuelen, + void *listv) +{ + struct rpm_names_list *list = listv; + char *name; + + /* Ignore bogus entries. */ + if (keylen == 0 || valuelen < 4) + return 0; + + /* The name (key) field won't be NUL-terminated, so we must do that. */ + name = safe_malloc (g, keylen+1); + memcpy (name, key, keylen); + name[keylen] = '\0'; + + list->names = safe_realloc (g, list->names, + (list->len + 1) * sizeof (struct rpm_name)); + list->names[list->len].name = name; + memcpy (list->names[list->len].link, value, 4); + list->len++; + + return 0; +} + +struct read_package_data { + struct rpm_names_list *list; + struct guestfs_application_list *apps; +}; + +static int +read_package (guestfs_h *g, + const unsigned char *key, size_t keylen, + const unsigned char *value, size_t valuelen, + void *datav) +{ + struct read_package_data *data = datav; + struct rpm_name nkey, *entry; + char *p; size_t len; + ssize_t max; + char *nul_name_nul, *version, *release; + + /* This function reads one (key, value) pair from the Packages + * database. The key is the link field (see struct rpm_name). The + * value is a long binary string, but we can extract the version + * number from it as below. First we have to look up the link field + * in the list of links (which is sorted by link field). + */ - snprintf (cmd, cmd_len, DB_DUMP " -p '%s'", tmpdir_basename); + /* Ignore bogus entries. */ + if (keylen < 4 || valuelen == 0) + return 0; - debug (g, "list_applications_rpm: %s", cmd); + /* Look up the link (key) in the list. */ + memcpy (nkey.link, key, 4); + entry = bsearch (&nkey, data->list->names, data->list->len, + sizeof (struct rpm_name), compare_links); + if (!entry) + return 0; /* Not found - ignore it. */ + + /* We found a matching link entry, so that gives us the application + * name (entry->name). Now we can get other data for this + * application out of the binary value string. XXX This is a real + * hack. + */ - pp = popen (cmd, "r"); - if (pp == NULL) { - perrorf (g, "popen: %s", cmd); - goto out; - } + /* Look for \0\0 */ + len = strlen (entry->name); + nul_name_nul = safe_malloc (g, len + 2); + nul_name_nul[0] = '\0'; + memcpy (&nul_name_nul[1], entry->name, len); + nul_name_nul[len+1] = '\0'; + p = memmem (value, valuelen, nul_name_nul, len+2); + free (nul_name_nul); + if (!p) + return 0; - /* Ignore everything to end-of-header marker. */ - for (;;) { - if (fgets (line, sizeof line, pp) == NULL) { - error (g, _("unexpected end of output from db_dump command")); - goto out; - } + /* Following that are \0-delimited version and release fields. */ + p += len + 2; /* Note we have to skip \0 + name + \0. */ + max = valuelen - (p - (char *) value); + if (max < 0) + max = 0; + version = safe_strndup (g, p, max); - len = strlen (line); - if (len > 0 && line[len-1] == '\n') { - line[len-1] = '\0'; - len--; - } + len = strlen (version); + p += len + 1; + max = valuelen - (p - (char *) value); + if (max < 0) + max = 0; + release = safe_strndup (g, p, max); - if (STREQ (line, "HEADER=END")) - break; - } + /* Add the application and what we know. */ + add_application (g, data->apps, entry->name, "", 0, version, release, + "", "", "", ""); - /* Allocate 'apps' list. */ - apps = safe_malloc (g, sizeof *apps); - apps->len = 0; - apps->val = NULL; + free (version); + free (release); - /* Read alternate lines until end of data marker. */ - for (;;) { - if (fgets (line, sizeof line, pp) == NULL) { - error (g, _("unexpected end of output from db_dump command")); - goto out; - } + return 0; +} - len = strlen (line); - if (len > 0 && line[len-1] == '\n') { - line[len-1] = '\0'; - len--; - } +static struct guestfs_application_list * +list_applications_rpm (guestfs_h *g, struct inspect_fs *fs) +{ + const char *name = "rpm_Name"; + char tmpdir_name[strlen (g->tmpdir) + strlen (name) + 2]; + snprintf (tmpdir_name, sizeof tmpdir_name, "%s/%s", + g->tmpdir, name); - if (STREQ (line, "DATA=END")) - break; + if (guestfs___download_to_tmp (g, "/var/lib/rpm/Name", name, + MAX_PKG_DB_SIZE) == -1) + return NULL; - char *p = line; - if (len > 0 && line[0] == ' ') - p = line+1; - /* Ignore any application name that contains non-printable chars. - * In the db_dump output these would be escaped with backslash, so - * we can just ignore any such line. - */ - if (strchr (p, '\\') == NULL) - add_application (g, apps, p, "", 0, "", "", "", "", "", ""); + const char *pkgs = "rpm_Packages"; + char tmpdir_pkgs[strlen (g->tmpdir) + strlen (pkgs) + 2]; + snprintf (tmpdir_pkgs, sizeof tmpdir_pkgs, "%s/%s", + g->tmpdir, pkgs); - /* Discard next line. */ - if (fgets (line, sizeof line, pp) == NULL) { - error (g, _("unexpected end of output from db_dump command")); - goto out; - } - } + if (guestfs___download_to_tmp (g, "/var/lib/rpm/Packages", pkgs, + MAX_PKG_DB_SIZE) == -1) + return NULL; - /* Catch errors from the db_dump command. */ - if (pclose (pp) == -1) { - perrorf (g, "pclose: %s", cmd); - goto out; + /* Allocate interim structure to store names and links. */ + struct rpm_names_list list; + list.names = NULL; + list.len = 0; + + /* Read Name database. */ + if (guestfs___read_db_dump (g, tmpdir_name, &list, read_rpm_name) == -1) { + free_rpm_names_list (&list); + return NULL; } - pp = NULL; - ret = apps; + /* Sort the names by link field for fast searching. */ + qsort (list.names, list.len, sizeof (struct rpm_name), compare_links); - out: - if (ret == NULL && apps != NULL) + /* Allocate 'apps' list. */ + struct guestfs_application_list *apps; + apps = safe_malloc (g, sizeof *apps); + apps->len = 0; + apps->val = NULL; + + /* Read Packages database. */ + struct read_package_data data; + data.list = &list; + data.apps = apps; + if (guestfs___read_db_dump (g, tmpdir_pkgs, &data, read_package) == -1) { + free_rpm_names_list (&list); guestfs_free_application_list (apps); - if (pp) - pclose (pp); + return NULL; + } - return ret; + free_rpm_names_list (&list); + + return apps; } + #endif /* defined DB_DUMP */ static struct guestfs_application_list * @@ -340,11 +430,11 @@ list_applications_windows (guestfs_h *g, struct inspect_fs *fs) fs->windows_systemroot); char *software_path = guestfs___case_sensitive_path_silently (g, software); - if (!software_path) - /* If the software hive doesn't exist, just accept that we cannot - * list windows apps. - */ - return 0; + if (!software_path) { + /* Missing software hive is a problem. */ + error (g, "no HKLM\\SOFTWARE hive found in the guest"); + return NULL; + } struct guestfs_application_list *ret = NULL; hive_h *h = NULL;