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