Pharos 0.7.23
Modern Web Framework & Web App Hosting Service.
Loading...
Searching...
No Matches
native_process_capture.c
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Runs child processes and captures their output, optionally with injected environment values.
4 */
5
7
8#include "native_support.h"
9
10#ifndef _WIN32
11#ifndef __wasm__
12
13#include <poll.h>
14#include <spawn.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/wait.h>
18#include <unistd.h>
19
20extern char **environ;
21
22static int append_pipe_to_buffer(int fd, Buffer *buffer, int *open_flag) {
23 char chunk[4096];
24 ssize_t bytes_read = read(fd, chunk, sizeof(chunk));
25 if (bytes_read > 0) {
26 if (buffer_append(buffer, chunk, (size_t)bytes_read) != 0) {
27 return -1;
28 }
29 return 0;
30 }
31 if (bytes_read == 0) {
32 close(fd);
33 *open_flag = 0;
34 return 0;
35 }
36 return -1;
37}
38
39static char *build_child_env_assignment_native(const char *key, const char *value) {
40 size_t key_len;
41 size_t value_len;
42 char *assignment;
43 if (key == NULL || key[0] == '\0' || value == NULL || value[0] == '\0') {
44 return NULL;
45 }
46 key_len = strlen(key);
47 value_len = strlen(value);
48 assignment = (char *)malloc(key_len + value_len + 2);
49 if (assignment == NULL) {
50 return NULL;
51 }
52 memcpy(assignment, key, key_len);
53 assignment[key_len] = '=';
54 memcpy(assignment + key_len + 1, value, value_len + 1);
55 return assignment;
56}
57
58static void free_child_env_native(char **child_env, size_t base_count, size_t child_count) {
59 size_t index;
60 if (child_env == NULL) {
61 return;
62 }
63 for (index = base_count; index < child_count; index++) {
64 free(child_env[index]);
65 }
66 free(child_env);
67}
68
69static char **build_child_env_native(const char *env_keys[], const char *env_values[], size_t env_count, size_t *base_count_out, size_t *child_count_out) {
70 size_t base_count = 0;
71 size_t child_count = 0;
72 char **child_env = NULL;
73 size_t env_index;
74 if (base_count_out != NULL) {
75 *base_count_out = 0;
76 }
77 if (child_count_out != NULL) {
78 *child_count_out = 0;
79 }
80 while (environ != NULL && environ[base_count] != NULL) {
81 base_count++;
82 }
83 child_env = (char **)malloc(sizeof(char *) * (base_count + env_count + 1));
84 if (child_env == NULL) {
85 return NULL;
86 }
87 for (size_t base_index = 0; base_index < base_count; base_index++) {
88 const char *entry = environ[base_index];
89 int overridden = 0;
90 for (env_index = 0; env_index < env_count; env_index++) {
91 const char *key = env_keys != NULL ? env_keys[env_index] : NULL;
92 size_t key_len = key != NULL ? strlen(key) : 0;
93 if (key_len > 0 && strncmp(entry, key, key_len) == 0 && entry[key_len] == '=') {
94 overridden = 1;
95 break;
96 }
97 }
98 if (!overridden) {
99 child_env[child_count++] = environ[base_index];
100 }
101 }
102 for (env_index = 0; env_index < env_count; env_index++) {
103 char *assignment = build_child_env_assignment_native(
104 env_keys != NULL ? env_keys[env_index] : NULL,
105 env_values != NULL ? env_values[env_index] : NULL
106 );
107 if (assignment != NULL) {
108 child_env[child_count++] = assignment;
109 }
110 }
111 child_env[child_count] = NULL;
112 if (base_count_out != NULL) {
113 *base_count_out = base_count;
114 }
115 if (child_count_out != NULL) {
116 *child_count_out = child_count;
117 }
118 return child_env;
119}
120
121int capture_execv_output_split_env_native(char *const argv[], const char *env_keys[], const char *env_values[], size_t env_count, char **stdout_output, size_t *stdout_len, char **stderr_output, size_t *stderr_len) {
122 int stdout_pipefd[2];
123 int stderr_pipefd[2];
124 pid_t pid;
125 Buffer stdout_buffer;
126 Buffer stderr_buffer;
127 int status_code = 0;
128 int stdout_open = 1;
129 int stderr_open = 1;
130 posix_spawn_file_actions_t file_actions;
131 char **child_env = NULL;
132 size_t base_count = 0;
133 size_t child_count = 0;
134 int spawn_errno = 0;
135 int file_actions_ready = 0;
136 buffer_init(&stdout_buffer);
137 buffer_init(&stderr_buffer);
138 if (stdout_output != NULL) {
139 *stdout_output = NULL;
140 }
141 if (stdout_len != NULL) {
142 *stdout_len = 0;
143 }
144 if (stderr_output != NULL) {
145 *stderr_output = NULL;
146 }
147 if (stderr_len != NULL) {
148 *stderr_len = 0;
149 }
150 if (argv == NULL || argv[0] == NULL) {
151 return 69;
152 }
153 if (pipe(stdout_pipefd) != 0) {
154 return 69;
155 }
156 if (pipe(stderr_pipefd) != 0) {
157 close(stdout_pipefd[0]);
158 close(stdout_pipefd[1]);
159 return 69;
160 }
161 child_env = build_child_env_native(env_keys, env_values, env_count, &base_count, &child_count);
162 if (env_count > 0 && child_env == NULL) {
163 close(stdout_pipefd[0]);
164 close(stdout_pipefd[1]);
165 close(stderr_pipefd[0]);
166 close(stderr_pipefd[1]);
167 return 69;
168 }
169 if (posix_spawn_file_actions_init(&file_actions) != 0) {
170 free_child_env_native(child_env, base_count, child_count);
171 close(stdout_pipefd[0]);
172 close(stdout_pipefd[1]);
173 close(stderr_pipefd[0]);
174 close(stderr_pipefd[1]);
175 return 69;
176 }
177 file_actions_ready = 1;
178 if (posix_spawn_file_actions_adddup2(&file_actions, stdout_pipefd[1], STDOUT_FILENO) != 0 ||
179 posix_spawn_file_actions_adddup2(&file_actions, stderr_pipefd[1], STDERR_FILENO) != 0 ||
180 posix_spawn_file_actions_addclose(&file_actions, stdout_pipefd[0]) != 0 ||
181 posix_spawn_file_actions_addclose(&file_actions, stdout_pipefd[1]) != 0 ||
182 posix_spawn_file_actions_addclose(&file_actions, stderr_pipefd[0]) != 0 ||
183 posix_spawn_file_actions_addclose(&file_actions, stderr_pipefd[1]) != 0) {
184 posix_spawn_file_actions_destroy(&file_actions);
185 free_child_env_native(child_env, base_count, child_count);
186 close(stdout_pipefd[0]);
187 close(stdout_pipefd[1]);
188 close(stderr_pipefd[0]);
189 close(stderr_pipefd[1]);
190 return 69;
191 }
192 spawn_errno = posix_spawn(&pid, argv[0], &file_actions, NULL, argv, child_env != NULL ? child_env : environ);
193 if (file_actions_ready) {
194 posix_spawn_file_actions_destroy(&file_actions);
195 }
196 free_child_env_native(child_env, base_count, child_count);
197 if (spawn_errno != 0) {
198 close(stdout_pipefd[0]);
199 close(stdout_pipefd[1]);
200 close(stderr_pipefd[0]);
201 close(stderr_pipefd[1]);
202 return 69;
203 }
204 close(stdout_pipefd[1]);
205 close(stderr_pipefd[1]);
206 while (stdout_open || stderr_open) {
207 struct pollfd fds[2];
208 nfds_t nfds = 0;
209 if (stdout_open) {
210 fds[nfds].fd = stdout_pipefd[0];
211 fds[nfds].events = POLLIN | POLLHUP;
212 nfds++;
213 }
214 if (stderr_open) {
215 fds[nfds].fd = stderr_pipefd[0];
216 fds[nfds].events = POLLIN | POLLHUP;
217 nfds++;
218 }
219 if (poll(fds, nfds, -1) < 0) {
220 if (stdout_open) close(stdout_pipefd[0]);
221 if (stderr_open) close(stderr_pipefd[0]);
222 waitpid(pid, NULL, 0);
223 buffer_free(&stdout_buffer);
224 buffer_free(&stderr_buffer);
225 return 69;
226 }
227 nfds = 0;
228 if (stdout_open) {
229 if (fds[nfds].revents & (POLLIN | POLLHUP)) {
230 if (append_pipe_to_buffer(stdout_pipefd[0], &stdout_buffer, &stdout_open) != 0) {
231 if (stdout_open) close(stdout_pipefd[0]);
232 if (stderr_open) close(stderr_pipefd[0]);
233 waitpid(pid, NULL, 0);
234 buffer_free(&stdout_buffer);
235 buffer_free(&stderr_buffer);
236 return 69;
237 }
238 }
239 nfds++;
240 }
241 if (stderr_open) {
242 if (fds[nfds].revents & (POLLIN | POLLHUP)) {
243 if (append_pipe_to_buffer(stderr_pipefd[0], &stderr_buffer, &stderr_open) != 0) {
244 if (stdout_open) close(stdout_pipefd[0]);
245 if (stderr_open) close(stderr_pipefd[0]);
246 waitpid(pid, NULL, 0);
247 buffer_free(&stdout_buffer);
248 buffer_free(&stderr_buffer);
249 return 69;
250 }
251 }
252 }
253 }
254 waitpid(pid, &status_code, 0);
255 if (stdout_output != NULL) {
256 *stdout_output = stdout_buffer.data;
257 } else {
258 buffer_free(&stdout_buffer);
259 }
260 if (stdout_len != NULL) {
261 *stdout_len = stdout_buffer.len;
262 }
263 if (stderr_output != NULL) {
264 *stderr_output = stderr_buffer.data;
265 } else {
266 buffer_free(&stderr_buffer);
267 }
268 if (stderr_len != NULL) {
269 *stderr_len = stderr_buffer.len;
270 }
271 if (WIFEXITED(status_code)) {
272 return WEXITSTATUS(status_code);
273 }
274 return 69;
275}
276
277int capture_execv_output_env_native(char *const argv[], const char *env_keys[], const char *env_values[], size_t env_count, char **output, size_t *output_len) {
278 char *stdout_output = NULL;
279 char *stderr_output = NULL;
280 size_t stdout_captured_len = 0;
281 size_t stderr_captured_len = 0;
282 int code = capture_execv_output_split_env_native(argv, env_keys, env_values, env_count, &stdout_output, &stdout_captured_len, &stderr_output, &stderr_captured_len);
283 Buffer combined;
284 buffer_init(&combined);
285 if (stdout_output != NULL && stdout_captured_len > 0) {
286 if (buffer_append(&combined, stdout_output, stdout_captured_len) != 0) {
287 free(stdout_output);
288 free(stderr_output);
289 return 69;
290 }
291 }
292 if (stderr_output != NULL && stderr_captured_len > 0) {
293 if (buffer_append(&combined, stderr_output, stderr_captured_len) != 0) {
294 free(stdout_output);
295 free(stderr_output);
296 buffer_free(&combined);
297 return 69;
298 }
299 }
300 free(stdout_output);
301 free(stderr_output);
302 if (output != NULL) {
303 *output = combined.data;
304 } else {
305 buffer_free(&combined);
306 }
307 if (output_len != NULL) {
308 *output_len = combined.len;
309 }
310 return code;
311}
312
313int capture_execv_output_native(char *const argv[], char **output, size_t *output_len) {
314 return capture_execv_output_env_native(argv, NULL, NULL, 0, output, output_len);
315}
316
317int run_process_capture_native(char *const argv[], char **output) {
318 return capture_execv_output_native(argv, output, NULL);
319}
320
321#else
322int capture_execv_output_native(char *const argv[], char **output, size_t *output_len) {
323 (void)argv;
324 (void)output;
325 (void)output_len;
326 return 78;
327}
328
329int capture_execv_output_split_env_native(char *const argv[], const char *env_keys[], const char *env_values[], size_t env_count, char **stdout_output, size_t *stdout_len, char **stderr_output, size_t *stderr_len) {
330 (void)argv;
331 (void)env_keys;
332 (void)env_values;
333 (void)env_count;
334 (void)stdout_output;
335 (void)stdout_len;
336 (void)stderr_output;
337 (void)stderr_len;
338 return 78;
339}
340
341int run_process_capture_native(char *const argv[], char **output) {
342 (void)argv;
343 (void)output;
344 return 78;
345}
346#endif
347#else
348int capture_execv_output_native(char *const argv[], char **output, size_t *output_len) {
349 (void)argv;
350 (void)output;
351 (void)output_len;
352 return 78;
353}
354
355int capture_execv_output_split_env_native(char *const argv[], const char *env_keys[], const char *env_values[], size_t env_count, char **stdout_output, size_t *stdout_len, char **stderr_output, size_t *stderr_len) {
356 (void)argv;
357 (void)env_keys;
358 (void)env_values;
359 (void)env_count;
360 (void)stdout_output;
361 (void)stdout_len;
362 (void)stderr_output;
363 (void)stderr_len;
364 return 78;
365}
366
367int run_process_capture_native(char *const argv[], char **output) {
368 (void)argv;
369 (void)output;
370 return 78;
371}
372#endif
int run_process_capture_native(char *const argv[], char **output)
Executes a process and captures its combined output text.
static int append_pipe_to_buffer(int fd, Buffer *buffer, int *open_flag)
int capture_execv_output_env_native(char *const argv[], const char *env_keys[], const char *env_values[], size_t env_count, char **output, size_t *output_len)
Executes a process with injected environment values and captures its output.
char ** environ
static char * build_child_env_assignment_native(const char *key, const char *value)
int capture_execv_output_native(char *const argv[], char **output, size_t *output_len)
Executes a process and captures its combined output.
static void free_child_env_native(char **child_env, size_t base_count, size_t child_count)
int capture_execv_output_split_env_native(char *const argv[], const char *env_keys[], const char *env_values[], size_t env_count, char **stdout_output, size_t *stdout_len, char **stderr_output, size_t *stderr_len)
Executes a process with injected environment values and captures stdout and stderr separately.
static char ** build_child_env_native(const char *env_keys[], const char *env_values[], size_t env_count, size_t *base_count_out, size_t *child_count_out)
Declares child-process capture helpers.
void buffer_init(Buffer *buffer)
Initializes a growable buffer to an empty state.
void buffer_free(Buffer *buffer)
Releases memory owned by a growable buffer.
int buffer_append(Buffer *buffer, const void *data, size_t len)
Appends bytes to a growable buffer and maintains a trailing NUL byte.
Declares shared low-level support helpers used across the Pharos native runtime.
Growable byte buffer used by parsing and string assembly helpers.
size_t len
char * data