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,7 +320,7 @@ CFLAGS+=-p | ||
320 | endif | 320 | endif |
321 | 321 | ||
322 | OBJS= main.o syscall.o strace.o mmap.o signal.o path.o thunk.o \ | 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 | LIBS+= $(AIOLIBS) | 324 | LIBS+= $(AIOLIBS) |
325 | ifdef TARGET_HAS_BFLT | 325 | ifdef TARGET_HAS_BFLT |
326 | OBJS+= flatload.o | 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,6 +32,9 @@ | ||
32 | /* For tb_lock */ | 32 | /* For tb_lock */ |
33 | #include "exec-all.h" | 33 | #include "exec-all.h" |
34 | 34 | ||
35 | + | ||
36 | +#include "envlist.h" | ||
37 | + | ||
35 | #define DEBUG_LOGFILE "/tmp/qemu.log" | 38 | #define DEBUG_LOGFILE "/tmp/qemu.log" |
36 | 39 | ||
37 | static const char *interp_prefix = CONFIG_QEMU_PREFIX; | 40 | static const char *interp_prefix = CONFIG_QEMU_PREFIX; |
@@ -2186,6 +2189,8 @@ static void usage(void) | @@ -2186,6 +2189,8 @@ static void usage(void) | ||
2186 | "-s size set the stack size in bytes (default=%ld)\n" | 2189 | "-s size set the stack size in bytes (default=%ld)\n" |
2187 | "-cpu model select CPU (-cpu ? for list)\n" | 2190 | "-cpu model select CPU (-cpu ? for list)\n" |
2188 | "-drop-ld-preload drop LD_PRELOAD for target process\n" | 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 | "\n" | 2194 | "\n" |
2190 | "Debug options:\n" | 2195 | "Debug options:\n" |
2191 | "-d options activate log (logfile=%s)\n" | 2196 | "-d options activate log (logfile=%s)\n" |
@@ -2195,6 +2200,12 @@ static void usage(void) | @@ -2195,6 +2200,12 @@ static void usage(void) | ||
2195 | "Environment variables:\n" | 2200 | "Environment variables:\n" |
2196 | "QEMU_STRACE Print system calls and arguments similar to the\n" | 2201 | "QEMU_STRACE Print system calls and arguments similar to the\n" |
2197 | " 'strace' program. Enable by setting to any value.\n" | 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 | TARGET_ARCH, | 2210 | TARGET_ARCH, |
2200 | interp_prefix, | 2211 | interp_prefix, |
@@ -2229,8 +2240,8 @@ int main(int argc, char **argv, char **envp) | @@ -2229,8 +2240,8 @@ int main(int argc, char **argv, char **envp) | ||
2229 | int optind; | 2240 | int optind; |
2230 | const char *r; | 2241 | const char *r; |
2231 | int gdbstub_port = 0; | 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 | if (argc <= 1) | 2246 | if (argc <= 1) |
2236 | usage(); | 2247 | usage(); |
@@ -2240,6 +2251,16 @@ int main(int argc, char **argv, char **envp) | @@ -2240,6 +2251,16 @@ int main(int argc, char **argv, char **envp) | ||
2240 | /* init debug */ | 2251 | /* init debug */ |
2241 | cpu_set_log_filename(DEBUG_LOGFILE); | 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 | cpu_model = NULL; | 2264 | cpu_model = NULL; |
2244 | optind = 1; | 2265 | optind = 1; |
2245 | for(;;) { | 2266 | for(;;) { |
@@ -2269,6 +2290,14 @@ int main(int argc, char **argv, char **envp) | @@ -2269,6 +2290,14 @@ int main(int argc, char **argv, char **envp) | ||
2269 | exit(1); | 2290 | exit(1); |
2270 | } | 2291 | } |
2271 | cpu_set_log(mask); | 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 | } else if (!strcmp(r, "s")) { | 2301 | } else if (!strcmp(r, "s")) { |
2273 | r = argv[optind++]; | 2302 | r = argv[optind++]; |
2274 | x86_stack_size = strtol(r, (char **)&r, 0); | 2303 | x86_stack_size = strtol(r, (char **)&r, 0); |
@@ -2301,7 +2330,7 @@ int main(int argc, char **argv, char **envp) | @@ -2301,7 +2330,7 @@ int main(int argc, char **argv, char **envp) | ||
2301 | _exit(1); | 2330 | _exit(1); |
2302 | } | 2331 | } |
2303 | } else if (!strcmp(r, "drop-ld-preload")) { | 2332 | } else if (!strcmp(r, "drop-ld-preload")) { |
2304 | - drop_ld_preload = 1; | 2333 | + (void) envlist_unsetenv(envlist, "LD_PRELOAD"); |
2305 | } else if (!strcmp(r, "strace")) { | 2334 | } else if (!strcmp(r, "strace")) { |
2306 | do_strace = 1; | 2335 | do_strace = 1; |
2307 | } else | 2336 | } else |
@@ -2369,19 +2398,8 @@ int main(int argc, char **argv, char **envp) | @@ -2369,19 +2398,8 @@ int main(int argc, char **argv, char **envp) | ||
2369 | do_strace = 1; | 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 | if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { | 2404 | if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) { |
2387 | printf("Error loading %s\n", filename); | 2405 | printf("Error loading %s\n", filename); |