summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2010-11-16 12:57:36 +0000
committerRichard W.M. Jones <rjones@redhat.com>2010-11-16 13:27:01 +0000
commitfab75c0337d7897d10ea5e95e47ac05eab81ace9 (patch)
treea1fee811daabdcbc98489dcbb47c4b6265a1d12a
parent35afe0cb33c986bf595585a716ff259cf3415a1f (diff)
downloadlibguestfs-fab75c0337d7897d10ea5e95e47ac05eab81ace9.tar.gz
libguestfs-fab75c0337d7897d10ea5e95e47ac05eab81ace9.tar.xz
libguestfs-fab75c0337d7897d10ea5e95e47ac05eab81ace9.zip
New API: inspect-get-hostname to return the hostname of the guest.
This returns the hostname of the guest. Tested on RHEL, Fedora, Debian 5, Ubuntu 10.10, FreeBSD 8, Windows 7.
-rw-r--r--generator/generator_actions.ml15
-rw-r--r--src/guestfs-internal.h1
-rw-r--r--src/guestfs.pod8
-rw-r--r--src/inspect.c241
4 files changed, 262 insertions, 3 deletions
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index 34a82bda..b4099388 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -1276,6 +1276,21 @@ If unavailable this is returned as an empty string C<\"\">.
Please read L<guestfs(3)/INSPECTION> for more details.");
+ ("inspect_get_hostname", (RString "hostname", [Device "root"], []), -1, [],
+ [],
+ "get hostname of the operating system",
+ "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This function returns the hostname of the operating system
+as found by inspection of the guest's configuration files.
+
+If the hostname could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
]
(* daemon_functions are any functions which cause some action
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index abcd4567..e3c5b1f0 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -219,6 +219,7 @@ struct inspect_fs {
int major_version;
int minor_version;
char *arch;
+ char *hostname;
char *windows_systemroot;
struct inspect_fstab_entry *fstab;
size_t nr_fstab;
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 83709827..fc58f4f1 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -1095,6 +1095,14 @@ data. Callers should be careful to escape these before printing them
to a structured file (for example, use HTML escaping if creating a web
page).
+Guest configuration may be altered in unusual ways by the
+administrator of the virtual machine, and may not reflect reality
+(particularly for untrusted or actively malicious guests). For
+example we parse the hostname from configuration files like
+C</etc/sysconfig/network> that we find in the guest, but the guest
+administrator can easily manipulate these files to provide the wrong
+hostname.
+
The inspection API parses guest configuration using two external
libraries: Augeas (Linux configuration) and hivex (Windows Registry).
Both are designed to be robust in the face of malicious data, although
diff --git a/src/inspect.c b/src/inspect.c
index 2006bbd7..baf4b3a0 100644
--- a/src/inspect.c
+++ b/src/inspect.c
@@ -195,10 +195,14 @@ 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_freebsd_root (guestfs_h *g, struct inspect_fs *fs);
static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
+static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs);
+static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs);
+static int check_hostname_freebsd (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);
-static int check_windows_registry (guestfs_h *g, struct inspect_fs *fs);
+static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs);
+static int check_windows_system_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);
@@ -594,6 +598,10 @@ check_linux_root (guestfs_h *g, struct inspect_fs *fs)
if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
return -1;
+ /* Determine hostname. */
+ if (check_hostname_unix (g, fs) == -1)
+ return -1;
+
return 0;
}
@@ -625,6 +633,10 @@ check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
if (inspect_with_augeas (g, fs, "/etc/fstab", check_fstab) == -1)
return -1;
+ /* Determine hostname. */
+ if (check_hostname_unix (g, fs) == -1)
+ return -1;
+
return 0;
}
@@ -654,6 +666,124 @@ check_architecture (guestfs_h *g, struct inspect_fs *fs)
}
}
+/* Try several methods to determine the hostname from a Linux or
+ * FreeBSD guest. Note that type and distro have been set, so we can
+ * use that information to direct the search.
+ */
+static int
+check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
+{
+ char **lines;
+
+ switch (fs->type) {
+ case OS_TYPE_LINUX:
+ /* Red Hat-derived would be in /etc/sysconfig/network, and
+ * Debian-derived in the file /etc/hostname. Very old Debian and
+ * SUSE use /etc/HOSTNAME. It's best to just look for each of
+ * these files in turn, rather than try anything clever based on
+ * distro.
+ */
+ if (guestfs_is_file (g, "/etc/HOSTNAME")) {
+ fs->hostname = first_line_of_file (g, "/etc/HOSTNAME");
+ if (fs->hostname == NULL)
+ return -1;
+ }
+ else if (guestfs_is_file (g, "/etc/hostname")) {
+ fs->hostname = first_line_of_file (g, "/etc/hostname");
+ if (fs->hostname == NULL)
+ return -1;
+ }
+ else if (guestfs_is_file (g, "/etc/sysconfig/network")) {
+ if (inspect_with_augeas (g, fs, "/etc/sysconfig/network",
+ check_hostname_redhat) == -1)
+ return -1;
+ }
+ break;
+
+ case OS_TYPE_FREEBSD:
+ /* /etc/rc.conf contains the hostname, but there is no Augeas lens
+ * for this file.
+ */
+ if (guestfs_is_file (g, "/etc/rc.conf")) {
+ if (check_hostname_freebsd (g, fs) == -1)
+ return -1;
+ }
+ break;
+
+ case OS_TYPE_WINDOWS: /* not here, see check_windows_system_registry */
+ case OS_TYPE_UNKNOWN:
+ default:
+ /* nothing, keep GCC warnings happy */;
+ }
+
+ return 0;
+}
+
+/* Parse the hostname from /etc/sysconfig/network. This must be called
+ * from the inspect_with_augeas wrapper.
+ */
+static int
+check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs)
+{
+ char *hostname;
+
+ hostname = guestfs_aug_get (g, "/files/etc/sysconfig/network/HOSTNAME");
+ if (!hostname)
+ return -1;
+
+ fs->hostname = hostname; /* freed by guestfs___free_inspect_info */
+ return 0;
+}
+
+/* Parse the hostname from /etc/rc.conf. On FreeBSD this file
+ * contains comments, blank lines and:
+ * hostname="freebsd8.example.com"
+ * ifconfig_re0="DHCP"
+ * keymap="uk.iso"
+ * sshd_enable="YES"
+ */
+static int
+check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs)
+{
+ const char *filename = "/etc/rc.conf";
+ int64_t size;
+ char **lines;
+ size_t i;
+
+ /* Don't trust guestfs_read_lines not to break with very large files.
+ * Check the file size is something reasonable first.
+ */
+ size = guestfs_filesize (g, filename);
+ if (size == -1)
+ /* guestfs_filesize failed and has already set error in handle */
+ return -1;
+ if (size > 1000000) {
+ error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
+ filename, size);
+ return -1;
+ }
+
+ lines = guestfs_read_lines (g, filename);
+ if (lines == NULL)
+ return -1;
+
+ for (i = 0; lines[i] != NULL; ++i) {
+ if (STRPREFIX (lines[i], "hostname=\"") ||
+ STRPREFIX (lines[i], "hostname='")) {
+ size_t len = strlen (lines[i]) - 10 - 1;
+ fs->hostname = safe_strndup (g, &lines[i][10], len);
+ break;
+ } else if (STRPREFIX (lines[i], "hostname=")) {
+ size_t len = strlen (lines[i]) - 9;
+ fs->hostname = safe_strndup (g, &lines[i][9], len);
+ break;
+ }
+ }
+
+ guestfs___free_string_list (lines);
+ return 0;
+}
+
static int
check_fstab (guestfs_h *g, struct inspect_fs *fs)
{
@@ -891,12 +1021,17 @@ check_windows_root (guestfs_h *g, struct inspect_fs *fs)
if (check_windows_arch (g, fs) == -1)
return -1;
- if (check_windows_registry (g, fs) == -1)
+ /* Product name and version. */
+ if (check_windows_software_registry (g, fs) == -1)
return -1;
check_package_format (g, fs);
check_package_management (g, fs);
+ /* Hostname. */
+ if (check_windows_system_registry (g, fs) == -1)
+ return -1;
+
return 0;
}
@@ -925,7 +1060,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)
+check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
{
TMP_TEMPLATE_ON_STACK (software_local);
@@ -1032,6 +1167,90 @@ check_windows_registry (guestfs_h *g, struct inspect_fs *fs)
return ret;
}
+static int
+check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
+{
+ TMP_TEMPLATE_ON_STACK (system_local);
+
+ size_t len = strlen (fs->windows_systemroot) + 64;
+ char system[len];
+ snprintf (system, len, "%s/system32/config/system",
+ fs->windows_systemroot);
+
+ char *system_path = resolve_windows_path_silently (g, system);
+ if (!system_path)
+ /* If the system hive doesn't exist, just accept that we cannot
+ * find hostname etc.
+ */
+ return 0;
+
+ int ret = -1;
+ hive_h *h = NULL;
+ hive_value_h *values = NULL;
+
+ if (download_to_tmp (g, system_path, system_local, 100000000) == -1)
+ goto out;
+
+ h = hivex_open (system_local, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
+ if (h == NULL) {
+ perrorf (g, "hivex_open");
+ goto out;
+ }
+
+ hive_node_h node = hivex_root (h);
+ /* XXX Don't hard-code ControlSet001. The current control set would
+ * be another good thing to expose up through the inspection API.
+ */
+ const char *hivepath[] =
+ { "ControlSet001", "Services", "Tcpip", "Parameters" };
+ size_t i;
+ for (i = 0;
+ node != 0 && i < sizeof hivepath / sizeof hivepath[0];
+ ++i) {
+ node = hivex_node_get_child (h, node, hivepath[i]);
+ }
+
+ if (node == 0) {
+ perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters");
+ goto out;
+ }
+
+ values = hivex_node_values (h, node);
+
+ for (i = 0; values[i] != 0; ++i) {
+ char *key = hivex_value_key (h, values[i]);
+ if (key == NULL) {
+ perrorf (g, "hivex_value_key");
+ goto out;
+ }
+
+ if (STRCASEEQ (key, "Hostname")) {
+ fs->hostname = hivex_value_string (h, values[i]);
+ if (!fs->hostname) {
+ perrorf (g, "hivex_value_string");
+ free (key);
+ goto out;
+ }
+ }
+ /* many other interesting fields here ... */
+
+ free (key);
+ }
+
+ ret = 0;
+
+ out:
+ if (h) hivex_close (h);
+ free (values);
+ free (system_path);
+
+ /* Free up the temporary file. */
+ unlink (system_local);
+#undef system_local_len
+
+ return ret;
+}
+
static char *
resolve_windows_path_silently (guestfs_h *g, const char *path)
{
@@ -1432,6 +1651,16 @@ guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
return ret;
}
+char *
+guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
+{
+ struct inspect_fs *fs = search_for_root (g, root);
+ if (!fs)
+ return NULL;
+
+ return safe_strdup (g, fs->hostname ? : "unknown");
+}
+
static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
@@ -2095,6 +2324,12 @@ guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
NOT_IMPL(NULL);
}
+char *
+guestfs__inspect_get_hostname (guestfs_h *g, const char *root)
+{
+ NOT_IMPL(NULL);
+}
+
struct guestfs_application_list *
guestfs__inspect_list_applications (guestfs_h *g, const char *root)
{