diff --git a/include/stdlib.h b/include/stdlib.h index ca96c06b83b7..bf1a612190ee 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -1,366 +1,368 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)stdlib.h 8.5 (Berkeley) 5/19/95 * $FreeBSD$ */ #ifndef _STDLIB_H_ #define _STDLIB_H_ #include #include #include __NULLABILITY_PRAGMA_PUSH #if __BSD_VISIBLE #ifndef _RUNE_T_DECLARED typedef __rune_t rune_t; #define _RUNE_T_DECLARED #endif #endif #ifndef _SIZE_T_DECLARED typedef __size_t size_t; #define _SIZE_T_DECLARED #endif #ifndef __cplusplus #ifndef _WCHAR_T_DECLARED typedef ___wchar_t wchar_t; #define _WCHAR_T_DECLARED #endif #endif typedef struct { int quot; /* quotient */ int rem; /* remainder */ } div_t; typedef struct { long quot; long rem; } ldiv_t; #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 /* * I.e., INT_MAX; rand(3) returns a signed integer but must produce output in * the range [0, RAND_MAX], so half of the possible output range is unused. */ #define RAND_MAX 0x7fffffff __BEGIN_DECLS #ifdef _XLOCALE_H_ #include #endif extern int __mb_cur_max; extern int ___mb_cur_max(void); #define MB_CUR_MAX ((size_t)___mb_cur_max()) _Noreturn void abort(void); int abs(int) __pure2; int atexit(void (* _Nonnull)(void)); double atof(const char *); int atoi(const char *); long atol(const char *); void *bsearch(const void *, const void *, size_t, size_t, int (*)(const void * _Nonnull, const void *)); void *calloc(size_t, size_t) __malloc_like __result_use_check __alloc_size2(1, 2); div_t div(int, int) __pure2; _Noreturn void exit(int); void free(void *); char *getenv(const char *); long labs(long) __pure2; ldiv_t ldiv(long, long) __pure2; void *malloc(size_t) __malloc_like __result_use_check __alloc_size(1); int mblen(const char *, size_t); size_t mbstowcs(wchar_t * __restrict , const char * __restrict, size_t); int mbtowc(wchar_t * __restrict, const char * __restrict, size_t); void qsort(void *, size_t, size_t, int (* _Nonnull)(const void *, const void *)); int rand(void); void *realloc(void *, size_t) __result_use_check __alloc_size(2); void srand(unsigned); double strtod(const char * __restrict, char ** __restrict); float strtof(const char * __restrict, char ** __restrict); long strtol(const char * __restrict, char ** __restrict, int); long double strtold(const char * __restrict, char ** __restrict); unsigned long strtoul(const char * __restrict, char ** __restrict, int); int system(const char *); int wctomb(char *, wchar_t); size_t wcstombs(char * __restrict, const wchar_t * __restrict, size_t); /* * Functions added in C99 which we make conditionally available in the * BSD^C89 namespace if the compiler supports `long long'. * The #if test is more complicated than it ought to be because * __BSD_VISIBLE implies __ISO_C_VISIBLE == 1999 *even if* `long long' * is not supported in the compilation environment (which therefore means * that it can't really be ISO C99). * * (The only other extension made by C99 in thie header is _Exit().) */ #if __ISO_C_VISIBLE >= 1999 || defined(__cplusplus) #ifdef __LONG_LONG_SUPPORTED /* LONGLONG */ typedef struct { long long quot; long long rem; } lldiv_t; /* LONGLONG */ long long atoll(const char *); /* LONGLONG */ long long llabs(long long) __pure2; /* LONGLONG */ lldiv_t lldiv(long long, long long) __pure2; /* LONGLONG */ long long strtoll(const char * __restrict, char ** __restrict, int); /* LONGLONG */ unsigned long long strtoull(const char * __restrict, char ** __restrict, int); #endif /* __LONG_LONG_SUPPORTED */ _Noreturn void _Exit(int); #endif /* __ISO_C_VISIBLE >= 1999 */ /* * If we're in a mode greater than C99, expose C11 functions. */ #if __ISO_C_VISIBLE >= 2011 || __cplusplus >= 201103L void * aligned_alloc(size_t, size_t) __malloc_like __alloc_align(1) __alloc_size(2); int at_quick_exit(void (*)(void)); _Noreturn void quick_exit(int); #endif /* __ISO_C_VISIBLE >= 2011 */ /* * Extensions made by POSIX relative to C. */ #if __POSIX_VISIBLE >= 199506 || __XSI_VISIBLE char *realpath(const char * __restrict, char * __restrict); #endif #if __POSIX_VISIBLE >= 199506 int rand_r(unsigned *); /* (TSF) */ #endif #if __POSIX_VISIBLE >= 200112 int posix_memalign(void **, size_t, size_t); /* (ADV) */ int setenv(const char *, const char *, int); int unsetenv(const char *); #endif #if __POSIX_VISIBLE >= 200809 || __XSI_VISIBLE int getsubopt(char **, char *const *, char **); #ifndef _MKDTEMP_DECLARED char *mkdtemp(char *); #define _MKDTEMP_DECLARED #endif #ifndef _MKSTEMP_DECLARED int mkstemp(char *); #define _MKSTEMP_DECLARED #endif #endif /* __POSIX_VISIBLE >= 200809 || __XSI_VISIBLE */ /* * The only changes to the XSI namespace in revision 6 were the deletion * of the ttyslot() and valloc() functions, which FreeBSD never declared * in this header. For revision 7, ecvt(), fcvt(), and gcvt(), which * FreeBSD also does not have, and mktemp(), are to be deleted. */ #if __XSI_VISIBLE /* XXX XSI requires pollution from here. We'd rather not. */ long a64l(const char *); double drand48(void); /* char *ecvt(double, int, int * __restrict, int * __restrict); */ double erand48(unsigned short[3]); /* char *fcvt(double, int, int * __restrict, int * __restrict); */ /* char *gcvt(double, int, int * __restrict, int * __restrict); */ char *initstate(unsigned int, char *, size_t); long jrand48(unsigned short[3]); char *l64a(long); void lcong48(unsigned short[7]); long lrand48(void); #if !defined(_MKTEMP_DECLARED) && (__BSD_VISIBLE || __XSI_VISIBLE <= 600) char *mktemp(char *); #define _MKTEMP_DECLARED #endif long mrand48(void); long nrand48(unsigned short[3]); int putenv(char *); long random(void); unsigned short *seed48(unsigned short[3]); char *setstate(/* const */ char *); void srand48(long); void srandom(unsigned int); #endif /* __XSI_VISIBLE */ #if __XSI_VISIBLE int grantpt(int); int posix_openpt(int); char *ptsname(int); int unlockpt(int); #endif /* __XSI_VISIBLE */ #if __BSD_VISIBLE /* ptsname_r will be included in POSIX issue 8 */ int ptsname_r(int, char *, size_t); #endif #if __BSD_VISIBLE extern const char *malloc_conf; extern void (*malloc_message)(void *, const char *); /* * The alloca() function can't be implemented in C, and on some * platforms it can't be implemented at all as a callable function. * The GNU C compiler provides a built-in alloca() which we can use. * On platforms where alloca() is not in libc, programs which use it * will fail to link when compiled with non-GNU compilers. */ #if __GNUC__ >= 2 #undef alloca /* some GNU bits try to get cute and define this on their own */ #define alloca(sz) __builtin_alloca(sz) #endif void abort2(const char *, int, void **) __dead2; __uint32_t arc4random(void); void arc4random_buf(void *, size_t); __uint32_t arc4random_uniform(__uint32_t); #ifdef __BLOCKS__ int atexit_b(void (^ _Nonnull)(void)); void *bsearch_b(const void *, const void *, size_t, size_t, int (^ _Nonnull)(const void *, const void *)); #endif char *getbsize(int *, long *); /* getcap(3) functions */ char *cgetcap(char *, const char *, int); int cgetclose(void); int cgetent(char **, char **, const char *); int cgetfirst(char **, char **); int cgetmatch(const char *, const char *); int cgetnext(char **, char **); int cgetnum(char *, const char *, long *); int cgetset(const char *); int cgetstr(char *, const char *, char **); int cgetustr(char *, const char *, char **); +int clearenv(void); + int daemon(int, int); int daemonfd(int, int); char *devname(__dev_t, __mode_t); char *devname_r(__dev_t, __mode_t, char *, int); char *fdevname(int); char *fdevname_r(int, char *, int); int getloadavg(double [], int); const char * getprogname(void); int heapsort(void *, size_t, size_t, int (* _Nonnull)(const void *, const void *)); #ifdef __BLOCKS__ int heapsort_b(void *, size_t, size_t, int (^ _Nonnull)(const void *, const void *)); void qsort_b(void *, size_t, size_t, int (^ _Nonnull)(const void *, const void *)); #endif int l64a_r(long, char *, int); int mergesort(void *, size_t, size_t, int (*)(const void *, const void *)); #ifdef __BLOCKS__ int mergesort_b(void *, size_t, size_t, int (^)(const void *, const void *)); #endif int mkostemp(char *, int); int mkostemps(char *, int, int); int mkostempsat(int, char *, int, int); void qsort_r(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *)); int radixsort(const unsigned char **, int, const unsigned char *, unsigned); void *reallocarray(void *, size_t, size_t) __result_use_check __alloc_size2(2, 3); void *reallocf(void *, size_t) __result_use_check __alloc_size(2); int rpmatch(const char *); void setprogname(const char *); int sradixsort(const unsigned char **, int, const unsigned char *, unsigned); void srandomdev(void); long long strtonum(const char *, long long, long long, const char **); /* Deprecated interfaces, to be removed. */ __int64_t strtoq(const char *, char **, int); __uint64_t strtouq(const char *, char **, int); extern char *suboptarg; /* getsubopt(3) external variable */ #endif /* __BSD_VISIBLE */ #if __EXT1_VISIBLE #ifndef _RSIZE_T_DEFINED #define _RSIZE_T_DEFINED typedef size_t rsize_t; #endif #ifndef _ERRNO_T_DEFINED #define _ERRNO_T_DEFINED typedef int errno_t; #endif /* K.3.6 */ typedef void (*constraint_handler_t)(const char * __restrict, void * __restrict, errno_t); /* K.3.6.1.1 */ constraint_handler_t set_constraint_handler_s(constraint_handler_t handler); /* K.3.6.1.2 */ _Noreturn void abort_handler_s(const char * __restrict, void * __restrict, errno_t); /* K3.6.1.3 */ void ignore_handler_s(const char * __restrict, void * __restrict, errno_t); /* K.3.6.3.2 */ errno_t qsort_s(void *, rsize_t, rsize_t, int (*)(const void *, const void *, void *), void *); #endif /* __EXT1_VISIBLE */ __END_DECLS __NULLABILITY_PRAGMA_POP #endif /* !_STDLIB_H_ */ diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index 361761e73fdd..a658fd78e862 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -1,68 +1,69 @@ # from @(#)Makefile.inc 8.3 (Berkeley) 2/4/95 # $FreeBSD$ # machine-independent stdlib sources .PATH: ${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib ${LIBC_SRCTOP}/stdlib MISRCS+=C99_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \ bsearch.c \ cxa_thread_atexit.c cxa_thread_atexit_impl.c \ div.c exit.c getenv.c getopt.c getopt_long.c \ getsubopt.c hcreate.c hcreate_r.c hdestroy_r.c heapsort.c heapsort_b.c \ hsearch_r.c imaxabs.c imaxdiv.c \ insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \ merge.c mergesort_b.c ptsname.c qsort.c qsort_r.c qsort_s.c \ quick_exit.c radixsort.c rand.c \ random.c reallocarray.c reallocf.c realpath.c remque.c \ set_constraint_handler_s.c strfmon.c strtoimax.c \ strtol.c strtold.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c \ strtoumax.c strtouq.c system.c tdelete.c tfind.c tsearch.c twalk.c CFLAGS.qsort.c+= -Wsign-compare # Work around an issue on case-insensitive file systems. # libc has both _Exit.c and _exit.s and they both yield # _exit.o (case insensitively speaking). CLEANFILES+=C99_Exit.c C99_Exit.c: ${LIBC_SRCTOP}/stdlib/_Exit.c .NOMETA ln -sf ${.ALLSRC} ${.TARGET} SYM_MAPS+= ${LIBC_SRCTOP}/stdlib/Symbol.map # machine-dependent stdlib sources .sinclude "${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib/Makefile.inc" MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 \ atoi.3 atol.3 at_quick_exit.3 bsearch.3 \ div.3 exit.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 \ hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 llabs.3 lldiv.3 \ lsearch.3 memory.3 ptsname.3 qsort.3 \ quick_exit.3 \ radixsort.3 rand.3 random.3 reallocarray.3 reallocf.3 realpath.3 \ set_constraint_handler_s.3 \ strfmon.3 strtod.3 strtol.3 strtonum.3 strtoul.3 system.3 \ tsearch.3 MLINKS+=a64l.3 l64a.3 a64l.3 l64a_r.3 MLINKS+=atol.3 atoll.3 MLINKS+=exit.3 _Exit.3 -MLINKS+=getenv.3 putenv.3 getenv.3 setenv.3 getenv.3 unsetenv.3 +MLINKS+=getenv.3 clearenv.3 getenv.3 putenv.3 getenv.3 setenv.3 \ + getenv.3 unsetenv.3 MLINKS+=getopt_long.3 getopt_long_only.3 MLINKS+=hcreate.3 hdestroy.3 hcreate.3 hsearch.3 MLINKS+=hcreate.3 hcreate_r.3 hcreate.3 hdestroy_r.3 hcreate.3 hsearch_r.3 MLINKS+=insque.3 remque.3 MLINKS+=lsearch.3 lfind.3 MLINKS+=ptsname.3 grantpt.3 ptsname.3 ptsname_r.3 ptsname.3 unlockpt.3 MLINKS+=qsort.3 heapsort.3 qsort.3 mergesort.3 qsort.3 qsort_r.3 \ qsort.3 qsort_s.3 MLINKS+=rand.3 rand_r.3 rand.3 srand.3 MLINKS+=random.3 initstate.3 random.3 setstate.3 random.3 srandom.3 \ random.3 srandomdev.3 MLINKS+=radixsort.3 sradixsort.3 MLINKS+=set_constraint_handler_s.3 abort_handler_s.3 MLINKS+=set_constraint_handler_s.3 ignore_handler_s.3 MLINKS+=strfmon.3 strfmon_l.3 MLINKS+=strtod.3 strtof.3 strtod.3 strtold.3 MLINKS+=strtol.3 strtoll.3 strtol.3 strtoq.3 strtol.3 strtoimax.3 MLINKS+=strtoul.3 strtoull.3 strtoul.3 strtouq.3 strtoul.3 strtoumax.3 MLINKS+=tsearch.3 tdelete.3 tsearch.3 tfind.3 tsearch.3 twalk.3 diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map index 2be23390e333..6524c6097b96 100644 --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -1,137 +1,141 @@ /* * $FreeBSD$ */ FBSD_1.0 { _Exit; a64l; abort; abs; atexit; __cxa_atexit; __cxa_finalize; atof; atoi; atol; atoll; bsearch; div; __isthreaded; exit; getenv; opterr; optind; optopt; optreset; optarg; getopt; getopt_long; getopt_long_only; suboptarg; getsubopt; grantpt; ptsname; unlockpt; hcreate; hdestroy; hsearch; heapsort; imaxabs; imaxdiv; insque; l64a; l64a_r; labs; ldiv; llabs; lldiv; lsearch; lfind; mergesort; putenv; qsort_r; qsort; radixsort; sradixsort; rand_r; srandom; srandomdev; initstate; setstate; random; reallocf; realpath; remque; setenv; unsetenv; strfmon; strtoimax; strtol; strtoll; strtonum; strtoq; strtoul; strtoull; strtoumax; strtouq; system; tdelete; tfind; tsearch; twalk; }; FBSD_1.3 { at_quick_exit; atof_l; atoi_l; atol_l; atoll_l; quick_exit; strtod_l; strtof_l; strtoimax_l; strtol_l; strtold_l; strtoll_l; strtoq_l; strtoul_l; strtoull_l; strtoumax_l; strtouq_l; }; FBSD_1.4 { atexit_b; bsearch_b; heapsort_b; mergesort_b; qsort_b; hcreate_r; hdestroy_r; hsearch_r; reallocarray; }; FBSD_1.5 { __cxa_thread_atexit; __cxa_thread_atexit_impl; abort_handler_s; ignore_handler_s; set_constraint_handler_s; }; FBSD_1.6 { ptsname_r; qsort_s; rand; srand; }; +FBSD_1.7 { + clearenv; +}; + FBSDprivate_1.0 { __system; _system; __libc_system; __cxa_thread_call_dtors; __libc_atexit; }; diff --git a/lib/libc/stdlib/getenv.3 b/lib/libc/stdlib/getenv.3 index e662b86d214b..5566d7b01dcd 100644 --- a/lib/libc/stdlib/getenv.3 +++ b/lib/libc/stdlib/getenv.3 @@ -1,230 +1,246 @@ .\" Copyright (c) 1988, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the American National Standards Committee X3, on Information .\" Processing Systems. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)getenv.3 8.2 (Berkeley) 12/11/93 .\" $FreeBSD$ .\" -.Dd June 20, 2007 +.Dd November 7, 2021 .Dt GETENV 3 .Os .Sh NAME +.Nm clearenv , .Nm getenv , .Nm putenv , .Nm setenv , .Nm unsetenv .Nd environment variable functions .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In stdlib.h +.Ft int +.Fn clearenv "void" .Ft char * .Fn getenv "const char *name" .Ft int .Fn setenv "const char *name" "const char *value" "int overwrite" .Ft int .Fn putenv "char *string" .Ft int .Fn unsetenv "const char *name" .Sh DESCRIPTION These functions set, unset and fetch environment variables from the host .Em environment list . .Pp The +.Fn clearenv +function clears all environment variables. +New variables can be added using +.Fn setenv +and +.Fn putenv . +.Pp +The .Fn getenv function obtains the current value of the environment variable, .Fa name . The application should not modify the string pointed to by the .Fn getenv function. .Pp The .Fn setenv function inserts or resets the environment variable .Fa name in the current environment list. If the variable .Fa name does not exist in the list, it is inserted with the given .Fa value . If the variable does exist, the argument .Fa overwrite is tested; if .Fa overwrite is zero, the variable is not reset, otherwise it is reset to the given .Fa value . .Pp The .Fn putenv function takes an argument of the form ``name=value'' and puts it directly into the current environment, so altering the argument shall change the environment. If the variable .Fa name does not exist in the list, it is inserted with the given .Fa value . If the variable .Fa name does exist, it is reset to the given .Fa value . .Pp The .Fn unsetenv function deletes all instances of the variable name pointed to by .Fa name from the list. .Pp If corruption (e.g., a name without a value) is detected while making a copy of environ for internal usage, then .Fn setenv , .Fn unsetenv and .Fn putenv will output a warning to stderr about the issue, drop the corrupt entry and complete the task without error. .Sh RETURN VALUES The .Fn getenv function returns the value of the environment variable as a .Dv NUL Ns -terminated string. If the variable .Fa name is not in the current environment, .Dv NULL is returned. .Pp -.Rv -std setenv putenv unsetenv +.Rv -std clearenv setenv putenv unsetenv .Sh ERRORS .Bl -tag -width Er .It Bq Er EINVAL The function .Fn getenv , .Fn setenv or .Fn unsetenv failed because the .Fa name is a .Dv NULL pointer, points to an empty string, or points to a string containing an .Dq Li \&= character. .Pp The function .Fn putenv failed because .Fa string is a .Dv NULL pointer, .Fa string is without an .Dq Li \&= character or .Dq Li \&= is the first character in .Fa string . This does not follow the .Tn POSIX specification. .It Bq Er ENOMEM The function .Fn setenv , .Fn unsetenv or .Fn putenv failed because they were unable to allocate memory for the environment. .El .Sh SEE ALSO .Xr csh 1 , .Xr sh 1 , .Xr execve 2 , .Xr environ 7 .Sh STANDARDS The .Fn getenv function conforms to .St -isoC . The .Fn setenv , .Fn putenv and .Fn unsetenv functions conforms to .St -p1003.1-2001 . .Sh HISTORY The functions .Fn setenv and .Fn unsetenv appeared in .At v7 . The .Fn putenv function appeared in .Bx 4.3 Reno . .Pp Until .Fx 7.0 , .Fn putenv would make a copy of .Fa string and insert it into the environment using .Fn setenv . This was changed to use .Fa string as the memory location of the ``name=value'' pair to follow the .Tn POSIX specification. +.Pp +The +.Fn clearenv +was added in +.Fx 14 . .Sh BUGS Successive calls to .Fn setenv that assign a larger-sized .Fa value than any previous value to the same .Fa name will result in a memory leak. The .Fx semantics for this function (namely, that the contents of .Fa value are copied and that old values remain accessible indefinitely) make this bug unavoidable. Future versions may eliminate one or both of these semantic guarantees in order to fix the bug. diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index 5d445e18d93b..bf79cb6b0d79 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -1,693 +1,718 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2007-2009 Sean C. Farley * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include "un-namespace.h" static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; static const char CorruptEnvValueMsg[] = "environment corrupt; missing value for "; /* * Standard environ. environ variable is exposed to entire process. * * origEnviron: Upon cleanup on unloading of library or failure, this * allows environ to return to as it was before. * environSize: Number of variables environ can hold. Can only * increase. * intEnviron: Internally-built environ. Exposed via environ during * (re)builds of the environment. */ extern char **environ; static char **origEnviron; static char **intEnviron = NULL; static int environSize = 0; /* * Array of environment variables built from environ. Each element records: * name: Pointer to name=value string * name length: Length of name not counting '=' character * value: Pointer to value within same string as name * value size: Size (not length) of space for value not counting the * nul character * active state: true/false value to signify whether variable is active. * Useful since multiple variables with the same name can * co-exist. At most, one variable can be active at any * one time. * putenv: Created from putenv() call. This memory must not be * reused. */ static struct envVars { size_t nameLen; size_t valueSize; char *name; char *value; bool active; bool putenv; } *envVars = NULL; /* * Environment array information. * * envActive: Number of active variables in array. * envVarsSize: Size of array. * envVarsTotal: Number of total variables in array (active or not). */ static int envActive = 0; static int envVarsSize = 0; static int envVarsTotal = 0; /* Deinitialization of new environment. */ static void __attribute__ ((destructor)) __clean_env_destructor(void); /* * A simple version of warnx() to avoid the bloat of including stdio in static * binaries. */ static void __env_warnx(const char *msg, const char *name, size_t nameLen) { static const char nl[] = "\n"; static const char progSep[] = ": "; _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); _write(STDERR_FILENO, msg, strlen(msg)); _write(STDERR_FILENO, name, nameLen); _write(STDERR_FILENO, nl, sizeof(nl) - 1); return; } /* * Inline strlen() for performance. Also, perform check for an equals sign. * Cheaper here than peforming a strchr() later. */ static inline size_t __strleneq(const char *str) { const char *s; for (s = str; *s != '\0'; ++s) if (*s == '=') return (0); return (s - str); } /* * Comparison of an environment name=value to a name. */ static inline bool strncmpeq(const char *nameValue, const char *name, size_t nameLen) { if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') return (true); return (false); } /* * Using environment, returns pointer to value associated with name, if any, * else NULL. If the onlyActive flag is set to true, only variables that are * active are returned else all are. */ static inline char * __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) { int ndx; /* * Find environment variable from end of array (more likely to be * active). A variable created by putenv is always active, or it is not * tracked in the array. */ for (ndx = *envNdx; ndx >= 0; ndx--) if (envVars[ndx].putenv) { if (strncmpeq(envVars[ndx].name, name, nameLen)) { *envNdx = ndx; return (envVars[ndx].name + nameLen + sizeof ("=") - 1); } } else if ((!onlyActive || envVars[ndx].active) && (envVars[ndx].nameLen == nameLen && strncmpeq(envVars[ndx].name, name, nameLen))) { *envNdx = ndx; return (envVars[ndx].value); } return (NULL); } /* * Using environ, returns pointer to value associated with name, if any, else * NULL. Used on the original environ passed into the program. */ static char * __findenv_environ(const char *name, size_t nameLen) { int envNdx; /* Find variable within environ. */ for (envNdx = 0; environ[envNdx] != NULL; envNdx++) if (strncmpeq(environ[envNdx], name, nameLen)) return (&(environ[envNdx][nameLen + sizeof("=") - 1])); return (NULL); } /* * Remove variable added by putenv() from variable tracking array. */ static void __remove_putenv(int envNdx) { envVarsTotal--; if (envVarsTotal > envNdx) memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), (envVarsTotal - envNdx) * sizeof (*envVars)); memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); return; } /* * Deallocate the environment built from environ as well as environ then set * both to NULL. Eases debugging of memory leaks. */ static void __clean_env(bool freeVars) { int envNdx; /* Deallocate environment and environ if created by *env(). */ if (envVars != NULL) { for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) /* Free variables or deactivate them. */ if (envVars[envNdx].putenv) { if (!freeVars) __remove_putenv(envNdx); } else { if (freeVars) free(envVars[envNdx].name); else envVars[envNdx].active = false; } if (freeVars) { free(envVars); envVars = NULL; } else envActive = 0; /* Restore original environ if it has not updated by program. */ if (origEnviron != NULL) { if (environ == intEnviron) environ = origEnviron; free(intEnviron); intEnviron = NULL; environSize = 0; } } return; } /* * Using the environment, rebuild the environ array for use by other C library * calls that depend upon it. */ static int __rebuild_environ(int newEnvironSize) { char **tmpEnviron; int envNdx; int environNdx; int tmpEnvironSize; /* Resize environ. */ if (newEnvironSize > environSize) { tmpEnvironSize = newEnvironSize * 2; tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1, sizeof(*intEnviron)); if (tmpEnviron == NULL) return (-1); environSize = tmpEnvironSize; intEnviron = tmpEnviron; } envActive = newEnvironSize; /* Assign active variables to environ. */ for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) if (envVars[envNdx].active) intEnviron[environNdx++] = envVars[envNdx].name; intEnviron[environNdx] = NULL; /* Always set environ which may have been replaced by program. */ environ = intEnviron; return (0); } /* * Enlarge new environment. */ static inline bool __enlarge_env(void) { int newEnvVarsSize; struct envVars *tmpEnvVars; envVarsTotal++; if (envVarsTotal > envVarsSize) { newEnvVarsSize = envVarsTotal * 2; tmpEnvVars = reallocarray(envVars, newEnvVarsSize, sizeof(*envVars)); if (tmpEnvVars == NULL) { envVarsTotal--; return (false); } envVarsSize = newEnvVarsSize; envVars = tmpEnvVars; } return (true); } /* * Using environ, build an environment for use by standard C library calls. */ static int __build_env(void) { char **env; int activeNdx; int envNdx; int savedErrno; size_t nameLen; /* Check for non-existant environment. */ if (environ == NULL || environ[0] == NULL) return (0); /* Count environment variables. */ for (env = environ, envVarsTotal = 0; *env != NULL; env++) envVarsTotal++; envVarsSize = envVarsTotal * 2; /* Create new environment. */ envVars = calloc(envVarsSize, sizeof(*envVars)); if (envVars == NULL) goto Failure; /* Copy environ values and keep track of them. */ for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { envVars[envNdx].putenv = false; envVars[envNdx].name = strdup(environ[envVarsTotal - envNdx - 1]); if (envVars[envNdx].name == NULL) goto Failure; envVars[envNdx].value = strchr(envVars[envNdx].name, '='); if (envVars[envNdx].value != NULL) { envVars[envNdx].value++; envVars[envNdx].valueSize = strlen(envVars[envNdx].value); } else { __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, strlen(envVars[envNdx].name)); errno = EFAULT; goto Failure; } /* * Find most current version of variable to make active. This * will prevent multiple active variables from being created * during this initialization phase. */ nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; envVars[envNdx].nameLen = nameLen; activeNdx = envVarsTotal - 1; if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, false) == NULL) { __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, nameLen); errno = EFAULT; goto Failure; } envVars[activeNdx].active = true; } /* Create a new environ. */ origEnviron = environ; environ = NULL; if (__rebuild_environ(envVarsTotal) == 0) return (0); Failure: savedErrno = errno; __clean_env(true); errno = savedErrno; return (-1); } /* * Destructor function with default argument to __clean_env(). */ static void __clean_env_destructor(void) { __clean_env(true); return; } /* * Returns the value of a variable or NULL if none are found. */ char * getenv(const char *name) { int envNdx; size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (NULL); } /* * Variable search order: * 1. Check for an empty environ. This allows an application to clear * the environment. * 2. Search the external environ array. * 3. Search the internal environment. * * Since malloc() depends upon getenv(), getenv() must never cause the * internal environment storage to be generated. */ if (environ == NULL || environ[0] == NULL) return (NULL); else if (envVars == NULL || environ != intEnviron) return (__findenv_environ(name, nameLen)); else { envNdx = envVarsTotal - 1; return (__findenv(name, nameLen, &envNdx, true)); } } /* * Set the value of a variable. Older settings are labeled as inactive. If an * older setting has enough room to store the new value, it will be reused. No * previous variables are ever freed here to avoid causing a segmentation fault * in a user's code. * * The variables nameLen and valueLen are passed into here to allow the caller * to calculate the length by means besides just strlen(). */ static int __setenv(const char *name, size_t nameLen, const char *value, int overwrite) { bool reuse; char *env; int envNdx; int newEnvActive; size_t valueLen; /* Find existing environment variable large enough to use. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; valueLen = strlen(value); reuse = false; if (__findenv(name, nameLen, &envNdx, false) != NULL) { /* Deactivate entry if overwrite is allowed. */ if (envVars[envNdx].active) { if (overwrite == 0) return (0); envVars[envNdx].active = false; newEnvActive--; } /* putenv() created variable cannot be reused. */ if (envVars[envNdx].putenv) __remove_putenv(envNdx); /* Entry is large enough to reuse. */ else if (envVars[envNdx].valueSize >= valueLen) reuse = true; } /* Create new variable if none was found of sufficient size. */ if (! reuse) { /* Enlarge environment. */ envNdx = envVarsTotal; if (!__enlarge_env()) return (-1); /* Create environment entry. */ envVars[envNdx].name = malloc(nameLen + sizeof ("=") + valueLen); if (envVars[envNdx].name == NULL) { envVarsTotal--; return (-1); } envVars[envNdx].nameLen = nameLen; envVars[envNdx].valueSize = valueLen; /* Save name of name/value pair. */ env = stpncpy(envVars[envNdx].name, name, nameLen); *env++ = '='; } else env = envVars[envNdx].value; /* Save value of name/value pair. */ strcpy(env, value); envVars[envNdx].value = env; envVars[envNdx].active = true; newEnvActive++; /* No need to rebuild environ if an active variable was reused. */ if (reuse && newEnvActive == envActive) return (0); else return (__rebuild_environ(newEnvActive)); } /* * If the program attempts to replace the array of environment variables * (environ) environ or sets the first varible to NULL, then deactivate all * variables and merge in the new list from environ. */ static int __merge_environ(void) { char **env; char *equals; /* * Internally-built environ has been replaced or cleared (detected by * using the count of active variables against a NULL as the first value * in environ). Clean up everything. */ if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && environ[0] == NULL))) { /* Deactivate all environment variables. */ if (envActive > 0) { origEnviron = NULL; __clean_env(false); } /* * Insert new environ into existing, yet deactivated, * environment array. */ origEnviron = environ; if (origEnviron != NULL) for (env = origEnviron; *env != NULL; env++) { if ((equals = strchr(*env, '=')) == NULL) { __env_warnx(CorruptEnvValueMsg, *env, strlen(*env)); errno = EFAULT; return (-1); } if (__setenv(*env, equals - *env, equals + 1, 1) == -1) return (-1); } } return (0); } /* * The exposed setenv() that peforms a few tests before calling the function * (__setenv()) that does the actual work of inserting a variable into the * environment. */ int setenv(const char *name, const char *value, int overwrite) { size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); return (__setenv(name, nameLen, value, overwrite)); } /* * Insert a "name=value" string into the environment. Special settings must be * made to keep setenv() from reusing this memory block and unsetenv() from * allowing it to be tracked. */ int putenv(char *string) { char *equals; int envNdx; int newEnvActive; size_t nameLen; /* Check for malformed argument. */ if (string == NULL || (equals = strchr(string, '=')) == NULL || (nameLen = equals - string) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate previous environment variable. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; if (__findenv(string, nameLen, &envNdx, true) != NULL) { /* Reuse previous putenv slot. */ if (envVars[envNdx].putenv) { envVars[envNdx].name = string; return (__rebuild_environ(envActive)); } else { newEnvActive--; envVars[envNdx].active = false; } } /* Enlarge environment. */ envNdx = envVarsTotal; if (!__enlarge_env()) return (-1); /* Create environment entry. */ envVars[envNdx].name = string; envVars[envNdx].nameLen = -1; envVars[envNdx].value = NULL; envVars[envNdx].valueSize = -1; envVars[envNdx].putenv = true; envVars[envNdx].active = true; newEnvActive++; return (__rebuild_environ(newEnvActive)); } /* * Unset variable with the same name by flagging it as inactive. No variable is * ever freed. */ int unsetenv(const char *name) { int envNdx; size_t nameLen; int newEnvActive; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate specified variable. */ /* Remove all occurrences. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; while (__findenv(name, nameLen, &envNdx, true) != NULL) { envVars[envNdx].active = false; if (envVars[envNdx].putenv) __remove_putenv(envNdx); envNdx--; newEnvActive--; } if (newEnvActive != envActive) __rebuild_environ(newEnvActive); return (0); } + +/* + * Unset all variable by flagging them as inactive. No variable is + * ever freed. + */ +int +clearenv(void) +{ + int ndx; + + /* Initialize environment. */ + if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) + return (-1); + + /* Remove from the end to not shuffle memory too much. */ + for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) { + envVars[ndx].active = false; + if (envVars[ndx].putenv) + __remove_putenv(ndx); + } + + __rebuild_environ(0); + + return (0); +} diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile index bd85efcb1b95..ffba83443a9e 100644 --- a/lib/libc/tests/stdlib/Makefile +++ b/lib/libc/tests/stdlib/Makefile @@ -1,72 +1,73 @@ # $FreeBSD$ .include +ATF_TESTS_C+= clearenv_test ATF_TESTS_C+= dynthr_test ATF_TESTS_C+= heapsort_test ATF_TESTS_C+= mergesort_test ATF_TESTS_C+= qsort_test ATF_TESTS_C+= qsort_r_test ATF_TESTS_C+= qsort_s_test ATF_TESTS_C+= set_constraint_handler_s_test ATF_TESTS_C+= strfmon_test ATF_TESTS_C+= tsearch_test ATF_TESTS_CXX+= cxa_thread_atexit_test ATF_TESTS_CXX+= cxa_thread_atexit_nothr_test # All architectures on FreeBSD have fenv.h CFLAGS+= -D__HAVE_FENV # Define __HAVE_LONG_DOUBLE for architectures whose long double has greater # precision than their double. .if ${MACHINE_CPUARCH} == "aarch64" || \ ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "riscv" CFLAGS+= -D__HAVE_LONG_DOUBLE .endif # TODO: t_getenv_thread, t_mi_vector_hash, t_strtoi NETBSD_ATF_TESTS_C+= abs_test NETBSD_ATF_TESTS_C+= atoi_test NETBSD_ATF_TESTS_C+= div_test NETBSD_ATF_TESTS_C+= getenv_test NETBSD_ATF_TESTS_C+= exit_test NETBSD_ATF_TESTS_C+= hsearch_test NETBSD_ATF_TESTS_C+= posix_memalign_test NETBSD_ATF_TESTS_C+= random_test NETBSD_ATF_TESTS_C+= strtod_test NETBSD_ATF_TESTS_C+= strtol_test NETBSD_ATF_TESTS_C+= system_test # TODO: need to come up with a correct explanation of what the patch pho does # with h_atexit #ATF_TESTS_SH= atexit_test NETBSD_ATF_TESTS_SH= getopt_test .include "../Makefile.netbsd-tests" BINDIR= ${TESTSDIR} # TODO: see comment above #PROGS+= h_atexit PROGS+= h_getopt h_getopt_long CFLAGS+= -I${.CURDIR} CXXSTD.cxa_thread_atexit_test= c++11 CXXSTD.cxa_thread_atexit_nothr_test= c++11 LIBADD.cxa_thread_atexit_test+= pthread .for t in h_getopt h_getopt_long CFLAGS.$t+= -I${LIBNETBSD_SRCDIR} -I${SRCTOP}/contrib/netbsd-tests LDFLAGS.$t+= -L${LIBNETBSD_OBJDIR} LIBADD.${t}+= netbsd util .endfor LIBADD.strtod_test+= m SUBDIR+= dynthr_mod .include diff --git a/lib/libc/tests/stdlib/clearenv_test.c b/lib/libc/tests/stdlib/clearenv_test.c new file mode 100644 index 000000000000..0c705bfece1e --- /dev/null +++ b/lib/libc/tests/stdlib/clearenv_test.c @@ -0,0 +1,176 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2021 Mariusz Zaborski + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Test for clearenv(3) routine. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include + +#define TEST_VARIABLE "TEST_VAR" +#define TEST_SYSTEM_VARIABLE "PWD" + +extern char **environ; + +void +create_multiple_variables(int num) +{ + char name[64]; + char value[64]; + int i; + + for (i = 0; i < num; i++) { + snprintf(name, sizeof(name), "%s_%d", TEST_VARIABLE, i); + snprintf(value, sizeof(value), "%d", i); + ATF_CHECK(getenv(name) == NULL); + ATF_CHECK(setenv(name, value, 0) != -1); + ATF_CHECK_STREQ(getenv(name), value); + } +} + +void +check_if_nulled_variables(int num) +{ + char name[64]; + int i; + + for (i = 0; i < num; i++) { + snprintf(name, sizeof(name), "%s_%d", TEST_VARIABLE, i); + ATF_CHECK(getenv(name) == NULL); + } +} + +ATF_TC_WITHOUT_HEAD(clearenv__single_var_test); +ATF_TC_BODY(clearenv__single_var_test, tc) +{ + + ATF_CHECK(setenv(TEST_VARIABLE, "true", 0) != -1); + ATF_CHECK_STREQ(getenv(TEST_VARIABLE), "true"); + ATF_CHECK(clearenv() == 0); + ATF_CHECK(getenv(TEST_VARIABLE) == NULL); +} + +ATF_TC_WITHOUT_HEAD(clearenv__multiple_vars_test); +ATF_TC_BODY(clearenv__multiple_vars_test, tc) +{ + + create_multiple_variables(10); + ATF_CHECK(clearenv() == 0); + check_if_nulled_variables(10); +} + +ATF_TC_WITHOUT_HEAD(clearenv__recreated_vars_test); +ATF_TC_BODY(clearenv__recreated_vars_test, tc) +{ + + create_multiple_variables(10); + ATF_CHECK(clearenv() == 0); + check_if_nulled_variables(10); + create_multiple_variables(10); +} + +ATF_TC_WITHOUT_HEAD(clearenv__system_var_test); +ATF_TC_BODY(clearenv__system_var_test, tc) +{ + + ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) != NULL); + ATF_CHECK(clearenv() == 0); + ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) == NULL); +} + +ATF_TC_WITHOUT_HEAD(clearenv__recreated_system_var_test); +ATF_TC_BODY(clearenv__recreated_system_var_test, tc) +{ + + ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) != NULL); + ATF_CHECK(clearenv() == 0); + ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) == NULL); + ATF_CHECK(setenv(TEST_SYSTEM_VARIABLE, "test", 0) != -1); + ATF_CHECK_STREQ(getenv(TEST_SYSTEM_VARIABLE), "test"); +} + +ATF_TC_WITHOUT_HEAD(clearenv__double_clear_vars); +ATF_TC_BODY(clearenv__double_clear_vars, tc) +{ + + create_multiple_variables(10); + ATF_CHECK(clearenv() == 0); + check_if_nulled_variables(10); + ATF_CHECK(clearenv() == 0); + check_if_nulled_variables(10); + create_multiple_variables(10); +} + +ATF_TC_WITHOUT_HEAD(clearenv__environ_null); +ATF_TC_BODY(clearenv__environ_null, tc) +{ + + ATF_CHECK(clearenv() == 0); + ATF_CHECK(environ == NULL); +} + +ATF_TC_WITHOUT_HEAD(clearenv__putenv_vars); +ATF_TC_BODY(clearenv__putenv_vars, tc) +{ + char buf[64], ref[64]; + + snprintf(buf, sizeof(buf), "%s=1", TEST_VARIABLE); + strcpy(ref, buf); + + ATF_CHECK(getenv(TEST_VARIABLE) == NULL); + ATF_CHECK(putenv(buf) != -1); + ATF_CHECK(strcmp(getenv(TEST_VARIABLE), "1") == 0); + + ATF_CHECK(clearenv() == 0); + + ATF_CHECK(getenv(TEST_VARIABLE) == NULL); + ATF_CHECK(strcmp(buf, ref) == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, clearenv__single_var_test); + ATF_TP_ADD_TC(tp, clearenv__multiple_vars_test); + ATF_TP_ADD_TC(tp, clearenv__recreated_vars_test); + + ATF_TP_ADD_TC(tp, clearenv__system_var_test); + ATF_TP_ADD_TC(tp, clearenv__recreated_system_var_test); + + ATF_TP_ADD_TC(tp, clearenv__double_clear_vars); + ATF_TP_ADD_TC(tp, clearenv__environ_null); + + ATF_TP_ADD_TC(tp, clearenv__putenv_vars); + + return (atf_no_error()); +}