1#include "qemu/osdep.h"
2#include "qemu/queue.h"
3#include "qemu/envlist.h"
4
5struct envlist_entry {
6 const char *ev_var; /* actual env value */
7 QLIST_ENTRY(envlist_entry) ev_link;
8};
9
10struct envlist {
11 QLIST_HEAD(, envlist_entry) el_entries; /* actual entries */
12 size_t el_count; /* number of entries */
13};
14
15static 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 */
21envlist_t *
22envlist_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 */
37void
38envlist_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 */
65int
66envlist_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 */
77int
78envlist_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 */
89static int
90envlist_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 */
126int
127envlist_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 */
171int
172envlist_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 */
214char **
215envlist_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