master
  1#include <unistd.h>
  2#include <stdlib.h>
  3#include <sysexits.h>
  4#ifdef __wasilibc_use_wasip2
  5#include <wasi/wasip2.h>
  6#else
  7#include <wasi/api.h>
  8#endif
  9#include <wasi/libc.h>
 10#include <wasi/libc-environ.h>
 11
 12/// If the program doesn't use `environ`, it'll get this version of
 13/// `__wasilibc_environ`, which isn't initialized with a constructor function.
 14/// `getenv` etc. call `__wasilibc_ensure_environ()` before accessing it.
 15/// Statically-initialize it to an invalid pointer value so that we can
 16/// detect if it's been explicitly initialized (we can't use `NULL` because
 17/// `clearenv` sets it to NULL.
 18weak char **__wasilibc_environ = (char **)-1;
 19
 20// See the comments in libc-environ.h.
 21void __wasilibc_ensure_environ(void) {
 22    if (__wasilibc_environ == (char **)-1) {
 23        __wasilibc_initialize_environ();
 24    }
 25}
 26
 27/// Avoid dynamic allocation for the case where there are no environment
 28/// variables, but we still need a non-NULL pointer to an (empty) array.
 29static char *empty_environ[1] = { NULL };
 30
 31// See the comments in libc-environ.h.
 32void __wasilibc_initialize_environ(void) {
 33#ifdef __wasilibc_use_wasip2
 34    // Get the environment
 35    wasip2_list_tuple2_string_string_t wasi_environment;
 36    environment_get_environment(&wasi_environment);
 37
 38    size_t environ_count = wasi_environment.len;
 39    if (environ_count == 0) {
 40        __wasilibc_environ = empty_environ;
 41        return;
 42    }
 43
 44    // Add 1 for the NULL pointer to mark the end, and check for overflow.
 45    size_t num_ptrs = environ_count + 1;
 46    if (num_ptrs == 0) {
 47        goto software;
 48    }
 49
 50    // Allocate memory for the array of pointers. This uses `calloc` both to
 51    // handle overflow and to initialize the NULL pointer at the end.
 52    char **environ_ptrs = calloc(num_ptrs, sizeof(char *));
 53
 54    // Copy the environment variables
 55    for (size_t i = 0; i < environ_count; i++) {
 56        wasip2_tuple2_string_string_t pair = wasi_environment.ptr[i];
 57        // 1 extra character for the null terminator, 1 for the '=' character
 58        environ_ptrs[i] = malloc(pair.f0.len + pair.f1.len + 2);
 59        if (!environ_ptrs[i]) {
 60            for (size_t j = 0; j < i; j++)
 61                free(environ_ptrs[j]);
 62            free(environ_ptrs);
 63            goto software;
 64        }
 65        memcpy(environ_ptrs[i], pair.f0.ptr, pair.f0.len);
 66        environ_ptrs[i][pair.f0.len] = '=';
 67        memcpy(environ_ptrs[i] + pair.f0.len + 1, pair.f1.ptr, pair.f1.len);
 68        environ_ptrs[i][pair.f0.len + pair.f1.len + 1] = '\0';
 69    }
 70
 71    // Free the WASI environment list
 72    wasip2_list_tuple2_string_string_free(&wasi_environment);
 73
 74    // Initialize the environment from the created array
 75    __wasilibc_environ = environ_ptrs;
 76    return;
 77software:
 78    wasip2_list_tuple2_string_string_free(&wasi_environment);
 79    _Exit(EX_SOFTWARE);
 80
 81#else
 82    // Get the sizes of the arrays we'll have to create to copy in the environment.
 83    size_t environ_count;
 84    size_t environ_buf_size;
 85    __wasi_errno_t err = __wasi_environ_sizes_get(&environ_count, &environ_buf_size);
 86    if (err != __WASI_ERRNO_SUCCESS) {
 87        goto oserr;
 88    }
 89    if (environ_count == 0) {
 90        __wasilibc_environ = empty_environ;
 91        return;
 92    }
 93
 94    // Add 1 for the NULL pointer to mark the end, and check for overflow.
 95    size_t num_ptrs = environ_count + 1;
 96    if (num_ptrs == 0) {
 97        goto software;
 98    }
 99
100    // Allocate memory for storing the environment chars.
101    char *environ_buf = malloc(environ_buf_size);
102    if (environ_buf == NULL) {
103        goto software;
104    }
105
106    // Allocate memory for the array of pointers. This uses `calloc` both to
107    // handle overflow and to initialize the NULL pointer at the end.
108    char **environ_ptrs = calloc(num_ptrs, sizeof(char *));
109    if (environ_ptrs == NULL) {
110        free(environ_buf);
111        goto software;
112    }
113
114    // Fill the environment chars, and the `__wasilibc_environ` array with
115    // pointers into those chars.
116    // TODO: Remove the casts on `environ_ptrs` and `environ_buf` once the witx is updated with char8 support.
117    err = __wasi_environ_get((uint8_t **)environ_ptrs, (uint8_t *)environ_buf);
118    if (err != __WASI_ERRNO_SUCCESS) {
119        free(environ_buf);
120        free(environ_ptrs);
121        goto oserr;
122    }
123
124    __wasilibc_environ = environ_ptrs;
125    return;
126oserr:
127    _Exit(EX_OSERR);
128software:
129    _Exit(EX_SOFTWARE);
130#endif
131}
132
133// See the comments in libc-environ.h.
134void __wasilibc_deinitialize_environ(void) {
135    if (__wasilibc_environ != (char **)-1) {
136        // Let libc-top-half clear the old environment-variable strings.
137        clearenv();
138        // Set the pointer to the special init value.
139        __wasilibc_environ = (char **)-1;
140    }
141}
142
143// See the comments in libc-environ.h.
144weak void __wasilibc_maybe_reinitialize_environ_eagerly(void) {
145    // This version does nothing. It may be overridden by a version which does
146    // something if `environ` is used.
147}