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}