[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