From 04a6dfebb6b52532a1e0bd637899f1eba14e94c6 Mon Sep 17 00:00:00 2001 From: aurel32 Date: Fri, 30 Jan 2009 19:59:17 +0000 Subject: [PATCH] linux-user: Add generic env variable handling Adds support for qemu to modify target process environment variables using -E and -U commandline switches. This replaces eventually the -drop-ld-preload flag. Signed-off-by: Aurelien Jarno Signed-off-by: Riku Voipio git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6484 c046a42c-6fe2-441c-8c8c-71466251a162 --- 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 a091ce9..372d185 100644 --- a/Makefile.target +++ b/Makefile.target @@ -320,7 +320,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 732524f..2841120 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" static const char *interp_prefix = CONFIG_QEMU_PREFIX; @@ -2186,6 +2189,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" @@ -2195,6 +2200,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, @@ -2229,8 +2240,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(); @@ -2240,6 +2251,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(;;) { @@ -2269,6 +2290,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); @@ -2301,7 +2330,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 @@ -2369,19 +2398,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.7.9.5