From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LOw50-000733-II for qemu-devel@nongnu.org; Mon, 19 Jan 2009 10:30:22 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LOw4z-00072c-Q6 for qemu-devel@nongnu.org; Mon, 19 Jan 2009 10:30:22 -0500 Received: from [199.232.76.173] (port=54934 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LOw4z-00072X-IK for qemu-devel@nongnu.org; Mon, 19 Jan 2009 10:30:21 -0500 Received: from [84.20.150.76] (port=44557 helo=narury.org) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LOw4y-0006Ze-P8 for qemu-devel@nongnu.org; Mon, 19 Jan 2009 10:30:21 -0500 Received: from kos.to (localhost.localdomain [127.0.0.1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by narury.org (Postfix) with ESMTP id 5180C3274001 for ; Mon, 19 Jan 2009 17:30:15 +0200 (EET) Date: Mon, 19 Jan 2009 17:30:15 +0200 From: Riku Voipio Message-ID: <20090119153015.GA21034@kos.to> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Subject: [Qemu-devel] [PATCH] linux-user: Add generic env variable handling Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Adds support for qemu to modify target process environment variables using -E and -U commandline switches. This replaces eventually the -drop-ld-preload flag. From: Mika Westerberg Signed-off-by: Riku Voipio --- Makefile.target | 2 +- linux-user/envlist.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++ linux-user/envlist.h | 22 +++++ linux-user/main.c | 50 +++++++--- 4 files changed, 304 insertions(+), 17 deletions(-) create mode 100644 linux-user/envlist.c create mode 100644 linux-user/envlist.h diff --git a/Makefile.target b/Makefile.target index fcaf4ec..a736343 100644 --- a/Makefile.target +++ b/Makefile.target @@ -373,7 +373,7 @@ CFLAGS+=-p endif OBJS= main.o syscall.o strace.o mmap.o signal.o path.o thunk.o \ - elfload.o linuxload.o uaccess.o + elfload.o linuxload.o uaccess.o envlist.o LIBS+= $(AIOLIBS) ifdef TARGET_HAS_BFLT OBJS+= flatload.o diff --git a/linux-user/envlist.c b/linux-user/envlist.c new file mode 100644 index 0000000..e13c2d3 --- /dev/null +++ b/linux-user/envlist.c @@ -0,0 +1,247 @@ +#include + +#include +#include +#include +#include +#include + +#include "envlist.h" + +struct envlist_entry { + const char *ev_var; /* actual env value */ + LIST_ENTRY(envlist_entry) ev_link; +}; + +struct envlist { + LIST_HEAD(, envlist_entry) el_entries; /* actual entries */ + size_t el_count; /* number of entries */ +}; + +static int envlist_parse(envlist_t *envlist, + const char *env, int (*)(envlist_t *, const char *)); + +/* + * Allocates new envlist and returns pointer to that or + * NULL in case of error. + */ +envlist_t * +envlist_create(void) +{ + envlist_t *envlist; + + if ((envlist = malloc(sizeof (*envlist))) == NULL) + return (NULL); + + LIST_INIT(&envlist->el_entries); + envlist->el_count = 0; + + return (envlist); +} + +/* + * Releases given envlist and its entries. + */ +void +envlist_free(envlist_t *envlist) +{ + struct envlist_entry *entry; + + assert(envlist != NULL); + + while (envlist->el_entries.lh_first != NULL) { + entry = envlist->el_entries.lh_first; + LIST_REMOVE(entry, ev_link); + + free((char *)entry->ev_var); + free(entry); + } + free(envlist); +} + +/* + * Parses comma separated list of set/modify environment + * variable entries and updates given enlist accordingly. + * + * For example: + * envlist_parse(el, "HOME=foo,SHELL=/bin/sh"); + * + * inserts/sets environment variables HOME and SHELL. + * + * Returns 0 on success, errno otherwise. + */ +int +envlist_parse_set(envlist_t *envlist, const char *env) +{ + return (envlist_parse(envlist, env, &envlist_setenv)); +} + +/* + * Parses comma separated list of unset environment variable + * entries and removes given variables from given envlist. + * + * Returns 0 on success, errno otherwise. + */ +int +envlist_parse_unset(envlist_t *envlist, const char *env) +{ + return (envlist_parse(envlist, env, &envlist_unsetenv)); +} + +/* + * Parses comma separated list of set, modify or unset entries + * and calls given callback for each entry. + * + * Returns 0 in case of success, errno otherwise. + */ +static int +envlist_parse(envlist_t *envlist, const char *env, + int (*callback)(envlist_t *, const char *)) +{ + char *tmpenv, *envvar; + char *envsave = NULL; + + assert(callback != NULL); + + if ((envlist == NULL) || (env == NULL)) + return (EINVAL); + + /* + * We need to make temporary copy of the env string + * as strtok_r(3) modifies it while it tokenizes. + */ + if ((tmpenv = strdup(env)) == NULL) + return (errno); + + envvar = strtok_r(tmpenv, ",", &envsave); + while (envvar != NULL) { + if ((*callback)(envlist, envvar) != 0) { + free(tmpenv); + return (errno); + } + envvar = strtok_r(NULL, ",", &envsave); + } + + free(tmpenv); + return (0); +} + +/* + * Sets environment value to envlist in similar manner + * than putenv(3). + * + * Returns 0 in success, errno otherwise. + */ +int +envlist_setenv(envlist_t *envlist, const char *env) +{ + struct envlist_entry *entry = NULL; + const char *eq_sign; + size_t envname_len; + + if ((envlist == NULL) || (env == NULL)) + return (EINVAL); + + /* find out first equals sign in given env */ + if ((eq_sign = strchr(env, '=')) == NULL) + return (EINVAL); + envname_len = eq_sign - env + 1; + + /* + * If there already exists variable with given name + * we remove and release it before allocating a whole + * new entry. + */ + for (entry = envlist->el_entries.lh_first; entry != NULL; + entry = entry->ev_link.le_next) { + if (strncmp(entry->ev_var, env, envname_len) == 0) + break; + } + + if (entry != NULL) { + LIST_REMOVE(entry, ev_link); + free((char *)entry->ev_var); + free(entry); + } else { + envlist->el_count++; + } + + if ((entry = malloc(sizeof (*entry))) == NULL) + return (errno); + if ((entry->ev_var = strdup(env)) == NULL) { + free(entry); + return (errno); + } + LIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link); + + return (0); +} + +/* + * Removes given env value from envlist in similar manner + * than unsetenv(3). Returns 0 in success, errno otherwise. + */ +int +envlist_unsetenv(envlist_t *envlist, const char *env) +{ + struct envlist_entry *entry; + size_t envname_len; + + if ((envlist == NULL) || (env == NULL)) + return (EINVAL); + + /* env is not allowed to contain '=' */ + if (strchr(env, '=') != NULL) + return (EINVAL); + + /* + * Find out the requested entry and remove + * it from the list. + */ + envname_len = strlen(env); + for (entry = envlist->el_entries.lh_first; entry != NULL; + entry = entry->ev_link.le_next) { + if (strncmp(entry->ev_var, env, envname_len) == 0) + break; + } + if (entry != NULL) { + LIST_REMOVE(entry, ev_link); + free((char *)entry->ev_var); + free(entry); + + envlist->el_count--; + } + return (0); +} + +/* + * Returns given envlist as array of strings (in same form that + * global variable environ is). Caller must free returned memory + * by calling free(3) for each element and for the array. Returned + * array and given envlist are not related (no common references). + * + * If caller provides count pointer, number of items in array is + * stored there. In case of error, NULL is returned and no memory + * is allocated. + */ +char ** +envlist_to_environ(const envlist_t *envlist, size_t *count) +{ + struct envlist_entry *entry; + char **env, **penv; + + penv = env = malloc((envlist->el_count + 1) * sizeof (char *)); + if (env == NULL) + return (NULL); + + for (entry = envlist->el_entries.lh_first; entry != NULL; + entry = entry->ev_link.le_next) { + *(penv++) = strdup(entry->ev_var); + } + *penv = NULL; /* NULL terminate the list */ + + if (count != NULL) + *count = envlist->el_count; + + return (env); +} diff --git a/linux-user/envlist.h b/linux-user/envlist.h new file mode 100644 index 0000000..e76d4a1 --- /dev/null +++ b/linux-user/envlist.h @@ -0,0 +1,22 @@ +#ifndef ENVLIST_H +#define ENVLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct envlist envlist_t; + +extern envlist_t *envlist_create(void); +extern void envlist_free(envlist_t *); +extern int envlist_setenv(envlist_t *, const char *); +extern int envlist_unsetenv(envlist_t *, const char *); +extern int envlist_parse_set(envlist_t *, const char *); +extern int envlist_parse_unset(envlist_t *, const char *); +extern char **envlist_to_environ(const envlist_t *, size_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* ENVLIST_H */ diff --git a/linux-user/main.c b/linux-user/main.c index 2ffe244..21a0626 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -32,6 +32,9 @@ /* For tb_lock */ #include "exec-all.h" + +#include "envlist.h" + #define DEBUG_LOGFILE "/tmp/qemu.log" char *exec_path; @@ -2190,6 +2193,8 @@ static void usage(void) "-s size set the stack size in bytes (default=%ld)\n" "-cpu model select CPU (-cpu ? for list)\n" "-drop-ld-preload drop LD_PRELOAD for target process\n" + "-E var=value sets/modifies targets environment variable(s)\n" + "-U var unsets targets environment variable(s)\n" "\n" "Debug options:\n" "-d options activate log (logfile=%s)\n" @@ -2199,6 +2204,12 @@ static void usage(void) "Environment variables:\n" "QEMU_STRACE Print system calls and arguments similar to the\n" " 'strace' program. Enable by setting to any value.\n" + "You can use -E and -U options to set/unset environment variables\n" + "for target process. It is possible to provide several variables\n" + "by repeating the option. For example:\n" + " -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n" + "Note that if you provide several changes to single variable\n" + "last change will stay in effect.\n" , TARGET_ARCH, interp_prefix, @@ -2233,8 +2244,8 @@ int main(int argc, char **argv, char **envp) int optind; const char *r; int gdbstub_port = 0; - int drop_ld_preload = 0, environ_count = 0; - char **target_environ, **wrk, **dst; + char **target_environ, **wrk; + envlist_t *envlist = NULL; if (argc <= 1) usage(); @@ -2244,6 +2255,16 @@ int main(int argc, char **argv, char **envp) /* init debug */ cpu_set_log_filename(DEBUG_LOGFILE); + if ((envlist = envlist_create()) == NULL) { + (void) fprintf(stderr, "Unable to allocate envlist\n"); + exit(1); + } + + /* add current environment into the list */ + for (wrk = environ; *wrk != NULL; wrk++) { + (void) envlist_setenv(envlist, *wrk); + } + cpu_model = NULL; optind = 1; for(;;) { @@ -2273,6 +2294,14 @@ int main(int argc, char **argv, char **envp) _exit(1); } cpu_set_log(mask); + } else if (!strcmp(r, "E")) { + r = argv[optind++]; + if (envlist_setenv(envlist, r) != 0) + usage(); + } else if (!strcmp(r, "U")) { + r = argv[optind++]; + if (envlist_unsetenv(envlist, r) != 0) + usage(); } else if (!strcmp(r, "s")) { r = argv[optind++]; x86_stack_size = strtol(r, (char **)&r, 0); @@ -2305,7 +2334,7 @@ int main(int argc, char **argv, char **envp) _exit(1); } } else if (!strcmp(r, "drop-ld-preload")) { - drop_ld_preload = 1; + (void) envlist_unsetenv(envlist, "LD_PRELOAD"); } else if (!strcmp(r, "strace")) { do_strace = 1; } else @@ -2374,19 +2403,8 @@ int main(int argc, char **argv, char **envp) do_strace = 1; } - wrk = environ; - while (*(wrk++)) - environ_count++; - - target_environ = malloc((environ_count + 1) * sizeof(char *)); - if (!target_environ) - abort(); - for (wrk = environ, dst = target_environ; *wrk; wrk++) { - if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11)) - continue; - *(dst++) = strdup(*wrk); - } - *dst = NULL; /* NULL terminate target_environ */ + target_environ = envlist_to_environ(envlist, NULL); + envlist_free(envlist); if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { printf("Error loading %s\n", filename); -- 1.5.6.5 -- "rm -rf" only sounds scary if you don't have backups