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