X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Finspect.c;h=33da1441c25bbafd826020ffef857c276aaae107;hp=3ffb2bd0218e4112e2781d399c8f0470918cfc8f;hb=bf0280bf589573c11529999a73e9ec642dea9d61;hpb=9160dee922ad0866a3f6245fb1e0ad0b2e90170c diff --git a/src/inspect.c b/src/inspect.c index 3ffb2bd..33da144 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -31,6 +31,7 @@ #include #include +#include "c-ctype.h" #include "ignore-value.h" #include "xstrtol.h" @@ -51,7 +52,7 @@ static pcre *re_fedora; static pcre *re_rhel_old; static pcre *re_rhel; static pcre *re_rhel_no_minor; -static pcre *re_debian; +static pcre *re_major_minor; static pcre *re_aug_seq; static pcre *re_xdev; static pcre *re_windows_version; @@ -84,7 +85,7 @@ compile_regexps (void) "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)\\.(\\d+)", 0); COMPILE (re_rhel_no_minor, "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+)", 0); - COMPILE (re_debian, "(\\d+)\\.(\\d+)", 0); + COMPILE (re_major_minor, "(\\d+)\\.(\\d+)", 0); COMPILE (re_aug_seq, "/\\d+$", 0); COMPILE (re_xdev, "^/dev/(?:h|s|v|xv)d([a-z]\\d*)$", 0); COMPILE (re_windows_version, "^(\\d+)\\.(\\d+)", 0); @@ -100,7 +101,7 @@ free_regexps (void) pcre_free (re_rhel_old); pcre_free (re_rhel); pcre_free (re_rhel_no_minor); - pcre_free (re_debian); + pcre_free (re_major_minor); pcre_free (re_aug_seq); pcre_free (re_xdev); pcre_free (re_windows_version); @@ -455,6 +456,7 @@ guestfs___free_inspect_info (guestfs_h *g) free (g->fses[i].device); free (g->fses[i].product_name); free (g->fses[i].arch); + free (g->fses[i].windows_systemroot); size_t j; for (j = 0; j < g->fses[i].nr_fstab; ++j) { free (g->fses[i].fstab[j].device); @@ -501,10 +503,8 @@ static int check_filesystem (guestfs_h *g, const char *device); static int check_linux_root (guestfs_h *g, struct inspect_fs *fs); static int check_fstab (guestfs_h *g, struct inspect_fs *fs); static int check_windows_root (guestfs_h *g, struct inspect_fs *fs); -static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs, - const char *systemroot); -static int check_windows_registry (guestfs_h *g, struct inspect_fs *fs, - const char *systemroot); +static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs); +static int check_windows_registry (guestfs_h *g, struct inspect_fs *fs); static char *resolve_windows_path_silently (guestfs_h *g, const char *); static int extend_fses (guestfs_h *g); static int parse_unsigned_int (guestfs_h *g, const char *str); @@ -622,6 +622,111 @@ check_filesystem (guestfs_h *g, const char *device) return 0; } +/* Set fs->product_name to the first line of the release file. */ +static int +parse_release_file (guestfs_h *g, struct inspect_fs *fs, + const char *release_filename) +{ + char **product_name = guestfs_head_n (g, 1, release_filename); + if (product_name == NULL) + return -1; + if (product_name[0] == NULL) { + error (g, "%s: file is empty", release_filename); + free_string_list (product_name); + return -1; + } + + /* Note that this string becomes owned by the handle and will + * be freed by guestfs___free_inspect_info. + */ + fs->product_name = product_name[0]; + free (product_name); + + return 0; +} + +/* Parse generic MAJOR.MINOR from the fs->product_name string. */ +static int +parse_major_minor (guestfs_h *g, struct inspect_fs *fs) +{ + char *major, *minor; + + if (match2 (g, fs->product_name, re_major_minor, &major, &minor)) { + fs->major_version = parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) { + free (minor); + return -1; + } + fs->minor_version = parse_unsigned_int (g, minor); + free (minor); + if (fs->minor_version == -1) + return -1; + } + return 0; +} + +/* Ubuntu has /etc/lsb-release containing: + * DISTRIB_ID=Ubuntu # Distro + * DISTRIB_RELEASE=10.04 # Version + * DISTRIB_CODENAME=lucid + * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name + * In theory other distros could have this LSB file, but none do. + */ +static int +parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) +{ + char **lines; + size_t i; + int r = 0; + + lines = guestfs_head_n (g, 10, "/etc/lsb-release"); + if (lines == NULL) + return -1; + + for (i = 0; lines[i] != NULL; ++i) { + if (fs->distro == 0 && + STREQ (lines[i], "DISTRIB_ID=Ubuntu")) { + fs->distro = OS_DISTRO_UBUNTU; + r = 1; + } + else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) { + char *major, *minor; + if (match2 (g, &lines[i][16], re_major_minor, &major, &minor)) { + fs->major_version = parse_unsigned_int (g, major); + free (major); + if (fs->major_version == -1) { + free (minor); + free_string_list (lines); + return -1; + } + fs->minor_version = parse_unsigned_int (g, minor); + free (minor); + if (fs->minor_version == -1) { + free_string_list (lines); + return -1; + } + } + } + else if (fs->product_name == NULL && + (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") || + STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) { + size_t len = strlen (lines[i]) - 21 - 1; + fs->product_name = safe_strndup (g, &lines[i][21], len); + r = 1; + } + else if (fs->product_name == NULL && + STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) { + size_t len = strlen (lines[i]) - 20; + fs->product_name = safe_strndup (g, &lines[i][20], len); + r = 1; + } + } + + free_string_list (lines); + return r; +} + /* The currently mounted device is known to be a Linux root. Try to * determine from this the distro, version, etc. Also parse * /etc/fstab to determine the arrangement of mountpoints and @@ -630,25 +735,23 @@ check_filesystem (guestfs_h *g, const char *device) static int check_linux_root (guestfs_h *g, struct inspect_fs *fs) { + int r; + fs->type = OS_TYPE_LINUX; + if (guestfs_exists (g, "/etc/lsb-release") > 0) { + r = parse_lsb_release (g, fs); + if (r == -1) /* error */ + return -1; + if (r == 1) /* ok - detected the release from this file */ + goto skip_release_checks; + } + if (guestfs_exists (g, "/etc/redhat-release") > 0) { fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */ - char **product_name = guestfs_head_n (g, 1, "/etc/redhat-release"); - if (product_name == NULL) - return -1; - if (product_name[0] == NULL) { - error (g, "/etc/redhat-release file is empty"); - free_string_list (product_name); + if (parse_release_file (g, fs, "/etc/redhat-release") == -1) return -1; - } - - /* Note that this string becomes owned by the handle and will - * be freed by guestfs___free_inspect_info. - */ - fs->product_name = product_name[0]; - free (product_name); char *major, *minor; if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) { @@ -684,36 +787,40 @@ check_linux_root (guestfs_h *g, struct inspect_fs *fs) else if (guestfs_exists (g, "/etc/debian_version") > 0) { fs->distro = OS_DISTRO_DEBIAN; - char **product_name = guestfs_head_n (g, 1, "/etc/debian_version"); - if (product_name == NULL) + if (parse_release_file (g, fs, "/etc/debian_version") == -1) return -1; - if (product_name[0] == NULL) { - error (g, "/etc/debian_version file is empty"); - free_string_list (product_name); + + if (parse_major_minor (g, fs) == -1) + return -1; + } + else if (guestfs_exists (g, "/etc/pardus-release") > 0) { + fs->distro = OS_DISTRO_PARDUS; + + if (parse_release_file (g, fs, "/etc/pardus-release") == -1) return -1; - } - /* Note that this string becomes owned by the handle and will - * be freed by guestfs___free_inspect_info. + if (parse_major_minor (g, fs) == -1) + return -1; + } + else if (guestfs_exists (g, "/etc/arch-release") > 0) { + fs->distro = OS_DISTRO_ARCHLINUX; + + /* /etc/arch-release file is empty and I can't see a way to + * determine the actual release or product string. */ - fs->product_name = product_name[0]; - free (product_name); + } + else if (guestfs_exists (g, "/etc/gentoo-release") > 0) { + fs->distro = OS_DISTRO_GENTOO; - char *major, *minor; - if (match2 (g, fs->product_name, re_debian, &major, &minor)) { - fs->major_version = parse_unsigned_int (g, major); - free (major); - if (fs->major_version == -1) { - free (minor); - return -1; - } - fs->minor_version = parse_unsigned_int (g, minor); - free (minor); - if (fs->minor_version == -1) - return -1; - } + if (parse_release_file (g, fs, "/etc/gentoo-release") == -1) + return -1; + + if (parse_major_minor (g, fs) == -1) + return -1; } + skip_release_checks:; + /* Determine the architecture. */ const char *binaries[] = { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" }; @@ -749,7 +856,7 @@ check_linux_root (guestfs_h *g, struct inspect_fs *fs) guestfs_aug_rm (g, "/augeas/load//incl[. != \"/etc/fstab\"]"); guestfs_aug_load (g); - int r = check_fstab (g, fs); + r = check_fstab (g, fs); guestfs_aug_close (g); if (r == -1) return -1; @@ -831,17 +938,24 @@ add_fstab_entry (guestfs_h *g, struct inspect_fs *fs, STREQ (mp, "/sys")) return 0; + /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. */ + if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) || + STREQ (spec, "/dev/floppy") || + STREQ (spec, "/dev/cdrom")) + return 0; + /* Resolve UUID= and LABEL= to the actual device. */ char *device = NULL; if (STRPREFIX (spec, "UUID=")) device = guestfs_findfs_uuid (g, &spec[5]); else if (STRPREFIX (spec, "LABEL=")) device = guestfs_findfs_label (g, &spec[6]); - /* Resolve guest block device names. */ - else if (spec[0] == '/') + /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */ + else if (STRPREFIX (spec, "/dev/")) + /* Resolve guest block device names. */ device = resolve_fstab_device (g, spec); - /* Also ignore pseudo-devices completely, like spec == "tmpfs". - * If we haven't resolved the device successfully by this point, + + /* If we haven't resolved the device successfully by this point, * we don't care, just ignore it. */ if (device == NULL) @@ -955,34 +1069,27 @@ check_windows_root (guestfs_h *g, struct inspect_fs *fs) return -1; } - /* XXX There is a case for exposing systemroot and many variables - * from the registry through the libguestfs API. - */ - if (g->verbose) fprintf (stderr, "windows %%SYSTEMROOT%% = %s", systemroot); - if (check_windows_arch (g, fs, systemroot) == -1) { - free (systemroot); + /* Freed by guestfs___free_inspect_info. */ + fs->windows_systemroot = systemroot; + + if (check_windows_arch (g, fs) == -1) return -1; - } - if (check_windows_registry (g, fs, systemroot) == -1) { - free (systemroot); + if (check_windows_registry (g, fs) == -1) return -1; - } - free (systemroot); return 0; } static int -check_windows_arch (guestfs_h *g, struct inspect_fs *fs, - const char *systemroot) +check_windows_arch (guestfs_h *g, struct inspect_fs *fs) { - size_t len = strlen (systemroot) + 32; + size_t len = strlen (fs->windows_systemroot) + 32; char cmd_exe[len]; - snprintf (cmd_exe, len, "%s/system32/cmd.exe", systemroot); + snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot); char *cmd_exe_path = resolve_windows_path_silently (g, cmd_exe); if (!cmd_exe_path) @@ -1002,8 +1109,7 @@ check_windows_arch (guestfs_h *g, struct inspect_fs *fs, * registry fields available to callers. */ static int -check_windows_registry (guestfs_h *g, struct inspect_fs *fs, - const char *systemroot) +check_windows_registry (guestfs_h *g, struct inspect_fs *fs) { TMP_TEMPLATE_ON_STACK (dir); #define dir_len (strlen (dir)) @@ -1012,9 +1118,10 @@ check_windows_registry (guestfs_h *g, struct inspect_fs *fs, #define cmd_len (dir_len + 16) char cmd[cmd_len]; - size_t len = strlen (systemroot) + 64; + size_t len = strlen (fs->windows_systemroot) + 64; char software[len]; - snprintf (software, len, "%s/system32/config/software", systemroot); + snprintf (software, len, "%s/system32/config/software", + fs->windows_systemroot); char *software_path = resolve_windows_path_silently (g, software); if (!software_path) @@ -1227,11 +1334,15 @@ guestfs__inspect_get_distro (guestfs_h *g, const char *root) char *ret; switch (fs->distro) { + case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break; case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break; case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break; + case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break; + case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break; case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break; case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break; case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break; + case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break; case OS_DISTRO_UNKNOWN: default: ret = safe_strdup (g, "unknown"); break; } @@ -1268,6 +1379,21 @@ guestfs__inspect_get_product_name (guestfs_h *g, const char *root) return safe_strdup (g, fs->product_name ? : "unknown"); } +char * +guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) +{ + struct inspect_fs *fs = search_for_root (g, root); + if (!fs) + return NULL; + + if (!fs->windows_systemroot) { + error (g, _("not a Windows guest, or systemroot could not be determined")); + return NULL; + } + + return safe_strdup (g, fs->windows_systemroot); +} + char ** guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) {