| 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. |
| 18 | weak char **__wasilibc_environ = (char **)-1; |
| 19 | |
| 20 | // See the comments in libc-environ.h. |
| 21 | void __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. |
| 29 | static char *empty_environ[1] = { NULL }; |
| 30 | |
| 31 | // See the comments in libc-environ.h. |
| 32 | void __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; |
| 77 | software: |
| 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; |
| 126 | oserr: |
| 127 | _Exit(EX_OSERR); |
| 128 | software: |
| 129 | _Exit(EX_SOFTWARE); |
| 130 | #endif |
| 131 | } |
| 132 | |
| 133 | // See the comments in libc-environ.h. |
| 134 | void __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. |
| 144 | weak 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 | } |