1 | #include "qemu/osdep.h" |
2 | #include "qemu/queue.h" |
3 | #include "qemu/envlist.h" |
4 | |
5 | struct envlist_entry { |
6 | const char *ev_var; /* actual env value */ |
7 | QLIST_ENTRY(envlist_entry) ev_link; |
8 | }; |
9 | |
10 | struct envlist { |
11 | QLIST_HEAD(, envlist_entry) el_entries; /* actual entries */ |
12 | size_t el_count; /* number of entries */ |
13 | }; |
14 | |
15 | static int envlist_parse(envlist_t *envlist, |
16 | const char *env, int (*)(envlist_t *, const char *)); |
17 | |
18 | /* |
19 | * Allocates new envlist and returns pointer to it. |
20 | */ |
21 | envlist_t * |
22 | envlist_create(void) |
23 | { |
24 | envlist_t *envlist; |
25 | |
26 | envlist = g_malloc(sizeof(*envlist)); |
27 | |
28 | QLIST_INIT(&envlist->el_entries); |
29 | envlist->el_count = 0; |
30 | |
31 | return (envlist); |
32 | } |
33 | |
34 | /* |
35 | * Releases given envlist and its entries. |
36 | */ |
37 | void |
38 | envlist_free(envlist_t *envlist) |
39 | { |
40 | struct envlist_entry *entry; |
41 | |
42 | assert(envlist != NULL); |
43 | |
44 | while (envlist->el_entries.lh_first != NULL) { |
45 | entry = envlist->el_entries.lh_first; |
46 | QLIST_REMOVE(entry, ev_link); |
47 | |
48 | g_free((char *)entry->ev_var); |
49 | g_free(entry); |
50 | } |
51 | g_free(envlist); |
52 | } |
53 | |
54 | /* |
55 | * Parses comma separated list of set/modify environment |
56 | * variable entries and updates given enlist accordingly. |
57 | * |
58 | * For example: |
59 | * envlist_parse(el, "HOME=foo,SHELL=/bin/sh"); |
60 | * |
61 | * inserts/sets environment variables HOME and SHELL. |
62 | * |
63 | * Returns 0 on success, errno otherwise. |
64 | */ |
65 | int |
66 | envlist_parse_set(envlist_t *envlist, const char *env) |
67 | { |
68 | return (envlist_parse(envlist, env, &envlist_setenv)); |
69 | } |
70 | |
71 | /* |
72 | * Parses comma separated list of unset environment variable |
73 | * entries and removes given variables from given envlist. |
74 | * |
75 | * Returns 0 on success, errno otherwise. |
76 | */ |
77 | int |
78 | envlist_parse_unset(envlist_t *envlist, const char *env) |
79 | { |
80 | return (envlist_parse(envlist, env, &envlist_unsetenv)); |
81 | } |
82 | |
83 | /* |
84 | * Parses comma separated list of set, modify or unset entries |
85 | * and calls given callback for each entry. |
86 | * |
87 | * Returns 0 in case of success, errno otherwise. |
88 | */ |
89 | static int |
90 | envlist_parse(envlist_t *envlist, const char *env, |
91 | int (*callback)(envlist_t *, const char *)) |
92 | { |
93 | char *tmpenv, *envvar; |
94 | char *envsave = NULL; |
95 | int ret = 0; |
96 | assert(callback != NULL); |
97 | |
98 | if ((envlist == NULL) || (env == NULL)) |
99 | return (EINVAL); |
100 | |
101 | tmpenv = g_strdup(env); |
102 | envsave = tmpenv; |
103 | |
104 | do { |
105 | envvar = strchr(tmpenv, ','); |
106 | if (envvar != NULL) { |
107 | *envvar = '\0'; |
108 | } |
109 | if ((*callback)(envlist, tmpenv) != 0) { |
110 | ret = errno; |
111 | break; |
112 | } |
113 | tmpenv = envvar + 1; |
114 | } while (envvar != NULL); |
115 | |
116 | g_free(envsave); |
117 | return ret; |
118 | } |
119 | |
120 | /* |
121 | * Sets environment value to envlist in similar manner |
122 | * than putenv(3). |
123 | * |
124 | * Returns 0 in success, errno otherwise. |
125 | */ |
126 | int |
127 | envlist_setenv(envlist_t *envlist, const char *env) |
128 | { |
129 | struct envlist_entry *entry = NULL; |
130 | const char *eq_sign; |
131 | size_t envname_len; |
132 | |
133 | if ((envlist == NULL) || (env == NULL)) |
134 | return (EINVAL); |
135 | |
136 | /* find out first equals sign in given env */ |
137 | if ((eq_sign = strchr(env, '=')) == NULL) |
138 | return (EINVAL); |
139 | envname_len = eq_sign - env + 1; |
140 | |
141 | /* |
142 | * If there already exists variable with given name |
143 | * we remove and release it before allocating a whole |
144 | * new entry. |
145 | */ |
146 | for (entry = envlist->el_entries.lh_first; entry != NULL; |
147 | entry = entry->ev_link.le_next) { |
148 | if (strncmp(entry->ev_var, env, envname_len) == 0) |
149 | break; |
150 | } |
151 | |
152 | if (entry != NULL) { |
153 | QLIST_REMOVE(entry, ev_link); |
154 | g_free((char *)entry->ev_var); |
155 | g_free(entry); |
156 | } else { |
157 | envlist->el_count++; |
158 | } |
159 | |
160 | entry = g_malloc(sizeof(*entry)); |
161 | entry->ev_var = g_strdup(env); |
162 | QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link); |
163 | |
164 | return (0); |
165 | } |
166 | |
167 | /* |
168 | * Removes given env value from envlist in similar manner |
169 | * than unsetenv(3). Returns 0 in success, errno otherwise. |
170 | */ |
171 | int |
172 | envlist_unsetenv(envlist_t *envlist, const char *env) |
173 | { |
174 | struct envlist_entry *entry; |
175 | size_t envname_len; |
176 | |
177 | if ((envlist == NULL) || (env == NULL)) |
178 | return (EINVAL); |
179 | |
180 | /* env is not allowed to contain '=' */ |
181 | if (strchr(env, '=') != NULL) |
182 | return (EINVAL); |
183 | |
184 | /* |
185 | * Find out the requested entry and remove |
186 | * it from the list. |
187 | */ |
188 | envname_len = strlen(env); |
189 | for (entry = envlist->el_entries.lh_first; entry != NULL; |
190 | entry = entry->ev_link.le_next) { |
191 | if (strncmp(entry->ev_var, env, envname_len) == 0) |
192 | break; |
193 | } |
194 | if (entry != NULL) { |
195 | QLIST_REMOVE(entry, ev_link); |
196 | g_free((char *)entry->ev_var); |
197 | g_free(entry); |
198 | |
199 | envlist->el_count--; |
200 | } |
201 | return (0); |
202 | } |
203 | |
204 | /* |
205 | * Returns given envlist as array of strings (in same form that |
206 | * global variable environ is). Caller must free returned memory |
207 | * by calling g_free for each element and the array. |
208 | * Returned array and given envlist are not related (no common |
209 | * references). |
210 | * |
211 | * If caller provides count pointer, number of items in array is |
212 | * stored there. |
213 | */ |
214 | char ** |
215 | envlist_to_environ(const envlist_t *envlist, size_t *count) |
216 | { |
217 | struct envlist_entry *entry; |
218 | char **env, **penv; |
219 | |
220 | penv = env = g_malloc((envlist->el_count + 1) * sizeof(char *)); |
221 | |
222 | for (entry = envlist->el_entries.lh_first; entry != NULL; |
223 | entry = entry->ev_link.le_next) { |
224 | *(penv++) = g_strdup(entry->ev_var); |
225 | } |
226 | *penv = NULL; /* NULL terminate the list */ |
227 | |
228 | if (count != NULL) |
229 | *count = envlist->el_count; |
230 | |
231 | return (env); |
232 | } |
233 | |