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