1 |
/** |
2 |
* Copyright (c) Members of the EGEE Collaboration. 2010. |
3 |
* See http://www.eu-egee.org/partners/ for details on the copyright |
4 |
* holders. |
5 |
* |
6 |
* Licensed under the Apache License, Version 2.0 (the "License"); |
7 |
* you may not use this file except in compliance with the License. |
8 |
* You may obtain a copy of the License at |
9 |
* |
10 |
* http://www.apache.org/licenses/LICENSE-2.0 |
11 |
* |
12 |
* Unless required by applicable law or agreed to in writing, software |
13 |
* distributed under the License is distributed on an "AS IS" BASIS, |
14 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 |
* See the License for the specific language governing permissions and |
16 |
* limitations under the License. |
17 |
* |
18 |
* Authors: Oscar Koeroo, Mischa Sall\'e, Aram Verstegen |
19 |
* NIKHEF Amsterdam, the Netherlands |
20 |
* <grid-mw-security@nikhef.nl> |
21 |
*/ |
22 |
|
23 |
#include <unistd.h> |
24 |
#include <stdlib.h> |
25 |
#include <string.h> |
26 |
#include <stdio.h> |
27 |
|
28 |
#include "environ.h" |
29 |
|
30 |
/** |
31 |
* returns number of entries in NULL terminated string array, such as argv and |
32 |
* environ(7). In case list is NULL it also returns 0. |
33 |
*/ |
34 |
int strarrlen(const char **list) { |
35 |
int entries; |
36 |
/* Check if list exists: prevents common segfault situation */ |
37 |
if (!list) return 0; |
38 |
/* Now loop over its entries */ |
39 |
for(entries=0; list[entries]; entries++); |
40 |
return entries; |
41 |
} |
42 |
|
43 |
/** |
44 |
* Returns the length of the name part of the var=value pair or -1 when there is |
45 |
* no '=' sign. |
46 |
*/ |
47 |
int getvarnamelen(const char *varname) { |
48 |
char *pos; |
49 |
/* find first '=' */ |
50 |
pos=strchr(varname,'='); |
51 |
if (pos==NULL) /* not var=value */ |
52 |
return -1; |
53 |
return (int)strlen(varname)-(int)strlen(pos); /* no pointer arithmetics */ |
54 |
} |
55 |
|
56 |
|
57 |
/** |
58 |
* Finds name for given var=value pair in namevalue. varname is a buffer |
59 |
* which will contain the result and has space for maxvarnamelen chars. |
60 |
* Returns integer, negative on error, -1 on no '=' sign, -2 name too long; |
61 |
* 0 success |
62 |
*/ |
63 |
int getvarname(char *varname,const int maxvarnamelen, const char *namevalue) { |
64 |
int varnamelen; |
65 |
|
66 |
if ((varnamelen=getvarnamelen(namevalue))==-1) |
67 |
return -1; |
68 |
if (varnamelen>maxvarnamelen) |
69 |
return -2; |
70 |
strncpy(varname,namevalue,(size_t)varnamelen); |
71 |
varname[varnamelen]='\0'; |
72 |
|
73 |
return 0; |
74 |
} |
75 |
|
76 |
/** |
77 |
* Finds index in **src matching variable name, in case of no match (including |
78 |
* env==NULL) returns -1 |
79 |
*/ |
80 |
int getvarindex(const env_t src, const char *varname) { |
81 |
int i; |
82 |
size_t len; |
83 |
|
84 |
if (src==NULL || varname==NULL) |
85 |
return -1; |
86 |
|
87 |
len=strlen(varname); |
88 |
/* Loop over env */ |
89 |
for (i=0; src[i]; i++) { |
90 |
if (strncmp(src[i],varname,len)==0 && src[i][len]=='=') |
91 |
/* Found match */ |
92 |
return i; |
93 |
} |
94 |
/* NO match found */ |
95 |
return -1; |
96 |
} |
97 |
|
98 |
/** |
99 |
* Finds the value of varname in src list. Returns NULL when no match, incl. |
100 |
* NULL valued src or varname. |
101 |
*/ |
102 |
char *getenv_src(const env_t src, char *varname) { |
103 |
int i; |
104 |
size_t len; |
105 |
char *match; |
106 |
|
107 |
/* Don't use getvarindex, since this saves us one strlen (-: */ |
108 |
if (src==NULL || varname==NULL) |
109 |
return NULL; |
110 |
|
111 |
len=strlen(varname); |
112 |
/* Loop over env */ |
113 |
for (i=0; src[i]; i++) { |
114 |
if (strncmp(src[i],varname,len)==0 && src[i][len]=='=') { |
115 |
/* Found match */ |
116 |
match=&(src[i][len+1]); |
117 |
return match; |
118 |
} |
119 |
} |
120 |
/* NO match found */ |
121 |
return NULL; |
122 |
} |
123 |
|
124 |
/** |
125 |
* As unsetenv(3) but using var=value pair, specified in namevalue. The maximum |
126 |
* var length is MAXVARNAMELEN. See also putenv(3). |
127 |
* Returns integer, 0 on success, negative on error. |
128 |
*/ |
129 |
int unsetenvpair(const char *namevalue) { |
130 |
char varname[MAXENVVARNAMELEN]; |
131 |
int rc; |
132 |
|
133 |
/* Get variable name for var=value pair */ |
134 |
if ((rc=getvarname(varname,MAXENVVARNAMELEN,namevalue))!=0) |
135 |
return rc; |
136 |
/* Unset variable */ |
137 |
if (unsetenv(varname)) |
138 |
return -1; |
139 |
|
140 |
return 0; |
141 |
} |
142 |
|
143 |
/** |
144 |
* Clears the entire environment |
145 |
* Returns integer, -1 on error, 0 on success |
146 |
*/ |
147 |
int clear_env(void) { |
148 |
while(environ[0]) { |
149 |
if (unsetenvpair(environ[0])) |
150 |
return -1; |
151 |
} |
152 |
return 0; |
153 |
} |
154 |
|
155 |
/* |
156 |
* Clears the environment of all variables starting with pattern, case |
157 |
* INsensitive. |
158 |
* Returns number of cleaned entries, -1 on error. |
159 |
*/ |
160 |
int clear_env_pattern(const char *pattern) { |
161 |
int i=0,match=0,rc; |
162 |
size_t patlen=strlen(pattern); |
163 |
|
164 |
while(environ[i]) { |
165 |
if (strncasecmp(pattern,environ[i],patlen)==0) { |
166 |
if ((rc=unsetenvpair(environ[i]))) |
167 |
return rc; |
168 |
match++; |
169 |
} else |
170 |
i++; /* only increase when we haven't just removed an entry... */ |
171 |
} |
172 |
return match; |
173 |
} |
174 |
|
175 |
|
176 |
/** |
177 |
* Creates a duplicate of src. In case *dst!=NULL it will be used and should be |
178 |
* big enough. Otherwise space is malloc-ed. dst is only updated upon success. |
179 |
* Returns -1 upon error, 0 on success. |
180 |
*/ |
181 |
int copy_env(env_t *dst,const env_t src) { |
182 |
int i,j,envlen; |
183 |
env_t newenv=*dst; |
184 |
|
185 |
/* If no dst exists, malloc it. */ |
186 |
if (newenv==NULL) { |
187 |
envlen=strarrlen((const char**)src); |
188 |
newenv=(env_t)malloc((envlen+1)*sizeof(char*)); |
189 |
if (newenv==NULL) |
190 |
return -1; |
191 |
} |
192 |
|
193 |
/* Copy src to newenv */ |
194 |
for (i=0; src[i]; i++) { |
195 |
if ( (newenv[i]=strdup(src[i]))==NULL ) { |
196 |
/* Error: cleanup memory and reset */ |
197 |
for (j=0; j<i; j++) |
198 |
free(newenv[j]); |
199 |
if (*dst==NULL) |
200 |
free(newenv); |
201 |
return -1; |
202 |
} |
203 |
} |
204 |
/* Finalize the new environ */ |
205 |
newenv[i]=NULL; |
206 |
if (*dst==NULL) |
207 |
*dst=newenv; |
208 |
|
209 |
return 0; |
210 |
} |
211 |
|
212 |
/** |
213 |
* Same as copy_env() but first unsets all environment variables starting with |
214 |
* MALLOC, case INsensitive. dst is again only updated upon success. |
215 |
* Returns -1 upon error, 0 on success. |
216 |
*/ |
217 |
int safe_copy_env(env_t *dst,const env_t src) { |
218 |
/* Clean all MALLOC* variables */ |
219 |
if (clear_env_pattern("MALLOC")) |
220 |
return -1; |
221 |
|
222 |
return copy_env(dst,src); |
223 |
} |
224 |
|
225 |
/** |
226 |
* Creates a list of the var=value pairs from src which have varname in list. |
227 |
* If it fails NULL is returned. This can also be done using add_src_list() |
228 |
* using dst==NULL, but this is more efficiently coded. All new entries are |
229 |
* strdup-ed from those in src. |
230 |
*/ |
231 |
env_t copy_src_list(const env_t src,const char *list[]) { |
232 |
int i,j,src_idx,match=0,listlen=strarrlen(list); |
233 |
env_t newenv; |
234 |
|
235 |
/* Malloc space */ |
236 |
newenv=(env_t)malloc((listlen+1)*sizeof(char*)); |
237 |
if (newenv==NULL) |
238 |
return NULL; |
239 |
|
240 |
/* IMPORTANT: list must be NULL terminated! */ |
241 |
newenv[0]=NULL; |
242 |
|
243 |
/* Only do when list exists, otherwise return empty list, but not NO list! |
244 |
* Note it's not an error, just an empty list. */ |
245 |
if (src==NULL || list==NULL) |
246 |
return newenv; |
247 |
|
248 |
/* loop over list: Note we can have unmatched entries: match counts */ |
249 |
for (i=0; list[i]; i++) { |
250 |
/* Look if list[i] is in src */ |
251 |
if ( (src_idx=getvarindex(src,list[i])) >= 0) { |
252 |
/* Don't! check for duplicates... */ |
253 |
/* substitute existing: first try strdup */ |
254 |
if ((newenv[match]=strdup(src[src_idx]))==NULL) { |
255 |
/* error: cleanup */ |
256 |
for (j=0; j<match; j++) { |
257 |
free(newenv[j]); newenv[j]=NULL; |
258 |
} |
259 |
free(newenv); |
260 |
return NULL; |
261 |
} |
262 |
match++; |
263 |
/* Don't forget the final NULL pointer! */ |
264 |
newenv[match]=NULL; |
265 |
} |
266 |
} |
267 |
return newenv; |
268 |
} |
269 |
|
270 |
/** |
271 |
* Adds to dst the var=value pairs from src which have varname in list. |
272 |
* All entries will be strdup-ed from those in src. |
273 |
* If *dst is NULL, it will be malloc-ed, otherwise it will be realloc-ed. |
274 |
* Upon success returns 0. On error -1 is returned, and dst stays unchanged. |
275 |
* See also copy_src_list() which is more efficient when dst is NULL ! |
276 |
*/ |
277 |
int add_src_list(env_t *dst,const env_t src,const char **list) { |
278 |
int i,j,src_idx,dst_idx,match=0; |
279 |
env_t newenv=*dst; |
280 |
int dstlen,listlen; |
281 |
char *dummy; |
282 |
|
283 |
dstlen=strarrlen((const char **)*dst); |
284 |
listlen=strarrlen(list); |
285 |
/* Create space for combined array, might be too much if list is not fully |
286 |
* used or partially replaces old entries, but more efficient (single |
287 |
* realloc() call). |
288 |
* Note that we check for empty list AFTER the malloc, and BEFORE the |
289 |
* realloc */ |
290 |
if (newenv==NULL) { |
291 |
newenv=(env_t)malloc((listlen+1)*sizeof(char*)); |
292 |
if (newenv==NULL) |
293 |
return -1; |
294 |
*dst=newenv; /* update *dst after malloc */ |
295 |
/* IMPORTANT: list must be NULL terminated! */ |
296 |
newenv[0]=NULL; |
297 |
/* Only continue when list and src exist, do alloc in this case, since |
298 |
* dst shouldn't return empty. Not an error. */ |
299 |
if (list==NULL || src==NULL) |
300 |
return 0; |
301 |
} else { |
302 |
/* Only do when list and src exist, no need to realloc, not an error. */ |
303 |
if (list==NULL || src==NULL) |
304 |
return 0; |
305 |
newenv=(env_t)realloc(newenv,(dstlen+listlen+1)*sizeof(char*)); |
306 |
if (newenv==NULL) |
307 |
return -1; |
308 |
*dst=newenv; /* update *dst after realloc */ |
309 |
} |
310 |
|
311 |
/* loop over list: Note we can have unmatched entries: match counts */ |
312 |
for (i=0; list[i]; i++) { |
313 |
/* Look if list[i] is in src */ |
314 |
if ( (src_idx=getvarindex(src,list[i])) >= 0) { |
315 |
/* Look for list[i] in newenv: we might have it already. Note |
316 |
* that this is not checked when using copy_src_list() */ |
317 |
if ( (dst_idx=getvarindex((const env_t)newenv,list[i])) >= 0) { |
318 |
/* substitute existing: first try strdup */ |
319 |
if ( (dummy=strdup(src[src_idx])) == NULL) { /* error: cleanup*/ |
320 |
for (j=dstlen; j<dstlen+match; j++) { |
321 |
free(newenv[j]); newenv[j]=NULL; |
322 |
} |
323 |
return -1; |
324 |
} |
325 |
/* Now substitute: free old entry in newenv and link to new |
326 |
* entry */ |
327 |
free(newenv[dst_idx]); |
328 |
newenv[dst_idx]=dummy; |
329 |
} else { |
330 |
/* create new one */ |
331 |
if ((newenv[dstlen+match]=strdup(src[src_idx]))==NULL) { |
332 |
/* Error: cleanup */ |
333 |
for (j=dstlen; j<dstlen+match; j++) { |
334 |
free(newenv[j]); newenv[j]=NULL; |
335 |
} |
336 |
return -1; |
337 |
} |
338 |
/* We created a new entry, increase match */ |
339 |
match++; |
340 |
/* Don't forget the final NULL pointer! */ |
341 |
newenv[dstlen+match]=NULL; |
342 |
} |
343 |
} |
344 |
} |
345 |
return 0; |
346 |
} |
347 |
|
348 |
/** |
349 |
* adds var=value pair to dst. If the variable already exists, it's entry is |
350 |
* replaced. Otherwise *dst is realloc-ed and a new entry is added, the |
351 |
* resulting array is returned. |
352 |
* Upon success returns 0. In case of error -1 is returned and dst remains |
353 |
* unchanged. namevalue is strdup-ed into dst and hence can be freed by the |
354 |
* caller (unlike putenv(3p) ) |
355 |
*/ |
356 |
int add_namevalue(env_t *dst,const char *namevalue) { |
357 |
int i,namelen,dstlen; |
358 |
char *namevaluecopy; |
359 |
env_t newenv=*dst; |
360 |
|
361 |
/* Look for length of name part (no = means no name) */ |
362 |
if ( (namelen=getvarnamelen(namevalue)) < 0) |
363 |
return -1; |
364 |
/* Make already a copy, we need it in any case, unless realloc fails */ |
365 |
if ( (namevaluecopy=strdup(namevalue)) == NULL) |
366 |
return -1; |
367 |
/* look for existing entry */ |
368 |
for (i=0; newenv[i]; i++) { |
369 |
if (strncmp(namevalue,newenv[i],namelen)==0 && newenv[i][namelen]=='=') |
370 |
{ |
371 |
free(newenv[i]); |
372 |
newenv[i]=namevaluecopy; |
373 |
return 0; |
374 |
} |
375 |
} |
376 |
/* No old match, make a new entry, add two, because strarrlen doesn't count |
377 |
* the NULL string at the end. First create new entry, since that's easier |
378 |
* to undo. */ |
379 |
dstlen=strarrlen((const char**)newenv); |
380 |
newenv=(env_t)realloc(newenv,(dstlen+2)*sizeof(char*)); |
381 |
if (newenv==NULL) { |
382 |
free(namevaluecopy); |
383 |
return -1; |
384 |
} |
385 |
*dst=newenv; /* update *dst after (re/m)alloc */ |
386 |
newenv[dstlen]=namevaluecopy; |
387 |
newenv[dstlen+1]=NULL; |
388 |
|
389 |
return 0; |
390 |
} |
391 |
|
392 |
/** |
393 |
* adds name=value pair to dst, as in setenv() with a 'external environment' . |
394 |
* If the variable already exists, it's entry is replaced. Otherwise *dst is |
395 |
* realloc-ed and a new entry is added. |
396 |
* Upon success returns 0. In case of error -1 is returned and dst remains |
397 |
* unchanged. name and value can be freed by the caller (unlike putenv(3p) ) |
398 |
*/ |
399 |
int setenv_dst(env_t *dst,const char *name, const char *value) { |
400 |
int i,dstlen; |
401 |
size_t namelen,valuelen; |
402 |
char *namevalue; |
403 |
env_t newenv=*dst; |
404 |
|
405 |
/* Check arguments */ |
406 |
if (name==NULL) |
407 |
return -1; |
408 |
if (value==NULL) { |
409 |
value=""; |
410 |
valuelen=0; |
411 |
} else |
412 |
valuelen=strlen(value); |
413 |
namelen=strlen(name); |
414 |
/* Add two: = and \0 */ |
415 |
if ((namevalue=(char*)malloc((namelen+valuelen+2)*sizeof(char)))==NULL) |
416 |
return -1; |
417 |
sprintf(namevalue,"%s=%s",name,value); |
418 |
|
419 |
/* look for existing entry */ |
420 |
for (i=0; newenv[i]; i++) { |
421 |
if (strncmp(name,newenv[i],namelen)==0 && newenv[i][namelen]=='=') { |
422 |
free(newenv[i]); |
423 |
newenv[i]=namevalue; |
424 |
return 0; |
425 |
} |
426 |
} |
427 |
/* No old match, make a new entry, add two, because strarrlen doesn't count |
428 |
* the NULL string at the end. */ |
429 |
dstlen=strarrlen((const char **)newenv); |
430 |
newenv=(env_t)realloc(newenv,(dstlen+2)*sizeof(char*)); |
431 |
if (newenv==NULL) { |
432 |
free(namevalue); |
433 |
return -1; |
434 |
} |
435 |
*dst=newenv; /* update *dst after (re/m)alloc */ |
436 |
newenv[dstlen]=namevalue; |
437 |
newenv[dstlen+1]=NULL; |
438 |
|
439 |
return 0; |
440 |
} |
441 |
|
442 |
/** |
443 |
* Adds to the current environment the var=value pairs from src which have |
444 |
* varname in list. Returns number of entries added or -1 on error. |
445 |
* NOTE: putenv doesn't reserve new memory so src should NOT be |
446 |
* free()d. |
447 |
*/ |
448 |
int putenv_src_list(env_t src,const char **list) { |
449 |
int i,src_idx,match=0; |
450 |
|
451 |
/* Only do when list exists...*/ |
452 |
if (!list) |
453 |
return 0; |
454 |
|
455 |
/* loop over list */ |
456 |
for (i=0; list[i]; i++) { |
457 |
if ((src_idx=getvarindex((const env_t)src,list[i]))>=0) { |
458 |
/* Found match */ |
459 |
if (putenv(src[src_idx])) |
460 |
return -1; |
461 |
match++; |
462 |
} |
463 |
} |
464 |
return match; |
465 |
} |
466 |
|
467 |
/** |
468 |
* Adds to the current environment the var=value pairs from src which start with |
469 |
* pattern. Returns number of entries added or -1 on error. |
470 |
* NOTE: putenv doesn't reserve new memory so src should NOT be |
471 |
* free()d. |
472 |
*/ |
473 |
int putenv_src_pattern(env_t src,const char *pattern) { |
474 |
int i,match=0; |
475 |
size_t len=strlen(pattern); |
476 |
|
477 |
/* loop over src environ */ |
478 |
for (i=0; src[i]; i++) { |
479 |
if (strncmp(src[i],pattern,len)==0) { |
480 |
/* Found match */ |
481 |
if (putenv(src[i])) |
482 |
return -1; |
483 |
match++; |
484 |
} |
485 |
} |
486 |
return match; |
487 |
} |
488 |
|