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