qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC PATCH 2/2] qga: add --getenv option to get env. vars f


From: Gabriel L. Somlo
Subject: [Qemu-devel] [RFC PATCH 2/2] qga: add --getenv option to get env. vars from fw_cfg
Date: Tue, 24 Feb 2015 14:43:07 -0500

The new "-g" (or "--getenv") command line option causes qemu-ga to extract
and parse the "etc/guestenv" blob from fw_cfg, and return the value of
the requested key (if available) on stdout.

Warnings and error messages are printed to stderr, and only the actual
value portion of a "key=value" string matching the "--getenv key" argument
will be printed to stdout. Strings are searched in reverse order to
implement a sort of "last dupe wins" policy -- but I expect to refine
things quite a bit after receiving some feedback.

I've added a flag to qemu-ga instead of creating a completely separate
binary, but I don't feel strongly about keeping it that way. I just need
something that would end up tightly integrated with the "qemu guest tools"
package...

I also haven't built this on cygwin yet, so it's only tested on Linux
and gcc 4.9.2 (which stubbornly optimizes away one of my "true" if branches
with -O2, if I let it, but that's something else I'm planning to have figured
out by the time the dust settles on this :)

Signed-off-by: Gabriel Somlo <address@hidden>
---
 qga/Makefile.objs      |   1 +
 qga/getenv.c           | 140 +++++++++++++++++++++++++++++++++++++++++++++++++
 qga/guest-agent-core.h |   2 +
 qga/main.c             |   7 ++-
 4 files changed, 149 insertions(+), 1 deletion(-)
 create mode 100644 qga/getenv.c

diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index 1c5986c..6392b93 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -4,5 +4,6 @@ qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o 
service-win32.o
 qga-obj-$(CONFIG_WIN32) += vss-win32.o
 qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
 qga-obj-y += qapi-generated/qga-qmp-marshal.o
+qga-obj-y += getenv.o
 
 qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/
diff --git a/qga/getenv.c b/qga/getenv.c
new file mode 100644
index 0000000..9fecb9b
--- /dev/null
+++ b/qga/getenv.c
@@ -0,0 +1,140 @@
+/*
+ * QEMU Guest Agent: host->guest environment variable retrieval
+ *
+ * Copyright Carnegie Mellon University 2015
+ *
+ * Author:
+ *  Gabriel L. Somlo  <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/io.h>
+#include "qemu/bswap.h"
+#include "hw/nvram/fw_cfg.h"
+#include "qga/guest-agent-core.h"
+
+#define PORT_FW_CFG_CTL       0x0510
+#define PORT_FW_CFG_DATA      0x0511
+
+#define GUESTENV_FW_CFG_FILE "etc/guestenv"
+
+static char *ge_raw;
+
+static struct {
+    char *key;
+    char *val;
+} *ge_var;
+static int ge_var_cnt;
+
+static void
+fw_cfg_select(uint16_t f)
+{
+    outw(f, PORT_FW_CFG_CTL);
+}
+
+static void
+fw_cfg_read(void *buf, int len)
+{
+    insb(PORT_FW_CFG_DATA, buf, len);
+}
+
+static void
+fw_cfg_read_entry(void *buf, int e, int len)
+{
+    fw_cfg_select(e);
+    fw_cfg_read(buf, len);
+}
+
+static int
+__attribute__((optimize("O0"))) //FIXME: gcc -O2 nukes "true" if branch 
below!!!
+fw_cfg_grab_guestenv(void)
+{
+    int i;
+    uint32_t count, len = 0;
+    uint16_t sel;
+    uint8_t sig[] = "QEMU";
+    FWCfgFile fcfile;
+
+    /* ensure access to the fw_cfg device */
+    if (ioperm(PORT_FW_CFG_CTL, 2, 1) != 0) {
+        perror("ioperm failed");
+        return EXIT_FAILURE;
+    }
+
+    /* verify presence of fw_cfg device */
+    fw_cfg_select(FW_CFG_SIGNATURE);
+    for (i = 0; i < sizeof(sig) - 1; i++) {
+        sig[i] = inb(PORT_FW_CFG_DATA);
+    }
+    if (memcmp(sig, "QEMU", sizeof(sig)) != 0) {
+        fprintf(stderr, "fw_cfg signature not found!\n");
+        return EXIT_FAILURE;
+    }
+
+    /* read number of fw_cfg entries, then scan for guestenv entry */
+    fw_cfg_read_entry(&count, FW_CFG_FILE_DIR, sizeof(count));
+    count = be32_to_cpu(count);
+    for (i = 0; i < count; i++) {
+        fw_cfg_read(&fcfile, sizeof(fcfile));
+        //FIXME: why does gcc -O2 optimize away the whole if {} block below?!?
+        if (!strcmp(fcfile.name, GUESTENV_FW_CFG_FILE)) {
+            len = be32_to_cpu(fcfile.size);
+            sel = be16_to_cpu(fcfile.select);
+            ge_raw = g_malloc(len);
+            fw_cfg_read_entry(ge_raw, sel, len);
+            break;
+        }
+    }
+
+    if (i == count) {
+        /* guestenv entry not present in fw_cfg */
+        fprintf(stderr, "File %s not found in fw_cfg!\n", 
GUESTENV_FW_CFG_FILE);
+        return EXIT_FAILURE;
+    }
+
+    /* guestenv entry (concatenation of null-terminated ascii strings)
+     * found and copied to ge_raw; Expected string format is "key=val",
+     * and we attempt to extract them into the ge_var table */
+    for (i = 0; i < len;) {
+        ge_var = g_realloc(ge_var, (ge_var_cnt + 1) * sizeof(ge_var[0]));
+        ge_var[ge_var_cnt].key = ge_raw + i;
+        i += strlen(ge_raw + i) + 1;
+        ge_var[ge_var_cnt].val = strchr(ge_var[ge_var_cnt].key, '=');
+        if (ge_var[ge_var_cnt].val != NULL) {
+            ge_var[ge_var_cnt].val[0] = '\0';
+            ge_var[ge_var_cnt].val++;
+            ge_var_cnt++;
+        } else {
+            fprintf(stderr, "warning: skipping non-assignment line: \"%s\"\n",
+                            ge_var[ge_var_cnt].key);
+        }
+    }
+    return 0;
+}
+
+
+int
+ga_getenv(const char *key)
+{
+    int i;
+
+    if (fw_cfg_grab_guestenv() != 0) {
+        return EXIT_FAILURE;
+    }
+
+    /* last value for key "wins" */
+    for (i = ge_var_cnt - 1; i >= 0; i--) {
+        if (strcmp(key, ge_var[i].key) == 0) {
+            printf("%s\n", ge_var[i].val);
+            return 0;
+        }
+    }
+
+    fprintf(stderr, "Key \"%s\" not found\n", key);
+    return EXIT_FAILURE;;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e92c6ab..ef54b45 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -41,3 +41,5 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp);
 #ifndef _WIN32
 void reopen_fd_to_null(int fd);
 #endif
+
+int ga_getenv(const char *key);
diff --git a/qga/main.c b/qga/main.c
index 9939a2b..9159244 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -215,6 +215,8 @@ static void usage(const char *cmd)
 #endif
 "  -b, --blacklist   comma-separated list of RPCs to disable (no spaces, 
\"?\"\n"
 "                    to list available RPCs)\n"
+"  -g, --getenv      display the value of a given guest environment variable\n"
+"                    (passed into the guest via the -guestenv \"name=value\" 
option\n"
 "  -h, --help        display this help and exit\n"
 "\n"
 "Report bugs to <address@hidden>\n"
@@ -923,7 +925,7 @@ static void ga_print_cmd(QmpCommand *cmd, void *opaque)
 
 int main(int argc, char **argv)
 {
-    const char *sopt = "hVvdm:p:l:f:F::b:s:t:";
+    const char *sopt = "hVvdm:p:l:f:F::b:s:t:g:";
     const char *method = NULL, *path = NULL;
     const char *log_filepath = NULL;
     const char *pid_filepath;
@@ -951,6 +953,7 @@ int main(int argc, char **argv)
         { "service", 1, NULL, 's' },
 #endif
         { "statedir", 1, NULL, 't' },
+        { "getenv", 1, NULL, 'g' },
         { NULL, 0, NULL, 0 }
     };
     int opt_ind = 0, ch, daemonize = 0, i, j, len;
@@ -1042,6 +1045,8 @@ int main(int argc, char **argv)
             }
             break;
 #endif
+        case 'g':
+            return ga_getenv(optarg);
         case 'h':
             usage(argv[0]);
             return 0;
-- 
2.1.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]