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 <aurelien@aurel32.net> Signed-off-by: Riku Voipio <riku.voipio@iki.fi> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6484 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
		
							parent
							
								
									e1ce5e400a
								
							
						
					
					
						commit
						04a6dfebb6
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -0,0 +1,247 @@ | |||
| #include <sys/queue.h> | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <errno.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #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); | ||||
| } | ||||
|  | @ -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 */ | ||||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 aurel32
						aurel32