Commit 04a6dfebb6b52532a1e0bd637899f1eba14e94c6
1 parent
e1ce5e40
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
Showing
4 changed files
with
304 additions
and
17 deletions
Makefile.target
| ... | ... | @@ -320,7 +320,7 @@ CFLAGS+=-p |
| 320 | 320 | endif |
| 321 | 321 | |
| 322 | 322 | OBJS= main.o syscall.o strace.o mmap.o signal.o path.o thunk.o \ |
| 323 | - elfload.o linuxload.o uaccess.o | |
| 323 | + elfload.o linuxload.o uaccess.o envlist.o | |
| 324 | 324 | LIBS+= $(AIOLIBS) |
| 325 | 325 | ifdef TARGET_HAS_BFLT |
| 326 | 326 | OBJS+= flatload.o | ... | ... |
linux-user/envlist.c
0 → 100644
| 1 | +#include <sys/queue.h> | |
| 2 | + | |
| 3 | +#include <assert.h> | |
| 4 | +#include <errno.h> | |
| 5 | +#include <stdlib.h> | |
| 6 | +#include <string.h> | |
| 7 | +#include <unistd.h> | |
| 8 | + | |
| 9 | +#include "envlist.h" | |
| 10 | + | |
| 11 | +struct envlist_entry { | |
| 12 | + const char *ev_var; /* actual env value */ | |
| 13 | + LIST_ENTRY(envlist_entry) ev_link; | |
| 14 | +}; | |
| 15 | + | |
| 16 | +struct envlist { | |
| 17 | + LIST_HEAD(, envlist_entry) el_entries; /* actual entries */ | |
| 18 | + size_t el_count; /* number of entries */ | |
| 19 | +}; | |
| 20 | + | |
| 21 | +static int envlist_parse(envlist_t *envlist, | |
| 22 | + const char *env, int (*)(envlist_t *, const char *)); | |
| 23 | + | |
| 24 | +/* | |
| 25 | + * Allocates new envlist and returns pointer to that or | |
| 26 | + * NULL in case of error. | |
| 27 | + */ | |
| 28 | +envlist_t * | |
| 29 | +envlist_create(void) | |
| 30 | +{ | |
| 31 | + envlist_t *envlist; | |
| 32 | + | |
| 33 | + if ((envlist = malloc(sizeof (*envlist))) == NULL) | |
| 34 | + return (NULL); | |
| 35 | + | |
| 36 | + LIST_INIT(&envlist->el_entries); | |
| 37 | + envlist->el_count = 0; | |
| 38 | + | |
| 39 | + return (envlist); | |
| 40 | +} | |
| 41 | + | |
| 42 | +/* | |
| 43 | + * Releases given envlist and its entries. | |
| 44 | + */ | |
| 45 | +void | |
| 46 | +envlist_free(envlist_t *envlist) | |
| 47 | +{ | |
| 48 | + struct envlist_entry *entry; | |
| 49 | + | |
| 50 | + assert(envlist != NULL); | |
| 51 | + | |
| 52 | + while (envlist->el_entries.lh_first != NULL) { | |
| 53 | + entry = envlist->el_entries.lh_first; | |
| 54 | + LIST_REMOVE(entry, ev_link); | |
| 55 | + | |
| 56 | + free((char *)entry->ev_var); | |
| 57 | + free(entry); | |
| 58 | + } | |
| 59 | + free(envlist); | |
| 60 | +} | |
| 61 | + | |
| 62 | +/* | |
| 63 | + * Parses comma separated list of set/modify environment | |
| 64 | + * variable entries and updates given enlist accordingly. | |
| 65 | + * | |
| 66 | + * For example: | |
| 67 | + * envlist_parse(el, "HOME=foo,SHELL=/bin/sh"); | |
| 68 | + * | |
| 69 | + * inserts/sets environment variables HOME and SHELL. | |
| 70 | + * | |
| 71 | + * Returns 0 on success, errno otherwise. | |
| 72 | + */ | |
| 73 | +int | |
| 74 | +envlist_parse_set(envlist_t *envlist, const char *env) | |
| 75 | +{ | |
| 76 | + return (envlist_parse(envlist, env, &envlist_setenv)); | |
| 77 | +} | |
| 78 | + | |
| 79 | +/* | |
| 80 | + * Parses comma separated list of unset environment variable | |
| 81 | + * entries and removes given variables from given envlist. | |
| 82 | + * | |
| 83 | + * Returns 0 on success, errno otherwise. | |
| 84 | + */ | |
| 85 | +int | |
| 86 | +envlist_parse_unset(envlist_t *envlist, const char *env) | |
| 87 | +{ | |
| 88 | + return (envlist_parse(envlist, env, &envlist_unsetenv)); | |
| 89 | +} | |
| 90 | + | |
| 91 | +/* | |
| 92 | + * Parses comma separated list of set, modify or unset entries | |
| 93 | + * and calls given callback for each entry. | |
| 94 | + * | |
| 95 | + * Returns 0 in case of success, errno otherwise. | |
| 96 | + */ | |
| 97 | +static int | |
| 98 | +envlist_parse(envlist_t *envlist, const char *env, | |
| 99 | + int (*callback)(envlist_t *, const char *)) | |
| 100 | +{ | |
| 101 | + char *tmpenv, *envvar; | |
| 102 | + char *envsave = NULL; | |
| 103 | + | |
| 104 | + assert(callback != NULL); | |
| 105 | + | |
| 106 | + if ((envlist == NULL) || (env == NULL)) | |
| 107 | + return (EINVAL); | |
| 108 | + | |
| 109 | + /* | |
| 110 | + * We need to make temporary copy of the env string | |
| 111 | + * as strtok_r(3) modifies it while it tokenizes. | |
| 112 | + */ | |
| 113 | + if ((tmpenv = strdup(env)) == NULL) | |
| 114 | + return (errno); | |
| 115 | + | |
| 116 | + envvar = strtok_r(tmpenv, ",", &envsave); | |
| 117 | + while (envvar != NULL) { | |
| 118 | + if ((*callback)(envlist, envvar) != 0) { | |
| 119 | + free(tmpenv); | |
| 120 | + return (errno); | |
| 121 | + } | |
| 122 | + envvar = strtok_r(NULL, ",", &envsave); | |
| 123 | + } | |
| 124 | + | |
| 125 | + free(tmpenv); | |
| 126 | + return (0); | |
| 127 | +} | |
| 128 | + | |
| 129 | +/* | |
| 130 | + * Sets environment value to envlist in similar manner | |
| 131 | + * than putenv(3). | |
| 132 | + * | |
| 133 | + * Returns 0 in success, errno otherwise. | |
| 134 | + */ | |
| 135 | +int | |
| 136 | +envlist_setenv(envlist_t *envlist, const char *env) | |
| 137 | +{ | |
| 138 | + struct envlist_entry *entry = NULL; | |
| 139 | + const char *eq_sign; | |
| 140 | + size_t envname_len; | |
| 141 | + | |
| 142 | + if ((envlist == NULL) || (env == NULL)) | |
| 143 | + return (EINVAL); | |
| 144 | + | |
| 145 | + /* find out first equals sign in given env */ | |
| 146 | + if ((eq_sign = strchr(env, '=')) == NULL) | |
| 147 | + return (EINVAL); | |
| 148 | + envname_len = eq_sign - env + 1; | |
| 149 | + | |
| 150 | + /* | |
| 151 | + * If there already exists variable with given name | |
| 152 | + * we remove and release it before allocating a whole | |
| 153 | + * new entry. | |
| 154 | + */ | |
| 155 | + for (entry = envlist->el_entries.lh_first; entry != NULL; | |
| 156 | + entry = entry->ev_link.le_next) { | |
| 157 | + if (strncmp(entry->ev_var, env, envname_len) == 0) | |
| 158 | + break; | |
| 159 | + } | |
| 160 | + | |
| 161 | + if (entry != NULL) { | |
| 162 | + LIST_REMOVE(entry, ev_link); | |
| 163 | + free((char *)entry->ev_var); | |
| 164 | + free(entry); | |
| 165 | + } else { | |
| 166 | + envlist->el_count++; | |
| 167 | + } | |
| 168 | + | |
| 169 | + if ((entry = malloc(sizeof (*entry))) == NULL) | |
| 170 | + return (errno); | |
| 171 | + if ((entry->ev_var = strdup(env)) == NULL) { | |
| 172 | + free(entry); | |
| 173 | + return (errno); | |
| 174 | + } | |
| 175 | + LIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link); | |
| 176 | + | |
| 177 | + return (0); | |
| 178 | +} | |
| 179 | + | |
| 180 | +/* | |
| 181 | + * Removes given env value from envlist in similar manner | |
| 182 | + * than unsetenv(3). Returns 0 in success, errno otherwise. | |
| 183 | + */ | |
| 184 | +int | |
| 185 | +envlist_unsetenv(envlist_t *envlist, const char *env) | |
| 186 | +{ | |
| 187 | + struct envlist_entry *entry; | |
| 188 | + size_t envname_len; | |
| 189 | + | |
| 190 | + if ((envlist == NULL) || (env == NULL)) | |
| 191 | + return (EINVAL); | |
| 192 | + | |
| 193 | + /* env is not allowed to contain '=' */ | |
| 194 | + if (strchr(env, '=') != NULL) | |
| 195 | + return (EINVAL); | |
| 196 | + | |
| 197 | + /* | |
| 198 | + * Find out the requested entry and remove | |
| 199 | + * it from the list. | |
| 200 | + */ | |
| 201 | + envname_len = strlen(env); | |
| 202 | + for (entry = envlist->el_entries.lh_first; entry != NULL; | |
| 203 | + entry = entry->ev_link.le_next) { | |
| 204 | + if (strncmp(entry->ev_var, env, envname_len) == 0) | |
| 205 | + break; | |
| 206 | + } | |
| 207 | + if (entry != NULL) { | |
| 208 | + LIST_REMOVE(entry, ev_link); | |
| 209 | + free((char *)entry->ev_var); | |
| 210 | + free(entry); | |
| 211 | + | |
| 212 | + envlist->el_count--; | |
| 213 | + } | |
| 214 | + return (0); | |
| 215 | +} | |
| 216 | + | |
| 217 | +/* | |
| 218 | + * Returns given envlist as array of strings (in same form that | |
| 219 | + * global variable environ is). Caller must free returned memory | |
| 220 | + * by calling free(3) for each element and for the array. Returned | |
| 221 | + * array and given envlist are not related (no common references). | |
| 222 | + * | |
| 223 | + * If caller provides count pointer, number of items in array is | |
| 224 | + * stored there. In case of error, NULL is returned and no memory | |
| 225 | + * is allocated. | |
| 226 | + */ | |
| 227 | +char ** | |
| 228 | +envlist_to_environ(const envlist_t *envlist, size_t *count) | |
| 229 | +{ | |
| 230 | + struct envlist_entry *entry; | |
| 231 | + char **env, **penv; | |
| 232 | + | |
| 233 | + penv = env = malloc((envlist->el_count + 1) * sizeof (char *)); | |
| 234 | + if (env == NULL) | |
| 235 | + return (NULL); | |
| 236 | + | |
| 237 | + for (entry = envlist->el_entries.lh_first; entry != NULL; | |
| 238 | + entry = entry->ev_link.le_next) { | |
| 239 | + *(penv++) = strdup(entry->ev_var); | |
| 240 | + } | |
| 241 | + *penv = NULL; /* NULL terminate the list */ | |
| 242 | + | |
| 243 | + if (count != NULL) | |
| 244 | + *count = envlist->el_count; | |
| 245 | + | |
| 246 | + return (env); | |
| 247 | +} | ... | ... |
linux-user/envlist.h
0 → 100644
| 1 | +#ifndef ENVLIST_H | |
| 2 | +#define ENVLIST_H | |
| 3 | + | |
| 4 | +#ifdef __cplusplus | |
| 5 | +extern "C" { | |
| 6 | +#endif | |
| 7 | + | |
| 8 | +typedef struct envlist envlist_t; | |
| 9 | + | |
| 10 | +extern envlist_t *envlist_create(void); | |
| 11 | +extern void envlist_free(envlist_t *); | |
| 12 | +extern int envlist_setenv(envlist_t *, const char *); | |
| 13 | +extern int envlist_unsetenv(envlist_t *, const char *); | |
| 14 | +extern int envlist_parse_set(envlist_t *, const char *); | |
| 15 | +extern int envlist_parse_unset(envlist_t *, const char *); | |
| 16 | +extern char **envlist_to_environ(const envlist_t *, size_t *); | |
| 17 | + | |
| 18 | +#ifdef __cplusplus | |
| 19 | +} | |
| 20 | +#endif | |
| 21 | + | |
| 22 | +#endif /* ENVLIST_H */ | ... | ... |
linux-user/main.c
| ... | ... | @@ -32,6 +32,9 @@ |
| 32 | 32 | /* For tb_lock */ |
| 33 | 33 | #include "exec-all.h" |
| 34 | 34 | |
| 35 | + | |
| 36 | +#include "envlist.h" | |
| 37 | + | |
| 35 | 38 | #define DEBUG_LOGFILE "/tmp/qemu.log" |
| 36 | 39 | |
| 37 | 40 | static const char *interp_prefix = CONFIG_QEMU_PREFIX; |
| ... | ... | @@ -2186,6 +2189,8 @@ static void usage(void) |
| 2186 | 2189 | "-s size set the stack size in bytes (default=%ld)\n" |
| 2187 | 2190 | "-cpu model select CPU (-cpu ? for list)\n" |
| 2188 | 2191 | "-drop-ld-preload drop LD_PRELOAD for target process\n" |
| 2192 | + "-E var=value sets/modifies targets environment variable(s)\n" | |
| 2193 | + "-U var unsets targets environment variable(s)\n" | |
| 2189 | 2194 | "\n" |
| 2190 | 2195 | "Debug options:\n" |
| 2191 | 2196 | "-d options activate log (logfile=%s)\n" |
| ... | ... | @@ -2195,6 +2200,12 @@ static void usage(void) |
| 2195 | 2200 | "Environment variables:\n" |
| 2196 | 2201 | "QEMU_STRACE Print system calls and arguments similar to the\n" |
| 2197 | 2202 | " 'strace' program. Enable by setting to any value.\n" |
| 2203 | + "You can use -E and -U options to set/unset environment variables\n" | |
| 2204 | + "for target process. It is possible to provide several variables\n" | |
| 2205 | + "by repeating the option. For example:\n" | |
| 2206 | + " -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n" | |
| 2207 | + "Note that if you provide several changes to single variable\n" | |
| 2208 | + "last change will stay in effect.\n" | |
| 2198 | 2209 | , |
| 2199 | 2210 | TARGET_ARCH, |
| 2200 | 2211 | interp_prefix, |
| ... | ... | @@ -2229,8 +2240,8 @@ int main(int argc, char **argv, char **envp) |
| 2229 | 2240 | int optind; |
| 2230 | 2241 | const char *r; |
| 2231 | 2242 | int gdbstub_port = 0; |
| 2232 | - int drop_ld_preload = 0, environ_count = 0; | |
| 2233 | - char **target_environ, **wrk, **dst; | |
| 2243 | + char **target_environ, **wrk; | |
| 2244 | + envlist_t *envlist = NULL; | |
| 2234 | 2245 | |
| 2235 | 2246 | if (argc <= 1) |
| 2236 | 2247 | usage(); |
| ... | ... | @@ -2240,6 +2251,16 @@ int main(int argc, char **argv, char **envp) |
| 2240 | 2251 | /* init debug */ |
| 2241 | 2252 | cpu_set_log_filename(DEBUG_LOGFILE); |
| 2242 | 2253 | |
| 2254 | + if ((envlist = envlist_create()) == NULL) { | |
| 2255 | + (void) fprintf(stderr, "Unable to allocate envlist\n"); | |
| 2256 | + exit(1); | |
| 2257 | + } | |
| 2258 | + | |
| 2259 | + /* add current environment into the list */ | |
| 2260 | + for (wrk = environ; *wrk != NULL; wrk++) { | |
| 2261 | + (void) envlist_setenv(envlist, *wrk); | |
| 2262 | + } | |
| 2263 | + | |
| 2243 | 2264 | cpu_model = NULL; |
| 2244 | 2265 | optind = 1; |
| 2245 | 2266 | for(;;) { |
| ... | ... | @@ -2269,6 +2290,14 @@ int main(int argc, char **argv, char **envp) |
| 2269 | 2290 | exit(1); |
| 2270 | 2291 | } |
| 2271 | 2292 | cpu_set_log(mask); |
| 2293 | + } else if (!strcmp(r, "E")) { | |
| 2294 | + r = argv[optind++]; | |
| 2295 | + if (envlist_setenv(envlist, r) != 0) | |
| 2296 | + usage(); | |
| 2297 | + } else if (!strcmp(r, "U")) { | |
| 2298 | + r = argv[optind++]; | |
| 2299 | + if (envlist_unsetenv(envlist, r) != 0) | |
| 2300 | + usage(); | |
| 2272 | 2301 | } else if (!strcmp(r, "s")) { |
| 2273 | 2302 | r = argv[optind++]; |
| 2274 | 2303 | x86_stack_size = strtol(r, (char **)&r, 0); |
| ... | ... | @@ -2301,7 +2330,7 @@ int main(int argc, char **argv, char **envp) |
| 2301 | 2330 | _exit(1); |
| 2302 | 2331 | } |
| 2303 | 2332 | } else if (!strcmp(r, "drop-ld-preload")) { |
| 2304 | - drop_ld_preload = 1; | |
| 2333 | + (void) envlist_unsetenv(envlist, "LD_PRELOAD"); | |
| 2305 | 2334 | } else if (!strcmp(r, "strace")) { |
| 2306 | 2335 | do_strace = 1; |
| 2307 | 2336 | } else |
| ... | ... | @@ -2369,19 +2398,8 @@ int main(int argc, char **argv, char **envp) |
| 2369 | 2398 | do_strace = 1; |
| 2370 | 2399 | } |
| 2371 | 2400 | |
| 2372 | - wrk = environ; | |
| 2373 | - while (*(wrk++)) | |
| 2374 | - environ_count++; | |
| 2375 | - | |
| 2376 | - target_environ = malloc((environ_count + 1) * sizeof(char *)); | |
| 2377 | - if (!target_environ) | |
| 2378 | - abort(); | |
| 2379 | - for (wrk = environ, dst = target_environ; *wrk; wrk++) { | |
| 2380 | - if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11)) | |
| 2381 | - continue; | |
| 2382 | - *(dst++) = strdup(*wrk); | |
| 2383 | - } | |
| 2384 | - *dst = NULL; /* NULL terminate target_environ */ | |
| 2401 | + target_environ = envlist_to_environ(envlist, NULL); | |
| 2402 | + envlist_free(envlist); | |
| 2385 | 2403 | |
| 2386 | 2404 | if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { |
| 2387 | 2405 | printf("Error loading %s\n", filename); | ... | ... |