1 /*
2 * Copyright (c) 2023, Meta
3 * Copyright (c) 2025 Tenstorrent AI ULC
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include <zephyr/logging/log.h>
14 #include <zephyr/sys/sem.h>
15
16 #define TRACK_ALLOC (IS_ENABLED(CONFIG_POSIX_ENV_LOG_LEVEL_DBG) || IS_ENABLED(CONFIG_ZTEST))
17
18 LOG_MODULE_REGISTER(posix_env, CONFIG_POSIX_ENV_LOG_LEVEL);
19
20 static SYS_SEM_DEFINE(environ_lock, 1, 1);
21 static size_t allocated;
22 char **environ;
23
24 #ifdef CONFIG_ZTEST
posix_env_get_allocated_space(void)25 size_t posix_env_get_allocated_space(void)
26 {
27 return allocated;
28 }
29 #endif
30
environ_size(void)31 static size_t environ_size(void)
32 {
33 size_t ret;
34
35 if (environ == NULL) {
36 return 0;
37 }
38
39 for (ret = 0; environ[ret] != NULL; ++ret) {
40 }
41
42 return ret;
43 }
44
findenv(const char * name,size_t namelen)45 static int findenv(const char *name, size_t namelen)
46 {
47 const char *env;
48
49 if (name == NULL || namelen == 0 || strchr(name, '=') != NULL) {
50 /* Note: '=' is not a valid name character */
51 return -EINVAL;
52 }
53
54 if (environ == NULL) {
55 return -ENOENT;
56 }
57
58 for (char **envp = &environ[0]; *envp != NULL; ++envp) {
59 env = *envp;
60 if (strncmp(env, name, namelen) == 0 && env[namelen] == '=') {
61 return envp - environ;
62 }
63 }
64
65 return -ENOENT;
66 }
67
z_getenv(const char * name)68 char *z_getenv(const char *name)
69 {
70 int ret;
71 size_t nsize;
72 char *val = NULL;
73
74 nsize = (name == NULL) ? 0 : strlen(name);
75 SYS_SEM_LOCK(&environ_lock) {
76 ret = findenv(name, nsize);
77 if (ret < 0) {
78 SYS_SEM_LOCK_BREAK;
79 }
80
81 val = environ[ret] + nsize + 1;
82 }
83
84 return val;
85 }
86
z_getenv_r(const char * name,char * buf,size_t len)87 int z_getenv_r(const char *name, char *buf, size_t len)
88 {
89 int ret = 0;
90 size_t vsize;
91 size_t nsize;
92 char *val = NULL;
93
94 nsize = (name == NULL) ? 0 : strlen(name);
95 SYS_SEM_LOCK(&environ_lock) {
96 ret = findenv(name, nsize);
97 if (ret < 0) {
98 LOG_DBG("No entry for name '%s'", name);
99 SYS_SEM_LOCK_BREAK;
100 }
101
102 val = environ[ret] + nsize + 1;
103 vsize = strlen(val) + 1;
104 if (vsize > len) {
105 ret = -ERANGE;
106 SYS_SEM_LOCK_BREAK;
107 }
108 strcpy(buf, val);
109 LOG_DBG("Found entry %s", environ[ret]);
110 }
111
112 if (ret < 0) {
113 errno = -ret;
114 ret = -1;
115 }
116
117 return ret;
118 }
119
z_setenv(const char * name,const char * val,int overwrite)120 int z_setenv(const char *name, const char *val, int overwrite)
121 {
122 int ret = 0;
123 char *env;
124 char **envp;
125 size_t esize;
126 const size_t vsize = (val == NULL) ? 0 : strlen(val);
127 const size_t nsize = (name == NULL) ? 0 : strlen(name);
128 /* total size of name + '=' + val + '\0' */
129 const size_t tsize = nsize + 1 /* '=' */ + vsize + 1 /* '\0' */;
130
131 if (name == NULL || val == NULL) {
132 LOG_DBG("Invalid name '%s' or value '%s'", name, val);
133 errno = EINVAL;
134 return -1;
135 }
136
137 SYS_SEM_LOCK(&environ_lock) {
138 ret = findenv(name, nsize);
139 if (ret == -EINVAL) {
140 LOG_DBG("Invalid name '%s'", name);
141 SYS_SEM_LOCK_BREAK;
142 }
143 if (ret >= 0) {
144 /* name was found in environ */
145 esize = strlen(environ[ret]) + 1;
146 if (overwrite == 0) {
147 LOG_DBG("Found entry %s", environ[ret]);
148 ret = 0;
149 SYS_SEM_LOCK_BREAK;
150 }
151 } else {
152 /* name was not found in environ -> add new entry */
153 esize = environ_size();
154 envp = realloc(environ,
155 sizeof(void *) * (esize + 1 /* new entry */ + 1 /* NULL */));
156 if (envp == NULL) {
157 ret = -ENOMEM;
158 SYS_SEM_LOCK_BREAK;
159 }
160
161 if (TRACK_ALLOC) {
162 allocated += sizeof(void *) * (esize + 2);
163 LOG_DBG("realloc %zu bytes (allocated: %zu)",
164 sizeof(void *) * (esize + 2), allocated);
165 }
166
167 environ = envp;
168 ret = esize;
169 environ[ret] = NULL;
170 environ[ret + 1] = NULL;
171 esize = 0;
172 }
173
174 if (esize < tsize) {
175 /* need to malloc or realloc space for new environ entry */
176 env = realloc(environ[ret], tsize);
177 if (env == NULL) {
178 ret = -ENOMEM;
179 SYS_SEM_LOCK_BREAK;
180 }
181 if (TRACK_ALLOC) {
182 allocated += tsize - esize;
183 LOG_DBG("realloc %zu bytes (allocated: %zu)", tsize - esize,
184 allocated);
185 }
186 environ[ret] = env;
187 }
188
189 strcpy(environ[ret], name);
190 environ[ret][nsize] = '=';
191 strncpy(environ[ret] + nsize + 1, val, vsize + 1);
192 LOG_DBG("Added entry %s", environ[ret]);
193
194 ret = 0;
195 }
196
197 if (ret < 0) {
198 errno = -ret;
199 ret = -1;
200 }
201
202 return ret;
203 }
204
z_unsetenv(const char * name)205 int z_unsetenv(const char *name)
206 {
207 int ret = 0;
208 char **envp;
209 size_t esize;
210 size_t nsize;
211
212 nsize = (name == NULL) ? 0 : strlen(name);
213 SYS_SEM_LOCK(&environ_lock) {
214 ret = findenv(name, nsize);
215 if (ret < 0) {
216 ret = (ret == -EINVAL) ? -EINVAL : 0;
217 SYS_SEM_LOCK_BREAK;
218 }
219
220 esize = environ_size();
221 if (TRACK_ALLOC) {
222 allocated -= strlen(environ[ret]) + 1;
223 LOG_DBG("free %zu bytes (allocated: %zu)", strlen(environ[ret]) + 1,
224 allocated);
225 }
226 free(environ[ret]);
227
228 /* shuffle remaining environment variable pointers forward */
229 for (; ret < esize; ++ret) {
230 environ[ret] = environ[ret + 1];
231 }
232 /* environ must be terminated with a NULL pointer */
233 environ[ret] = NULL;
234
235 /* reduce environ size and update allocation */
236 --esize;
237 if (esize == 0) {
238 free(environ);
239 environ = NULL;
240 } else {
241 envp = realloc(environ, (esize + 1 /* NULL */) * sizeof(void *));
242 if (envp != NULL) {
243 environ = envp;
244 }
245 }
246 __ASSERT_NO_MSG((esize >= 1 && environ != NULL) || environ == NULL);
247
248 if (TRACK_ALLOC) {
249 /* recycle nsize here */
250 nsize = ((esize == 0) ? 2 : 1) * sizeof(void *);
251 allocated -= nsize;
252 LOG_DBG("free %zu bytes (allocated: %zu)", nsize, allocated);
253 }
254
255 ret = 0;
256 }
257
258 if (ret < 0) {
259 errno = -ret;
260 ret = -1;
261 }
262
263 return ret;
264 }
265