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); | ... | ... |