/ stdlib / FreeBSD / setenv.c
setenv.c
  1  /*
  2   * Copyright (c) 1987, 1993
  3   *	The Regents of the University of California.  All rights reserved.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions
  7   * are met:
  8   * 1. Redistributions of source code must retain the above copyright
  9   *    notice, this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   * 4. Neither the name of the University nor the names of its contributors
 14   *    may be used to endorse or promote products derived from this software
 15   *    without specific prior written permission.
 16   *
 17   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 18   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 21   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 22   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 23   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 24   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 25   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 26   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 27   * SUCH DAMAGE.
 28   */
 29  
 30  #if defined(LIBC_SCCS) && !defined(lint)
 31  static char sccsid[] = "@(#)setenv.c	8.1 (Berkeley) 6/4/93";
 32  #endif /* LIBC_SCCS and not lint */
 33  #include <sys/cdefs.h>
 34  __FBSDID("$FreeBSD: src/lib/libc/stdlib/setenv.c,v 1.14 2007/05/01 16:02:41 ache Exp $");
 35  
 36  #include <stddef.h>
 37  #include <stdlib.h>
 38  #include <string.h>
 39  #include <crt_externs.h>
 40  #include <errno.h>
 41  #include <sys/types.h>
 42  #include <fcntl.h>
 43  
 44  struct owned_ptr;
 45  extern char *__findenv_locked(const char *, int *, char **);
 46  extern int __setenv_locked(const char *, const char *, int, int, char ***, struct owned_ptr *);
 47  extern void __unsetenv_locked(const char *, char **, struct owned_ptr *);
 48  
 49  extern void __environ_lock(void);
 50  extern void __environ_unlock(void);
 51  
 52  extern struct owned_ptr *__env_owned;
 53  extern int __init__env_owned_locked(int);
 54  
 55  /*
 56   * _cthread_init_routine used to be called from crt1.o to initialize threads.
 57   * This is no longer needed, as initialization happens in dylib initializers,
 58   * but is provided to maintain backwards compatibility.  Normally, for 10.5
 59   * or greater, _cthread_init_routine does nothing.
 60   *
 61   * Before 10.5, the _start routine in crt1.o clobbers environ with the original
 62   * stack value, which effectively undoes any environment changes made in
 63   * initializers.  When LEGACY_CRT1_ENVIRON is defined, we replace the
 64   * do-nothing routine with one that attempts to restore the environ value.
 65   * But this only works if the setenv (and family) routines were used
 66   * exclusively, (no direct manipulation of environ).  Note that according to
 67   * SUSv3, direct manipulation of environ may result in undefined behavior in
 68   * setenv and family, so we don't support that (on less than 10.5).
 69   */
 70  #ifdef BUILDING_VARIANT
 71  #  ifdef LEGACY_CRT1_ENVIRON
 72  extern char **_saved_environ;
 73  #  endif /* LEGACY_CRT1_ENVIRON */
 74  #else /* !BUILDING_VARIANT */
 75  #  ifdef LEGACY_CRT1_ENVIRON
 76  __private_extern__ char **_saved_environ = NULL;
 77  
 78  static int
 79  _legacy_crt1_environ(void)
 80  {
 81  	if (_saved_environ) *_NSGetEnviron() = _saved_environ;
 82  	return 0;
 83  }
 84  int (*_cthread_init_routine)(void) = _legacy_crt1_environ;
 85  
 86  #  else /* !LEGACY_CRT1_ENVIRON */
 87  static int _do_nothing(void) { return 0; }
 88  int (*_cthread_init_routine)(void) = _do_nothing;
 89  #  endif /* !LEGACY_CRT1_ENVIRON */
 90  
 91  __private_extern__ struct owned_ptr *__env_owned = NULL;
 92  
 93  /*
 94   * The owned_ptr structure is a table of pointers that we own, and can
 95   * realloc/free as we choose.  table[0] is always NULL, so it is always
 96   * less that any real pointer.  "used" is the number of table entries in use
 97   * (including the initial NULL), while "size" is the allocated size of the
 98   * table (used to enlarge the size of the table when needed).
 99   */
100  struct owned_ptr {
101      const void **table;
102      int used;
103      int size;
104  };
105  
106  #define OWNED_PTR_INITIAL_SIZE	8
107  
108  __private_extern__ int _owned_ptr_search(struct owned_ptr * __restrict, const void * __restrict, int * __restrict);
109  
110  __private_extern__ void
111  _owned_ptr_add(struct owned_ptr * __restrict owned, const void * __restrict ptr)
112  {
113  	int index;
114  
115  	if (_owned_ptr_search(owned, ptr, &index) == 0) return; /* already there */
116  	if (owned->used >= owned->size) {
117  		int new_size = 2 * owned->size;
118  		const void **new_table = (const void **)realloc(owned->table,
119  					 new_size * sizeof(const void *));
120  		if (!new_table) {
121  			/* no memory to enlarge the table, so just drop */
122  			return;
123  		}
124  		owned->table = new_table;
125  		owned->size = new_size;
126  	}
127  	memmove(owned->table + index + 2, owned->table + index + 1,
128  		sizeof(void *) * (owned->used - index - 1));
129  	owned->table[index + 1] = ptr;
130  	owned->used++;
131  }
132  
133  __private_extern__ struct owned_ptr *
134  _owned_ptr_alloc(void)
135  {
136  	struct owned_ptr *owned;
137  
138  	owned = (struct owned_ptr *)malloc(sizeof(struct owned_ptr));
139  	if (!owned) return NULL;
140  	owned->table = (const void **)malloc(OWNED_PTR_INITIAL_SIZE *
141  					     sizeof(const void *));
142  	if (!owned->table) {
143  		int save = errno;
144  		free(owned);
145  		errno = save;
146  		return NULL;
147  	}
148  	owned->table[0] = NULL;
149  	owned->used = 1;
150  	owned->size = OWNED_PTR_INITIAL_SIZE;
151  	return owned;
152  }
153  
154  __private_extern__ void
155  _owned_ptr_delete(struct owned_ptr *owned, int index)
156  {
157  	if (!index || index >= owned->used) return;
158  	memmove(owned->table + index, owned->table + index + 1,
159  		sizeof(void *) * (owned->used - index - 1));
160  	owned->used--;
161  }
162  
163  __private_extern__ void
164  _owned_ptr_free(struct owned_ptr *owned)
165  {
166  	free(owned->table);
167  	free(owned);
168  }
169  
170  /*
171   * Search owned->table for "ptr".  Zero is returned if found, non-zero means
172   * not found.  If "result" is non-NULL, the found index is returned in it, or
173   * if not found, the index of the immediate lower value (ptr can be inserted
174   * after this index).
175   */
176  __private_extern__ int
177  _owned_ptr_search(struct owned_ptr * __restrict owned, const void * __restrict ptr, int * __restrict result)
178  {
179  	int low = 0;
180  	int high = owned->used - 1;
181  	int cur;
182  
183  	if (owned->table[high] < ptr) {
184  		if (result) *result = high;
185  		return -1;
186  	} else if (owned->table[high] == ptr) {
187  		if (result) *result = high;
188  		return 0;
189  	}
190  	while (high - low > 1) {
191  		cur = (low + high) / 2;
192  		if (ptr > owned->table[cur]) {
193  			low = cur;
194  		} else if (ptr < owned->table[cur]) {
195  			high = cur;
196  		} else {
197  			/* match found */
198  			if (result) *result = cur;
199  			return 0;
200  		}
201  	}
202  	/* no match found; *result will be the insert-after position */
203  	if (result) *result = low;
204  	return -1;
205  }
206  
207  /*
208   * Initialize the process's __env_owned structure
209   */
210  __private_extern__ int
211  __init__env_owned_locked(int should_set_errno)
212  {
213  	int save;
214  
215  	if (__env_owned) return 0;
216  
217  	if (!should_set_errno)
218  		save = errno;
219  	__env_owned = _owned_ptr_alloc();
220  	if (!__env_owned) {
221  		if (!should_set_errno)
222  			errno = save;
223  		return -1;
224  	}
225  	return 0;
226  }
227  
228  /*
229   * The copy flag may have 3 values:
230   *  1 - make a copy of the name/value pair
231   *  0 - take the name as a user-supplied name=value string
232   * -1 - like 0, except we copy of the name=value string in name
233   */
234  __private_extern__ int
235  __setenv_locked(name, value, rewrite, copy, environp, owned)
236  	const char *name;
237  	const char *value;
238  	int rewrite, copy;
239  	char ***environp;
240  	struct owned_ptr *owned;
241  {
242  	char *c;
243  	int offset;
244  	int oindex;
245  
246  	if ((c = __findenv_locked(name, &offset, *environp))) { /* find if already exists */
247  		char *e;
248  		if (!rewrite)
249  			return (0);
250  		/*
251  		 * In UNIX03, we can overwrite only if we allocated the
252  		 * string.  Then we can realloc it if it is too small.
253  		 */
254  		e = (*environp)[offset];
255  		if (_owned_ptr_search(owned, e, &oindex) == 0) {
256  			if (copy > 0) {
257  				size_t l_value = strlen(value);
258  				if (strlen(c) < l_value) {	/* old smaller; resize*/
259  					char *r;
260  					size_t len = c - e;
261  					if ((r = realloc(e, l_value + len + 1)) == NULL)
262  						return (-1);
263  					if (r != e) {
264  						(*environp)[offset] = r;
265  						c = r + len;
266  						_owned_ptr_delete(owned, oindex);
267  						_owned_ptr_add(owned, r);
268  					}
269  				}
270  				while ( (*c++ = *value++) );
271  				return (0);
272  			}
273  			/* Free up the slot for later use (putenv) */
274  			_owned_ptr_delete(owned, oindex);
275  			free(e);
276  		}
277  	} else {					/* create new slot */
278  		int cnt;
279  		char **p;
280  
281  		for (p = *environp, cnt = 0; *p; ++p, ++cnt);
282  		if (_owned_ptr_search(owned, *environp, &oindex) == 0) {	/* just increase size */
283  			p = (char **)realloc((char *)*environp,
284  			    (size_t)(sizeof(char *) * (cnt + 2)));
285  			if (!p)
286  				return (-1);
287  			if (*environp != p) {
288  				_owned_ptr_delete(owned, oindex);
289  				_owned_ptr_add(owned, p);
290  				*environp = p;
291  			}
292  		}
293  		else {				/* get new space */
294  						/* copy old entries into it */
295  			p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
296  			if (!p)
297  				return (-1);
298  			_owned_ptr_add(owned, p);
299  			bcopy(*environp, p, cnt * sizeof(char *));
300  			*environp = p;
301  		}
302  		(*environp)[cnt + 1] = NULL;
303  		offset = cnt;
304  	}
305  	/* For non Unix03, or UnixO3 setenv(), we make a copy of the user's
306  	 * strings.  For Unix03 putenv(), we put the string directly in
307  	 * the environment. */
308  	if (copy > 0) {
309  		for (c = (char *)name; *c && *c != '='; ++c);	/* no `=' in name */
310  		if (!((*environp)[offset] =			/* name + `=' + value */
311  		    malloc((size_t)((int)(c - name) + strlen(value) + 2))))
312  			return (-1);
313  		_owned_ptr_add(owned, (*environp)[offset]);
314  		for (c = (*environp)[offset]; (*c = *name++) && *c != '='; ++c);
315  		for (*c++ = '='; (*c++ = *value++); );
316  	} else {
317  		/* the legacy behavior copies the string */
318  		if (copy < 0) {
319  			size_t len = strlen(name);
320  			if((c = malloc(len + 1)) == NULL)
321  				return (-1);
322  			_owned_ptr_add(owned, c);
323  			memcpy(c, name, len + 1);
324  			name = c;
325  		}
326  		(*environp)[offset] = (char *)name;
327  	}
328  	return (0);
329  }
330  
331  __private_extern__ void
332  __unsetenv_locked(const char *name, char **environ, struct owned_ptr *owned)
333  {
334  	char **p;
335  	int offset;
336  	int oindex;
337  
338  	while (__findenv_locked(name, &offset, environ)) { /* if set multiple times */
339  		/* if we malloc-ed it, free it first */
340  		if (_owned_ptr_search(owned, environ[offset], &oindex) == 0) {
341  			_owned_ptr_delete(owned, oindex);
342  			free(environ[offset]);
343  		}
344  		for (p = &environ[offset];; ++p)
345  			if (!(*p = *(p + 1)))
346  				break;
347  	}
348  }
349  
350  /****************************************************************************/
351  /*
352   * _allocenvstate -- SPI that creates a new state (opaque)
353   */
354  void *
355  _allocenvstate(void)
356  {
357  	return _owned_ptr_alloc();
358  }
359  
360  /*
361   * _copyenv -- SPI that copies a NULL-tereminated char * array in a newly
362   * allocated buffer, compatible with the other SPI env routines.  If env
363   * is NULL, a char * array composed of a single NULL is returned.  NULL
364   * is returned on error.  (This isn't needed anymore, as __setenv_locked will
365   * automatically make a copy.)
366   */
367  char **
368  _copyenv(char **env)
369  {
370  	char **p;
371  	int cnt = 1;
372  
373  	if (env)
374  		for (p = env; *p; ++p, ++cnt);
375  	p = (char **)malloc((size_t)(sizeof(char *) * cnt));
376  	if (!p)
377  		return (NULL);
378  	if (env)
379  		bcopy(env, p, cnt * sizeof(char *));
380  	else
381  		*p = NULL;
382  	return p;
383  }
384  
385  /*
386   * _deallocenvstate -- SPI that frees all the memory associated with the state
387   * and all allocated strings, including the environment array itself if it
388   * was copied.
389   */
390  int
391  _deallocenvstate(void *state)
392  {
393  	struct owned_ptr *owned;
394  
395  	if (!(owned = (struct owned_ptr *)state) || owned == __env_owned) {
396  		errno = EINVAL;
397  		return -1;
398  	}
399  	_owned_ptr_free(owned);
400  	return 0;
401  }
402  
403  /*
404   * setenvp -- SPI using an arbitrary pointer to string array and an env state,
405   * created by _allocenvstate().  Initial checking is not done.
406   *
407   *	Set the value of the environmental variable "name" to be
408   *	"value".  If rewrite is set, replace any current value.
409   */
410  int
411  _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state)
412  {
413  	__environ_lock();
414  	if (__init__env_owned_locked(1)) {
415  		__environ_unlock();
416  		return (-1);
417  	}
418  	int ret = __setenv_locked(name, value, rewrite, 1, envp,
419  			(state ? (struct owned_ptr *)state : __env_owned));
420  	__environ_unlock();
421  	return ret;
422  }
423  
424  /*
425   * unsetenv(name) -- SPI using an arbitrary pointer to string array and an env
426   * state, created by _allocenvstate().  Initial checking is not done.
427   *
428   *	Delete environmental variable "name".
429   */
430  int
431  _unsetenvp(const char *name, char ***envp, void *state)
432  {
433  	__environ_lock();
434  	if (__init__env_owned_locked(1)) {
435  		__environ_unlock();
436  		return (-1);
437  	}
438  	__unsetenv_locked(name, *envp, (state ? (struct owned_ptr *)state : __env_owned));
439  	__environ_unlock();
440  	return 0;
441  }
442  
443  #endif /* !BUILD_VARIANT */
444  
445  /*
446   * setenv --
447   *	Set the value of the environmental variable "name" to be
448   *	"value".  If rewrite is set, replace any current value.
449   */
450  int
451  setenv(name, value, rewrite)
452  	const char *name;
453  	const char *value;
454  	int rewrite;
455  {
456  	int ret;
457  
458  	/* no null ptr or empty str */
459  	if(name == NULL || *name == 0) {
460  		errno = EINVAL;
461  		return (-1);
462  	}
463  
464  #if __DARWIN_UNIX03
465  	/* no '=' in name */
466  	if (strchr(name, '=')) {
467  		errno = EINVAL;
468  		return (-1);
469  	}
470  #endif /* __DARWIN_UNIX03 */
471  
472  	__environ_lock();
473  	if (__init__env_owned_locked(1)) {
474  		__environ_unlock();
475  		return (-1);
476  	}
477  	ret = __setenv_locked(name, value, rewrite, 1, _NSGetEnviron(), __env_owned);
478  #ifdef LEGACY_CRT1_ENVIRON
479  	_saved_environ = *_NSGetEnviron();
480  #endif /* !LEGACY_CRT1_ENVIRON */
481  	__environ_unlock();
482  
483  	return ret;
484  }
485  
486  static inline __attribute__((always_inline)) int
487  _unsetenv(const char *name, int should_set_errno)
488  {
489  	__environ_lock();
490  	if (__init__env_owned_locked(should_set_errno)) {
491  		__environ_unlock();
492  		return (-1);
493  	}
494  	__unsetenv_locked(name, *_NSGetEnviron(), __env_owned);
495  	__environ_unlock();
496  	return 0;
497  }
498  
499  /*
500   * unsetenv(name) --
501   *	Delete environmental variable "name".
502   */
503  #if __DARWIN_UNIX03
504  int
505  unsetenv(const char *name)
506  {
507  	/* no null ptr or empty str */
508  	if(name == NULL || *name == 0) {
509  		errno = EINVAL;
510  		return (-1);
511  	}
512  
513  	/* no '=' in name */
514  	if (strchr(name, '=')) {
515  		errno = EINVAL;
516  		return (-1);
517  	}
518  	return _unsetenv(name, 1);
519  }
520  #else /* !__DARWIN_UNIX03 */
521  void
522  unsetenv(const char *name)
523  {
524  	/* no null ptr or empty str */
525  	if(name == NULL || *name == 0)
526  		return;
527  	_unsetenv(name, 0);
528  }
529  #endif /* __DARWIN_UNIX03 */