GCC Code Coverage Report


Directory: src/gate/
File: src/gate/processes_posix.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 449 597 75.2%
Functions: 35 42 83.3%
Branches: 186 334 55.7%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/processes.h"
30
31 #include "gate/platforms.h"
32 #include "gate/debugging.h"
33 #include "gate/results.h"
34
35 #if defined(GATE_SYS_POSIX)
36
37 #include <unistd.h>
38 #include <dirent.h>
39 #include <sys/wait.h>
40 #include <sys/resource.h>
41 #include <sys/types.h>
42 #include <sys/select.h>
43 #include <sys/wait.h>
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <grp.h>
47
48 #if defined(GATE_SYS_LINUX)
49 # include <pty.h>
50 #elif defined(GATE_SYS_BSD)
51 # include <termios.h>
52 # if defined(GATE_SYS_FREEBSD)
53 # include <libutil.h>
54 # else
55 # include <util.h>
56 # endif
57 #elif defined(GATE_SYS_DARWIN)
58 # include <util.h>
59 # include "gate/platform/darwin/darwin_gate.h"
60 #endif
61
62 #if !defined(GATE_SYS_BSD)
63 # include <utmp.h>
64 #endif
65 #include <dlfcn.h>
66 #include <errno.h>
67
68 #include "gate/times.h"
69 #include "gate/threading.h"
70 #include "gate/platforms.h"
71 #include "gate/platform/linux/procfs.h"
72 #include "gate/files.h"
73
74
75 typedef struct gate_process_stream_impl
76 {
77 GATE_INTERFACE_VTBL(gate_process_stream)* vbl;
78 gate_atomic_int_t ref_counter;
79 pid_t target_pid;
80 int fd_stdin_write;
81 int fd_stdout_read;
82 int fd_stderr_read;
83 char peek_buffer[1024];
84 gate_size_t peek_buffer_used;
85 } gate_process_stream_impl_t;
86
87 31 static void gate_process_stream_release(void* thisptr)
88 {
89 31 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
90
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 17 times.
31 if (gate_atomic_int_dec(&stream->ref_counter) == 0)
91 {
92
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (stream->fd_stdin_write >= 0)
93 {
94 14 close(stream->fd_stdin_write);
95 14 stream->fd_stdin_write = -1;
96 }
97
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (stream->fd_stdout_read >= 0)
98 {
99 14 close(stream->fd_stdout_read);
100 14 stream->fd_stdout_read = -1;
101 }
102
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (stream->fd_stderr_read >= 0)
103 {
104 14 close(stream->fd_stderr_read);
105 14 stream->fd_stderr_read = -1;
106 }
107 14 gate_mem_dealloc(stream);
108 }
109 31 }
110 static char const* gate_process_stream_interface_name(void* thisptr)
111 {
112 (void)thisptr;
113 return GATE_INTERFACE_NAME_PROCESSSTREAM;
114 }
115 17 static int gate_process_stream_retain(void* thisptr)
116 {
117 17 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
118 17 return gate_atomic_int_inc(&stream->ref_counter);
119 }
120
121 1741652 static gate_result_t is_process_termined(pid_t pid, gate_bool_t* ptr_terminated)
122 {
123 siginfo_t info;
124 int result;
125
126 1741652 gate_mem_clear(&info, sizeof(info));
127 1741652 result = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
128
1/2
✓ Branch 0 taken 1741652 times.
✗ Branch 1 not taken.
1741652 if (0 == result)
129 {
130
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 1741588 times.
1741652 if ((info.si_code == CLD_EXITED)
131
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 || (info.si_code == CLD_KILLED)
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 || (info.si_code == CLD_DUMPED)
133 )
134 {
135 1741588 *ptr_terminated = true;
136 }
137 else
138 {
139 64 *ptr_terminated = false;
140 }
141 1741652 return GATE_RESULT_OK;
142 }
143 else
144 {
145 /* error */
146 return gate_platform_print_last_error(NULL, 0);
147 }
148 }
149
150 5996013 static gate_bool_t can_read_from_fd(int read_fd)
151 {
152 fd_set read_set;
153 struct timeval timeout;
154 int result;
155
156 5996013 timeout.tv_sec = 0;
157 5996013 timeout.tv_usec = 0;
158
159 5996013 FD_ZERO(&read_set);
160 5996013 FD_SET(read_fd, &read_set);
161
162 5996013 result = select(read_fd + 1, &read_set, NULL, NULL, &timeout);
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5996013 times.
5996013 if (result < 0)
164 {
165 /* error */
166 return false;
167 }
168
2/2
✓ Branch 0 taken 1741589 times.
✓ Branch 1 taken 4254424 times.
5996013 if (FD_ISSET(read_fd, &read_set))
169 {
170 1741589 return true;
171 }
172 4254424 return false;
173 }
174
175 79 static int await_read_fd_or_process_exit(pid_t pid, int read_fd)
176 {
177 for (;;)
178 62 {
179 fd_set read_set;
180 fd_set err_set;
181 struct timeval timeout;
182 int result;
183
184 79 timeout.tv_sec = 0;
185 79 timeout.tv_usec = 100000;
186
187 79 FD_ZERO(&read_set);
188 79 FD_ZERO(&err_set);
189 79 FD_SET(read_fd, &read_set);
190 79 FD_SET(read_fd, &err_set);
191
192 79 result = select(read_fd + 1, &read_set, NULL, &err_set, &timeout);
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if (result < 0)
194 {
195 /* error */
196 17 return -1;
197 }
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if (FD_ISSET(read_fd, &err_set))
199 {
200 /* error */
201 return -1;
202 }
203
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 62 times.
79 if (FD_ISSET(read_fd, &read_set))
204 {
205 /* can read */
206 17 return 1;
207 }
208
209
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 if (pid > 0)
210 {
211 62 gate_bool_t is_terminated = false;
212 62 gate_result_t wait_result = is_process_termined(pid, &is_terminated);
213
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (GATE_FAILED(wait_result))
215 {
216 /* failed to detect process state */
217 return -1;
218 }
219 else
220 {
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (is_terminated)
222 {
223 /* process terminated, nothing else to read */
224 return 0;
225 }
226
227 /* else: continue */
228 }
229 }
230 }
231 }
232
233 19 static gate_result_t gate_process_stream_read(void* thisptr, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
234 {
235 19 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
236 19 gate_result_t ret = GATE_RESULT_FAILED;
237 gate_int32_t errcode;
238 ptrdiff_t result;
239 int wait_result;
240
241
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
19 if (stream->peek_buffer_used > 0)
242 {
243 /* there is something in the peek buffer */
244
245
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bufferlength >= stream->peek_buffer_used)
246 {
247 /* we can read the whole peek-buffer */
248 2 gate_mem_copy(buffer, stream->peek_buffer, stream->peek_buffer_used);
249
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (returned) *returned = stream->peek_buffer_used;
250 2 stream->peek_buffer_used = 0;
251 }
252 else
253 {
254 /* we can only return a subset of the peek buffer */
255 gate_mem_copy(buffer, &stream->peek_buffer[0], bufferlength);
256 gate_mem_copy(&stream->peek_buffer[0], &stream->peek_buffer[bufferlength], stream->peek_buffer_used - bufferlength);
257 stream->peek_buffer_used -= bufferlength;
258 if (returned) *returned = bufferlength;
259 }
260 2 return GATE_RESULT_OK;
261 }
262
263 17 wait_result = await_read_fd_or_process_exit(stream->target_pid, stream->fd_stdout_read);
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (wait_result < 0)
265 {
266 return GATE_RESULT_FAILED;
267 }
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 else if (wait_result == 0)
269 {
270 if (returned)
271 {
272 *returned = 0;
273 }
274 return GATE_RESULT_OK;
275 }
276
277 /* data available to read */
278 17 result = gate_posix_read(stream->fd_stdout_read, buffer, bufferlength);
279
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14 times.
17 if (result < 0)
280 {
281 3 errcode = gate_platform_get_last_error();
282
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (errcode == EIO)
283 {
284 3 gate_bool_t terminated = false;
285 3 gate_result_t terminated_result = is_process_termined(stream->target_pid, &terminated);
286
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (GATE_SUCCEEDED(terminated_result) && terminated)
287 {
288 /* signal end-of-stream, as process was terminated */
289
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (returned)
290 {
291 3 *returned = 0;
292 }
293 3 return GATE_RESULT_OK;
294 }
295 }
296 GATE_DEBUG_TRACE_MSG_VALUE("gate_process_stream_read() failed", errcode);
297 ret = gate_platform_print_error(errcode, NULL, 0);
298 }
299 else
300 {
301
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (returned) *returned = (gate_size_t)result;
302 14 ret = GATE_RESULT_OK;
303 }
304 14 return ret;
305 }
306 5996013 static gate_result_t gate_process_stream_peek(void* thisptr, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
307 {
308 5996013 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
309 gate_size_t copylen;
310
311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5996013 times.
5996013 if (stream->peek_buffer_used > 0)
312 {
313 /* there is already something in the peek buffer */
314 copylen = bufferlength < stream->peek_buffer_used ? bufferlength : stream->peek_buffer_used;
315 gate_mem_copy(buffer, &stream->peek_buffer[0], copylen);
316 if (returned) *returned = copylen;
317 return GATE_RESULT_OK;
318 }
319
320
2/2
✓ Branch 1 taken 1741589 times.
✓ Branch 2 taken 4254424 times.
5996013 if (can_read_from_fd(stream->fd_stdout_read))
321 {
322 /* read some bytes into peek-buffer */
323 ptrdiff_t result;
324 1741589 copylen = bufferlength < sizeof(stream->peek_buffer) ? bufferlength : sizeof(stream->peek_buffer);
325 1741589 result = gate_posix_read(stream->fd_stdout_read, &stream->peek_buffer[0], copylen);
326
2/2
✓ Branch 0 taken 1741587 times.
✓ Branch 1 taken 2 times.
1741589 if (result < 0)
327 {
328 /* read error */
329 1741587 gate_int32_t errcode = gate_platform_get_last_error();
330
1/2
✓ Branch 0 taken 1741587 times.
✗ Branch 1 not taken.
1741587 if (errcode == EIO)
331 {
332 1741587 gate_bool_t terminated = false;
333 1741587 gate_result_t wait_result = is_process_termined(stream->target_pid, &terminated);
334
3/4
✓ Branch 0 taken 1741587 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1741585 times.
✓ Branch 3 taken 2 times.
1741587 if (GATE_SUCCEEDED(wait_result) && terminated)
335 {
336 /* signal end-of-stream, as process was terminated */
337
1/2
✓ Branch 0 taken 1741585 times.
✗ Branch 1 not taken.
1741585 if (returned)
338 {
339 1741585 *returned = 0;
340 }
341 1741585 return GATE_RESULT_ENDOFSTREAM;
342 }
343 }
344 /* otherwise return native error info */
345 2 return gate_platform_print_last_error(NULL, 0);
346 }
347
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (result > 0)
348 {
349 /* peek-buffer is filled, return a copy via caller-supplied buffer */
350 2 stream->peek_buffer_used = (gate_size_t)result;
351 2 gate_mem_copy(buffer, &stream->peek_buffer[0], stream->peek_buffer_used);
352
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (returned) *returned = (gate_size_t)result;
353 2 return GATE_RESULT_OK;
354 }
355 }
356
357 /* else: nothing available to read*/
358
1/2
✓ Branch 0 taken 4254424 times.
✗ Branch 1 not taken.
4254424 if (returned) *returned = 0;
359 4254424 return GATE_RESULT_OK;
360 }
361 1 static gate_result_t gate_process_stream_write(void* thisptr, char const* buffer, gate_size_t bufferlength, gate_size_t* written)
362 {
363 1 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
364 1 gate_result_t ret = GATE_RESULT_FAILED;
365 1 ptrdiff_t result = gate_posix_write(stream->fd_stdin_write, buffer, bufferlength);
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (result <= 0)
367 {
368 gate_int32_t errcode = gate_platform_get_last_error();
369 GATE_DEBUG_TRACE_MSG_VALUE("gate_process_stream_write() failed", errcode);
370 ret = gate_platform_print_error(errcode, NULL, 0);
371 }
372 else
373 {
374
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (written) *written = (gate_size_t)result;
375 1 ret = GATE_RESULT_OK;
376 }
377 1 return ret;
378 }
379 static gate_result_t gate_process_stream_flush(void* thisptr)
380 {
381 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
382 fsync(stream->fd_stdin_write);
383 return GATE_RESULT_OK;
384 }
385 static gate_result_t gate_process_stream_read_err(void* thisptr, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
386 {
387 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
388 gate_result_t ret = GATE_RESULT_FAILED;
389 ptrdiff_t result = gate_posix_read(stream->fd_stderr_read, buffer, bufferlength);
390 if (result < 0)
391 {
392 gate_int32_t errcode = gate_platform_get_last_error();
393 GATE_DEBUG_TRACE_MSG_VALUE("gate_process_stream_read() failed", errcode);
394 ret = gate_platform_print_error(errcode, NULL, 0);
395 }
396 else
397 {
398 if (returned) *returned = (gate_size_t)result;
399 ret = GATE_RESULT_OK;
400 }
401 return ret;
402 }
403 static gate_result_t gate_process_stream_get_resource(void* thisptr, gate_enumint_t resource_type, gate_uintptr_t* resource)
404 {
405 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
406 gate_result_t ret;
407 switch (resource_type)
408 {
409 case GATE_STREAM_RESOURCE_INPUT:
410 {
411 *resource = (gate_uintptr_t)stream->fd_stdin_write;
412 ret = GATE_RESULT_OK;
413 break;
414 }
415 case GATE_STREAM_RESOURCE_OUTPUT:
416 {
417 *resource = (gate_uintptr_t)stream->fd_stdout_read;
418 ret = GATE_RESULT_OK;
419 break;
420 }
421 case GATE_STREAM_RESOURCE_ERROR:
422 {
423 *resource = (gate_uintptr_t)stream->fd_stderr_read;
424 ret = GATE_RESULT_OK;
425 break;
426 }
427 default:
428 {
429 ret = GATE_RESULT_NOTSUPPORTED;
430 break;
431 }
432 }
433 return ret;
434 }
435
436 static GATE_INTERFACE_VTBL(gate_process_stream) gate_process_stream_vtbl;
437 17 static void gate_init_process_stream_vtbl()
438 {
439
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if (!gate_process_stream_vtbl.get_interface_name)
440 {
441 GATE_INTERFACE_VTBL(gate_process_stream) const local_vtbl =
442 {
443 &gate_process_stream_interface_name,
444 &gate_process_stream_release,
445 &gate_process_stream_retain,
446 &gate_process_stream_read,
447 &gate_process_stream_peek,
448 &gate_process_stream_write,
449 &gate_process_stream_flush,
450 &gate_process_stream_get_resource,
451 &gate_process_stream_read_err
452 };
453 8 gate_process_stream_vtbl = local_vtbl;
454 }
455 17 }
456
457 17 static gate_process_stream_t* gate_process_stream_create(int fd_stdin_write, int fd_stdout_read, int fd_stderr_read)
458 {
459 17 gate_process_stream_impl_t* stream = gate_mem_alloc(sizeof(gate_process_stream_impl_t));
460
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (stream)
461 {
462 17 gate_init_process_stream_vtbl();
463 17 stream->vbl = &gate_process_stream_vtbl;
464 17 gate_atomic_int_set(&stream->ref_counter, 1);
465 17 stream->target_pid = 0;
466 17 stream->fd_stdin_write = fd_stdin_write;
467 17 stream->fd_stdout_read = fd_stdout_read;
468 17 stream->fd_stderr_read = fd_stderr_read;
469 17 stream->peek_buffer_used = 0;
470 }
471 17 return (gate_process_stream_t*)stream;
472 }
473
474
475 /*
476 typedef struct rlimit_setting
477 {
478 int setting;
479 struct rlimit limit;
480 } rlimit_setting_t;
481
482 typedef struct gate_process_exec_config
483 {
484 gate_bool_t set_uid;
485 uid_t uid;
486 gate_bool_t set_gid;
487 gid_t gid;
488
489 gate_bool_t new_session;
490 gate_bool_t new_terminal;
491
492 rlimit_setting_t limits[8];
493 gate_size_t limit_count;
494
495 } gate_process_exec_config_t;
496
497 gate_result_t gate_process_apply_exec_config(gate_process_exec_config_t const* config)
498 {
499 gate_result_t ret = GATE_RESULT_OK;
500 gate_size_t ndx;
501 int result;
502 char user_name[512];
503 gate_size_t user_name_used;
504
505 do
506 {
507 if(config == NULL) break;
508
509 for(ndx = 0; ndx != config->limit_count; ++ndx)
510 {
511 if(-1 == setrlimit(config->limits[ndx].setting, &config->limits[ndx].limit))
512 {
513 ret = GATE_RESULT_PREPARATIONFAILED;
514 break;
515 }
516 }
517 GATE_BREAK_IF_FAILED(ret);
518
519 if(config->set_uid && config->set_gid)
520 {
521 user_name_used = gate_posix_get_user_name(config->set_uid, user_name, sizeof(user_name));
522 if(user_name_used != 0)
523 {
524 result = initgroups(user_name, config->gid);
525 if(result != 0)
526 {
527 GATE_DEBUG_TRACE("Failed to initialize groups");
528 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
529 break;
530 }
531 }
532 }
533
534 if(config->set_gid)
535 {
536 result = setgid(config->gid);
537 if(result != 0)
538 {
539 GATE_DEBUG_TRACE("setgid() failed");
540 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
541 ret = GATE_RESULT_FAILED;
542 break;
543 }
544 }
545
546 if(config->set_uid)
547 {
548 result = setuid(config->uid);
549 if(result != 0)
550 {
551 GATE_DEBUG_TRACE("setuid() failed");
552 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
553 ret = GATE_RESULT_FAILED;
554 break;
555 }
556 }
557
558 if(config->new_session)
559 {
560 result = setsid();
561 if(result != 0)
562 {
563 GATE_DEBUG_TRACE("setsid() failed");
564 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
565 ret = GATE_RESULT_FAILED;
566 break;
567 }
568 }
569
570 } while(0);
571
572 return ret;
573 }
574 */
575
576 16 gate_result_t gate_process_get_id(gate_process_id_t* pid)
577 {
578 gate_result_t ret;
579 16 pid_t p = getpid();
580
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (pid == NULL)
581 {
582 ret = GATE_RESULT_INVALIDARG;
583 }
584 else
585 {
586 16 *pid = (gate_process_id_t)p;
587 16 ret = GATE_RESULT_OK;
588 }
589 16 return ret;
590 }
591 void gate_process_quit(int exitcode)
592 {
593 _exit(exitcode);
594 }
595
596 #if defined(GATE_SYS_BSD) || defined(GATE_SYS_DARWIN)
597
598 #include <stdlib.h>
599 #include <sys/types.h>
600 #include <sys/sysctl.h>
601
602 #if defined(GATE_SYS_NETBSD)
603 /* NetBSD is special here and uses kinfo_proc2 for usermode, BSD's kinfo_proc is for kernel-mode only */
604
605 # include <kvm.h>
606
607 typedef kvm_t* (*kvm_open_func_t)(char const* execfile, char const* corefile, char* swapfile, int flags, char* errstr);
608 typedef int (*kvm_close_func_t)(kvm_t* kd);
609 typedef struct kinfo_proc2* (*kvm_getproc2_func_t)(kvm_t* kd, int op, int arg, size_t elemsize, int* cnt);
610
611 static kvm_open_func_t kvm_open_func;
612 static kvm_close_func_t kvm_close_func;
613 static kvm_getproc2_func_t kvm_getproc2_func;
614
615 static void* volatile kvm_lib = NULL;
616
617 static void close_kvm_lib_atexit()
618 {
619 if (NULL != kvm_lib)
620 {
621 void* handle = kvm_lib;
622 kvm_lib = NULL;
623 gate_posix_dlclose(handle);
624 }
625 }
626
627 static gate_result_t load_kvm_library()
628 {
629 if (!kvm_lib)
630 {
631 kvm_lib = gate_posix_dlopen("libkvm.so", RTLD_GLOBAL | RTLD_NOW);
632 if (kvm_lib == NULL)
633 {
634 return GATE_RESULT_NOTAVAILABLE;
635 }
636 gate_platform_atexit(&close_kvm_lib_atexit);
637 }
638 if (!kvm_open_func)
639 {
640 kvm_open_func = (kvm_open_func_t)gate_posix_dlsym(kvm_lib, "kvm_open");
641 }
642 if (!kvm_close_func)
643 {
644 kvm_close_func = (kvm_close_func_t)gate_posix_dlsym(kvm_lib, "kvm_close");
645 }
646 if (!kvm_getproc2_func)
647 {
648 kvm_getproc2_func = (kvm_getproc2_func_t)gate_posix_dlsym(kvm_lib, "kvm_getproc2");
649 }
650 return (kvm_open_func && kvm_close_func && kvm_getproc2_func) ? GATE_RESULT_OK : GATE_RESULT_NOTAVAILABLE;
651 }
652
653 static gate_result_t load_process_infobuffer(gate_process_infobuffer_t* target, struct kinfo_proc2 const* src)
654 {
655 target->process.pid = src->p_pid;
656 target->process.parent_pid = src->p_ppid;
657 target->process.memory_used = src->p_vm_rssize;
658 target->process.uptime = src->p_ustart_sec * 1000;
659 target->process.cputime = src->p_uutime_sec * 1000;
660 target->process.resources = 1;
661 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->p_comm, gate_str_length(src->p_comm));
662 target->process.name = target->namebuffer;
663 gate_str_print_int64(target->ownerbuffer, sizeof(target->ownerbuffer), src->p_ruid);
664 target->process.owner = target->ownerbuffer;
665 return GATE_RESULT_OK;
666 }
667
668
669 gate_result_t gate_process_getinfo(gate_process_infobuffer_t* infobuffer, gate_process_id_t proc_id, gate_enumint_t flags)
670 {
671 gate_result_t ret = GATE_RESULT_FAILED;
672 kvm_t* kvm = NULL;
673
674 do
675 {
676 struct kinfo_proc2* procs = NULL;
677 int procs_count = 0;
678 char errmsg[_POSIX2_LINE_MAX] = GATE_INIT_EMPTY;
679
680 ret = load_kvm_library();
681 GATE_BREAK_IF_FAILED(ret);
682
683 kvm = kvm_open_func(NULL, NULL, NULL, O_RDONLY, errmsg);
684 if (kvm == NULL)
685 {
686 GATE_DEBUG_TRACE("kvm_open() failed");
687 errmsg[_POSIX2_LINE_MAX - 1] = 0;
688 GATE_DEBUG_TRACE(errmsg);
689 ret = GATE_RESULT_NOTAVAILABLE;
690 break;
691 }
692 procs = kvm_getproc2_func(kvm, KERN_PROC_PID, (int)proc_id, sizeof(struct kinfo_proc2), &procs_count);
693 if ((procs == NULL) || (procs_count == 0))
694 {
695 GATE_DEBUG_TRACE("kvm_getprocs(KERN_PROC_PID) failed");
696 ret = GATE_RESULT_NOMATCH;
697 break;
698 }
699
700 ret = load_process_infobuffer(infobuffer, procs);
701 } while (0);
702
703 if (kvm != NULL)
704 {
705 kvm_close_func(kvm);
706 }
707
708 return ret;
709 }
710
711 #else /* OpenBSD and FreeBSD: */
712
713 #include <sys/user.h>
714
715 static gate_result_t load_process_infobuffer(gate_process_infobuffer_t* target, struct kinfo_proc const* src)
716 {
717 gate_result_t ret = GATE_RESULT_OK;
718 gate_mem_clear(target, sizeof(gate_process_infobuffer_t));
719 #if defined(GATE_SYS_OPENBSD)
720 target->process.pid = src->p_pid;
721 target->process.parent_pid = src->p_ppid;
722 target->process.memory_used = src->p_vm_rssize;
723 target->process.uptime = src->p_uutime_sec * 1000 + src->p_ustime_sec * 1000 + src->p_rtime_sec * 1000;
724 target->process.resources = src->p_tid;
725 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->p_comm, gate_str_length(src->p_comm));
726 target->process.name = target->namebuffer;
727 gate_str_print_int64(target->ownerbuffer, sizeof(target->ownerbuffer), src->p_ruid);
728 target->process.owner = target->ownerbuffer;
729 #elif defined(GATE_SYS_DARWIN)
730 target->process.pid = src->kp_proc.p_pid;
731 target->process.parent_pid = src->kp_eproc.e_ppid;
732 target->process.memory_used = 1; /* TODO */
733 target->process.uptime = src->kp_proc.p_swtime;
734 target->process.cputime = src->kp_proc.p_rtime.tv_sec * 1000 + src->kp_proc.p_rtime.tv_usec / 1000;
735 target->process.resources = 1; /* TODO */
736 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->kp_proc.p_comm, gate_str_length(src->kp_proc.p_comm));
737 target->process.name = target->namebuffer;
738 gate_str_print_text(target->ownerbuffer, sizeof(target->ownerbuffer), src->kp_eproc.e_login, gate_str_length(src->kp_eproc.e_login));
739 target->process.owner = target->ownerbuffer;
740 #else
741 target->process.pid = src->ki_pid;
742 target->process.parent_pid = src->ki_ppid;
743 target->process.memory_used = src->ki_size;
744 target->process.uptime = src->ki_start.tv_sec * 1000 + src->ki_start.tv_usec / 1000;
745 target->process.cputime = src->ki_runtime;
746 target->process.resources = src->ki_numthreads;
747 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->ki_comm, gate_str_length(src->ki_comm));
748 target->process.name = target->namebuffer;
749 gate_str_print_text(target->ownerbuffer, sizeof(target->ownerbuffer), src->ki_login, gate_str_length(src->ki_login));
750 target->process.owner = target->ownerbuffer;
751 #endif
752
753 return ret;
754 }
755
756 gate_result_t gate_process_getinfo(gate_process_infobuffer_t* infobuffer, gate_process_id_t proc_id, gate_enumint_t flags)
757 {
758 gate_result_t ret = GATE_RESULT_FAILED;
759
760 do
761 {
762 int names[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)proc_id, sizeof(struct kinfo_proc), 1 };
763 #if defined(GATE_SYS_OPENBSD)
764 unsigned name_len = 6;
765 #else
766 unsigned name_len = 4;
767 #endif
768 char buffer[8192 + 4096];
769 size_t buffer_len = sizeof(buffer);
770 int result;
771
772 result = sysctl(names, name_len, &buffer[0], &buffer_len, NULL, 0);
773 if (result < 0)
774 {
775 GATE_DEBUG_TRACE_PLATFORM_ERROR("sysctl() failed");
776 GATE_DEBUG_TRACE_MSG_VALUE("PID", proc_id);
777 ret = GATE_RESULT_FAILED;
778 break;
779 }
780 ret = load_process_infobuffer(infobuffer, (struct kinfo_proc const*)buffer);
781 } while (0);
782 return ret;
783 }
784
785 #endif /* NETBSD or OPEN/FREEBSD variants for gate_process_getinfo() */
786
787
788 #if defined(GATE_SYS_DARWIN)
789
790 static gate_bool_t publish_process_info(int pid, gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
791 {
792 gate_process_infobuffer_t info;
793
794 int ppid = 0;
795 unsigned int uid = 0;
796 unsigned int gid = 0;
797 unsigned long long start_time = 0;
798 gate_result_t result;
799
800 gate_mem_clear(&info, sizeof(info));
801
802 info.process.pid = pid;
803
804 result = gate_darwin_get_pid_info(pid, &ppid, &uid, &gid, &start_time, info.namebuffer, sizeof(info.namebuffer));
805 if (GATE_SUCCEEDED(result))
806 {
807 info.process.parent_pid = ppid;
808 info.process.uptime = (gate_int64_t)start_time;
809 info.process.cputime = (gate_int64_t)start_time; /* TODO */
810 info.process.name = info.namebuffer;
811 }
812
813 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_PATH))
814 {
815 result = gate_darwin_get_pid_path(pid, info.pathbuffer, sizeof(info.pathbuffer), NULL);
816 if (GATE_SUCCEEDED(result))
817 {
818 info.process.path = info.pathbuffer;
819 }
820 }
821
822 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_MEMORY))
823 {
824 /* TODO */
825 }
826
827 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_MEMORY))
828 {
829 /* TODO */
830 }
831
832 return callback(&info.process, userparam);
833 }
834
835 static gate_result_t darwin_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
836 {
837 int pids[1024 + 512 + 256];
838 gate_size_t pids_used = 0;
839 gate_result_t result = gate_darwin_list_pids(&pids[0], sizeof(pids) / sizeof(pids[0]), &pids_used);
840 GATE_RETURN_IF_FAILED(result);
841 if (callback)
842 {
843 gate_size_t ndx;
844 for (ndx = 0; ndx != pids_used; ++ndx)
845 {
846 if (!publish_process_info(pids[ndx], callback, userparam, flags))
847 {
848 break;
849 }
850 }
851 }
852 return GATE_RESULT_OK;
853 }
854
855 #elif defined(GATE_SYS_FREEBSD)
856
857 typedef struct kinfo_proc* (*kinfo_getallproc_func_t)(int* cntp);
858
859 static void* volatile libutil = NULL;
860
861 static void close_libutil_atexit()
862 {
863 if (NULL != libutil)
864 {
865 void* handle = libutil;
866 libutil = NULL;
867 gate_posix_dlclose(handle);
868 }
869 }
870
871 static kinfo_getallproc_func_t load_getallproc()
872 {
873 if (libutil == NULL)
874 {
875 libutil = gate_posix_dlopen("libutil.so", RTLD_GLOBAL | RTLD_NOW);
876 if (NULL == libutil)
877 {
878 return NULL;
879 }
880 else
881 {
882 gate_platform_atexit(&close_libutil_atexit);
883 }
884 }
885 return (kinfo_getallproc_func_t)gate_posix_dlsym(libutil, "kinfo_getallproc");
886 }
887
888 static gate_result_t freebsd_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
889 {
890 int procs_count = 0;
891 int ndx;
892 gate_process_infobuffer_t infobuffer;
893 struct kinfo_proc* procs;
894 kinfo_getallproc_func_t kinfo_getallproc_func = load_getallproc();
895
896 if (!kinfo_getallproc_func)
897 {
898 return GATE_RESULT_NOTSUPPORTED;
899 }
900
901 procs = kinfo_getallproc_func(&procs_count);
902
903 if (procs == NULL)
904 {
905 return GATE_RESULT_FAILED;
906 }
907
908 for (ndx = 0; ndx < procs_count; ++ndx)
909 {
910 gate_result_t result = load_process_infobuffer(&infobuffer, &procs[ndx]);
911 if (GATE_FAILED(result))
912 {
913 continue;
914 }
915 if (!callback(&infobuffer.process, userparam))
916 {
917 break;
918 }
919 }
920
921 free(procs);
922 return GATE_RESULT_OK;
923 }
924
925 #elif defined(GATE_SYS_OPENBSD)
926
927 static gate_result_t openbsd_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
928 {
929 gate_result_t ret = GATE_RESULT_FAILED;
930 char buffer[8192 + 4096];
931 char* ptr_buffer = &buffer[0];
932
933 do
934 {
935 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), 0 };
936 unsigned mib_len = sizeof(mib) / sizeof(mib[0]);
937 size_t bufferlen = 0;
938 struct kinfo_proc* procs;
939 size_t procs_count;
940
941 /* retrieve required buffer len */
942 if (0 != sysctl(mib, mib_len, NULL, &bufferlen, NULL, 0))
943 {
944 GATE_DEBUG_TRACE_PLATFORM_ERROR("sysctl() failed");
945 ret = GATE_RESULT_NOTAVAILABLE;
946 break;
947 }
948
949 if (bufferlen > sizeof(buffer))
950 {
951 ptr_buffer = gate_mem_alloc(bufferlen);
952 if (ptr_buffer == NULL)
953 {
954 GATE_DEBUG_TRACE_MSG_VALUE("Out of memory", bufferlen);
955 ret = GATE_RESULT_OUTOFMEMORY;
956 break;
957 }
958 }
959
960 procs_count = bufferlen / sizeof(struct kinfo_proc);
961 mib[5] = procs_count;
962
963 if (0 != sysctl(mib, mib_len, ptr_buffer, &bufferlen, NULL, 0))
964 {
965 GATE_DEBUG_TRACE_PLATFORM_ERROR("sysctl() failed");
966 ret = GATE_RESULT_FAILED;
967 break;
968 }
969
970 procs = (struct kinfo_proc*)ptr_buffer;
971 while (procs_count-- > 0)
972 {
973 gate_process_infobuffer_t info_buffer;
974 gate_result_t result = load_process_infobuffer(&info_buffer, procs);
975 if (GATE_SUCCEEDED(result) && (callback != NULL))
976 {
977 if (!callback(&info_buffer.process, userparam))
978 {
979 break;
980 }
981 }
982 ++procs;
983 }
984 ret = GATE_RESULT_OK;
985 } while (0);
986
987 if ((ptr_buffer != NULL) && (ptr_buffer != &buffer[0]))
988 {
989 /* free allocated memory */
990 gate_mem_dealloc(ptr_buffer);
991 }
992 return ret;
993 }
994
995 #elif defined(GATE_SYS_NETBSD)
996
997 static gate_result_t netbsd_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
998 {
999 gate_result_t ret;
1000 kvm_t* kvm = NULL;
1001
1002 do
1003 {
1004 struct kinfo_proc2* procs = NULL;
1005 int procs_count = 0;
1006 int ndx;
1007 char errbuf[_POSIX2_LINE_MAX];
1008
1009 ret = load_kvm_library();
1010 GATE_BREAK_IF_FAILED(ret);
1011
1012 kvm = kvm_open_func(NULL, NULL, NULL, O_RDONLY, errbuf);
1013 if (kvm == NULL)
1014 {
1015 GATE_DEBUG_TRACE("kvm_open() failed");
1016 errbuf[_POSIX2_LINE_MAX - 1] = 0;
1017 GATE_DEBUG_TRACE(errbuf);
1018 ret = GATE_RESULT_NOTAVAILABLE;
1019 break;
1020 }
1021
1022 procs = kvm_getproc2_func(kvm, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &procs_count);
1023 if ((procs == NULL) || (procs_count == 0))
1024 {
1025 /* finding no processes is an error, we should at least find our own process */
1026 GATE_DEBUG_TRACE("kvm_getprocs(KERN_PROC_ALL) failed");
1027 ret = GATE_RESULT_FAILED;
1028 break;
1029 }
1030
1031 /* success case: iterator over infos */
1032 for (ndx = 0; ndx != procs_count; ++ndx, ++procs)
1033 {
1034 gate_process_infobuffer_t infobuffer;
1035 gate_result_t result = load_process_infobuffer(&infobuffer, procs);
1036 if (GATE_FAILED(result))
1037 {
1038 continue;
1039 }
1040 if (callback)
1041 {
1042 if (!callback(&infobuffer.process, userparam))
1043 {
1044 break;
1045 }
1046 }
1047 }
1048 } while (0);
1049
1050 if (kvm != NULL)
1051 {
1052 kvm_close_func(kvm);
1053 }
1054 return ret;
1055 }
1056
1057 #endif
1058
1059 #else /* Linux PROCFS */
1060
1061 8 gate_result_t gate_process_getinfo(gate_process_infobuffer_t* infobuffer, gate_process_id_t proc_id, gate_enumint_t flags)
1062 {
1063 8 gate_result_t ret = GATE_RESULT_NOTAVAILABLE;
1064 gate_size_t namepos;
1065 gate_procfs_proc_stat_t proc_stat;
1066 gate_procfs_memstat_t mem_stat;
1067 gate_uint64_t page_size;
1068 gate_uint64_t ticks_per_second;
1069 gate_uint64_t starttime_after_boot_ms;
1070 gate_int64_t system_up_time_ms;
1071 gate_posix_user_info_t user_info;
1072 gate_result_t result;
1073 int proc_uid;
1074
1075 8 gate_mem_clear(infobuffer, sizeof(gate_process_infobuffer_t));
1076
1077 8 infobuffer->process.pid = proc_id;
1078
1079
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_get_exe_path(proc_id, infobuffer->pathbuffer, sizeof(infobuffer->pathbuffer)))
1080 {
1081 8 infobuffer->process.path = infobuffer->pathbuffer;
1082 8 ret = GATE_RESULT_OK;
1083 }
1084
1085
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_load_proc_stat(proc_id, &proc_stat))
1086 {
1087 8 infobuffer->process.parent_pid = proc_stat.ppid;
1088
1089
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_TIMES))
1090 {
1091 8 ticks_per_second = (gate_uint64_t)sysconf(_SC_CLK_TCK);
1092 8 starttime_after_boot_ms = (gate_int64_t)proc_stat.starttime * 1000ULL / ticks_per_second;
1093
1094 8 system_up_time_ms = gate_procfs_get_uptime_ms();
1095
1096 8 infobuffer->process.uptime = system_up_time_ms - starttime_after_boot_ms;
1097
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.uptime < 0) infobuffer->process.uptime = 0;
1098 8 infobuffer->process.cputime = (gate_uint64_t)(proc_stat.utime + proc_stat.stime) * 1000ULL / ticks_per_second;
1099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.cputime < 0) infobuffer->process.cputime = 0;
1100 }
1101 8 ret = GATE_RESULT_OK;
1102 }
1103
1104
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_NAME))
1105 {
1106
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (infobuffer->process.path != NULL)
1107 {
1108 8 namepos = gate_str_char_pos_last(infobuffer->process.path, gate_str_length(infobuffer->process.path), '/');
1109
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (namepos != GATE_STR_NPOS)
1110 {
1111 8 infobuffer->process.name = &infobuffer->process.path[namepos + 1];
1112 }
1113 }
1114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.name == NULL)
1115 {
1116 gate_str_print_uint64(infobuffer->namebuffer, sizeof(infobuffer->namebuffer), proc_id);
1117 infobuffer->process.name = infobuffer->namebuffer;
1118 }
1119 }
1120
1121
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_OWNER))
1122 {
1123
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_get_exe_owner(proc_id, &proc_uid, NULL))
1124 {
1125 8 result = gate_posix_get_user_info(proc_uid, &user_info);
1126
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_SUCCEEDED(result))
1127 {
1128 8 gate_str_print_text(infobuffer->ownerbuffer, sizeof(infobuffer->ownerbuffer),
1129 user_info.name, gate_str_length(user_info.name));
1130 8 infobuffer->process.owner = infobuffer->ownerbuffer;
1131 }
1132 }
1133 }
1134
1135
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_MEMORY))
1136 {
1137
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_get_memstat(proc_id, &mem_stat))
1138 {
1139 8 page_size = sysconf(_SC_PAGESIZE);
1140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (page_size == 0)
1141 {
1142 page_size = 4096;
1143 }
1144 8 infobuffer->process.memory_used = (gate_uint64_t)mem_stat.resident * page_size;
1145 }
1146 }
1147
1148
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_RESOURCES))
1149 {
1150 8 infobuffer->process.resources = gate_procfs_get_open_fds(proc_id);
1151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.resources < 0) infobuffer->process.resources = 0;
1152 }
1153
1154 8 return ret;
1155 }
1156
1157 #endif
1158
1159 1 gate_result_t gate_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
1160 {
1161 1 gate_result_t ret = GATE_RESULT_FAILED;
1162 DIR* dir;
1163
1164 #if defined(GATE_SYS_DARWIN)
1165 ret = darwin_process_enum(callback, userparam, flags);
1166 #elif defined(GATE_SYS_BSD)
1167
1168 #if defined(GATE_SYS_FREEBSD)
1169 ret = freebsd_process_enum(callback, userparam, flags);
1170 #elif defined(GATE_SYS_OPENBSD)
1171 ret = openbsd_process_enum(callback, userparam, flags);
1172 #elif defined(GATE_SYS_NETBSD)
1173 ret = netbsd_process_enum(callback, userparam, flags);
1174 #endif
1175
1176 if (GATE_SUCCEEDED(ret))
1177 {
1178 /* we have a result, we are done */
1179 return GATE_RESULT_OK;
1180 }
1181 /* otherwise, continue with linux/procfs approach */
1182
1183 #endif /* GATE_SYS_BSD */
1184
1185 /* linux procfs: */
1186 1 dir = opendir("/proc");
1187
1188
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (dir != NULL)
1189 {
1190 struct dirent* entry;
1191
2/2
✓ Branch 1 taken 65 times.
✓ Branch 2 taken 1 times.
66 while (NULL != (entry = readdir(dir)))
1192 {
1193 gate_process_infobuffer_t proc_data;
1194 65 gate_uint64_t procid = 0;
1195 65 gate_size_t entrylen = gate_str_length(entry->d_name);
1196 65 gate_size_t parselen = gate_str_parse_uint64(entry->d_name, entrylen, &procid);
1197
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 58 times.
65 if (parselen == entrylen)
1198 {
1199 /* seems to be a numeric process id */
1200 7 ret = gate_process_getinfo(&proc_data, (gate_process_id_t)procid, flags);
1201
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (GATE_SUCCEEDED(ret))
1202 {
1203
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (false == callback(&proc_data.process, userparam))
1204 {
1205 break;
1206 }
1207 }
1208 }
1209 }
1210 1 closedir(dir);
1211 1 ret = GATE_RESULT_OK;
1212 }
1213 1 return ret;
1214 }
1215
1216
1217 16 gate_result_t gate_process_start(gate_string_t const* executablepath,
1218 gate_string_t const* args, gate_size_t argcount,
1219 gate_string_t const* workdir, gate_string_t const* envvars, gate_size_t envvarcount, gate_enumint_t flags,
1220 gate_process_handle_t* result_handle,
1221 gate_process_id_t* result_pid,
1222 gate_process_stream_t** result_stream
1223 )
1224 {
1225 16 return gate_process_start_ex(executablepath, args, argcount,
1226 workdir, envvars, envvarcount, flags,
1227 NULL, NULL, NULL,
1228 result_handle, result_pid, result_stream);
1229 }
1230
1231
1232 34 static char* gate_process_build_argarray(gate_string_t const* args, gate_size_t argcount, char** buffer, gate_size_t maxargs)
1233 {
1234 char* ret;
1235 char* ptr;
1236 gate_size_t ndx;
1237 34 gate_size_t byte_size = 0;
1238 gate_size_t len;
1239 34 gate_size_t buffer_ndx = 0;
1240
1241
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (argcount >= maxargs) argcount = maxargs - 1;
1242
1243
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 34 times.
61 for (ndx = 0; ndx < argcount; ++ndx)
1244 {
1245 27 byte_size += args[ndx].length + 1;
1246 }
1247 34 byte_size += 2;
1248
1249 34 ret = gate_mem_alloc(byte_size);
1250
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if (ret != NULL)
1251 {
1252 34 ptr = ret;
1253
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 34 times.
61 for (ndx = 0; ndx < argcount; ++ndx)
1254 {
1255 27 len = gate_str_print_text(ptr, args[ndx].length + 1, args[ndx].str, args[ndx].length);
1256 27 buffer[buffer_ndx++] = ptr;
1257 27 ptr += (len + 1);
1258 }
1259 34 ptr[0] = 0;
1260 34 buffer[buffer_ndx] = NULL;
1261 }
1262 34 return ret;
1263 }
1264
1265 17 static int gate_get_open_max(void)
1266 {
1267 #ifdef OPEN_MAX
1268 return OPEN_MAX;
1269 #else
1270 static volatile int cached_max = 0;
1271
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (cached_max == 0)
1272 {
1273 17 long cnf_open_max = sysconf(_SC_OPEN_MAX);
1274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (cnf_open_max <= 0)
1275 {
1276 cached_max = 1024;
1277 }
1278 else
1279 {
1280 17 cached_max = (int)cnf_open_max;
1281 }
1282 }
1283 17 return cached_max;
1284 #endif
1285 }
1286
1287
1288 17 static void close_private_file_descriptor(gate_bool_t exclude_inheritable_fds, int preserved_fd)
1289 {
1290 int inheritable_fds[1024];
1291 17 gate_size_t inheritable_count = 0;
1292 17 int fd_max = gate_get_open_max();
1293 int fd;
1294
1295 17 inheritable_fds[0] = preserved_fd;
1296 17 inheritable_count = 1;
1297
1298
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 7 times.
17 if (exclude_inheritable_fds)
1299 {
1300 10 inheritable_count = gate_posix_get_inheritable_fds(
1301 &inheritable_fds[1], sizeof(inheritable_fds) / sizeof(inheritable_fds[0]) - 1);
1302 10 ++inheritable_count;
1303 }
1304
1305
1306
2/2
✓ Branch 0 taken 17825741 times.
✓ Branch 1 taken 17 times.
17825758 for (fd = STDERR_FILENO + 1; fd < fd_max; ++fd)
1307 {
1308 17825741 gate_bool_t exclude_fd = false;
1309 size_t index;
1310
2/2
✓ Branch 0 taken 17825741 times.
✓ Branch 1 taken 17825724 times.
35651465 for (index = 0; index != inheritable_count; ++index)
1311 {
1312
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17825724 times.
17825741 if (fd == inheritable_fds[index])
1313 {
1314 17 exclude_fd = true;
1315 17 break;
1316 }
1317 }
1318
2/2
✓ Branch 0 taken 17825724 times.
✓ Branch 1 taken 17 times.
17825741 if (!exclude_fd)
1319 {
1320 17825724 gate_posix_close(fd);
1321 }
1322 }
1323 17 }
1324
1325 static gate_result_t gate_process_switch_user(char const* username)
1326 {
1327 gate_result_t ret;
1328 do
1329 {
1330 gate_posix_user_info_t user_info;
1331 int result;
1332
1333 ret = gate_posix_resolve_user_info(username, &user_info);
1334 GATE_BREAK_IF_FAILED(ret);
1335
1336 result = initgroups(user_info.name, user_info.gid);
1337 if (-1 == result)
1338 {
1339 GATE_DEBUG_TRACE_MSG_VALUE("initgroups failed()", gate_posix_errno(NULL));
1340 }
1341
1342 result = setgid(user_info.gid);
1343 if (result != 0)
1344 {
1345 GATE_DEBUG_TRACE_MSG_VALUE("setgid failed()", gate_posix_errno(NULL));
1346 ret = gate_platform_print_last_error(NULL, 0);
1347 break;
1348 }
1349
1350 result = setuid(user_info.uid);
1351 if (result != 0)
1352 {
1353 GATE_DEBUG_TRACE_MSG_VALUE("setuid failed()", gate_posix_errno(NULL));
1354 ret = gate_platform_print_last_error(NULL, 0);
1355 break;
1356 }
1357 ret = GATE_RESULT_OK;
1358 } while (0);
1359
1360 return ret;
1361 }
1362
1363 typedef int(*openpty_func_t)(int* amaster, int* aslave, char const* name, struct termios const* termp, struct winsize const* winp);
1364 typedef int(*login_tty_func_t)(int fd);
1365
1366 static openpty_func_t openpty_func;
1367 static login_tty_func_t login_tty_func;
1368
1369 20 static gate_bool_t are_pty_functions_loaded()
1370 {
1371
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
20 return (openpty_func != NULL) && (login_tty_func != NULL);
1372 }
1373
1374 6 static gate_bool_t try_load_pty_functions(void* libhandle)
1375 {
1376 6 openpty_func = (openpty_func_t)gate_posix_dlsym(libhandle, "openpty");
1377 6 login_tty_func = (login_tty_func_t)gate_posix_dlsym(libhandle, "login_tty");
1378 6 return are_pty_functions_loaded();
1379 }
1380
1381 static char const* util_libs[] =
1382 {
1383 "libutil.so",
1384 "libutil.so.1"
1385 };
1386 static unsigned const util_libs_count = sizeof(util_libs) / sizeof(util_libs[0]);
1387
1388 static void* volatile pty_lib_handle = NULL;
1389
1390 6 static void close_pty_lib_atexit()
1391 {
1392
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (NULL != pty_lib_handle)
1393 {
1394 6 void* handle = pty_lib_handle;
1395 6 pty_lib_handle = NULL;
1396 6 gate_posix_dlclose(handle);
1397 }
1398 6 }
1399
1400 8 static gate_bool_t load_pty_functions()
1401 {
1402
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
8 if (are_pty_functions_loaded())
1403 {
1404 2 return true;
1405 }
1406
1407 6 openpty_func = (openpty_func_t)gate_posix_global_sym("openpty");
1408 6 login_tty_func = (login_tty_func_t)gate_posix_global_sym("login_tty");
1409
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (are_pty_functions_loaded())
1410 {
1411 return true;
1412 }
1413
1414 unsigned ndx;
1415
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 for (ndx = 0; ndx != util_libs_count; ++ndx)
1416 {
1417 6 void* lib_handle = gate_posix_dlopen(util_libs[ndx], RTLD_GLOBAL | RTLD_NOW);
1418
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (lib_handle)
1419 {
1420
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (try_load_pty_functions(lib_handle))
1421 {
1422 6 pty_lib_handle = lib_handle;
1423 6 gate_platform_atexit(&close_pty_lib_atexit);
1424 6 return true;
1425 }
1426 else
1427 {
1428 gate_posix_dlclose(lib_handle);
1429 }
1430 }
1431 }
1432 return false;
1433 }
1434
1435 17 static gate_result_t gate_process_exec(gate_string_t const* executablepath, gate_string_t const* args, gate_size_t argcount,
1436 gate_string_t const* workdir, gate_string_t const* envvars, gate_size_t envvarcount, gate_enumint_t flags,
1437 char const* sessionlocation,
1438 char const* username, char const* password, int preserved_fd,
1439 int* ptr_tty_fd,
1440 int* new_std_in, int* new_std_out, int* new_std_err
1441 )
1442 {
1443 17 gate_result_t ret = GATE_RESULT_FAILED;
1444 int result;
1445 char* process_args[256];
1446 char* process_vars[256];
1447 char new_work_dir[GATE_MAX_FILEPATH_LENGTH];
1448 char exe_path[GATE_MAX_FILEPATH_LENGTH];
1449 17 char* process_args_buffer = NULL;
1450 17 char* process_vars_buffer = NULL;
1451
1452 do
1453 {
1454
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 14 times.
17 if (!gate_string_is_empty(workdir))
1455 {
1456 3 GATE_STRING_TO_BUFFER(workdir, new_work_dir);
1457
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (-1 == chdir(new_work_dir))
1458 {
1459 ret = GATE_RESULT_INVALIDINPUT;
1460 break;
1461 }
1462 }
1463
1464
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if (!gate_str_is_empty(username))
1465 {
1466 ret = gate_process_switch_user(username);
1467 GATE_BREAK_IF_FAILED(ret);
1468 }
1469
1470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_START_NEWSESSION))
1471 {
1472 result = setsid();
1473 if (result != 0)
1474 {
1475 GATE_DEBUG_TRACE_MSG_VALUE("setsid() failed", gate_posix_errno(NULL));
1476 ret = GATE_RESULT_FAILED;
1477 break;
1478 }
1479 }
1480
1481 17 process_args_buffer = gate_process_build_argarray(args, argcount, &process_args[1], sizeof(process_args) / sizeof(process_args[0]) - 1);
1482 17 process_vars_buffer = gate_process_build_argarray(envvars, envvarcount, process_vars, sizeof(process_vars) / sizeof(process_vars[0]));
1483
1484 17 GATE_STRING_TO_BUFFER(executablepath, exe_path);
1485 17 process_args[0] = exe_path;
1486
1487
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if (ptr_tty_fd)
1488 {
1489 /* login to terminal and set STD* to terminal fd */
1490
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (login_tty_func(*ptr_tty_fd) == -1)
1491 {
1492 GATE_DEBUG_TRACE_MSG_VALUE("login_tty() failed", gate_posix_errno(NULL));
1493 ret = GATE_RESULT_FAILED;
1494 break;
1495 }
1496 }
1497 else
1498 {
1499 /* redirect parent-pipes to STD* fds */
1500
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (new_std_in)
1501 {
1502 9 dup2(*new_std_in, STDIN_FILENO);
1503 9 close(*new_std_in);
1504 }
1505
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (new_std_out)
1506 {
1507 9 dup2(*new_std_out, STDOUT_FILENO);
1508 9 close(*new_std_out);
1509 }
1510
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (new_std_err)
1511 {
1512 9 dup2(*new_std_err, STDERR_FILENO);
1513 9 close(*new_std_err);
1514 }
1515 }
1516
1517 17 close_private_file_descriptor(!GATE_FLAG_ENABLED(flags, GATE_PROCESS_START_NOINHERIT), preserved_fd);
1518
1519
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (envvars)
1520 {
1521 execve(exe_path, process_args, process_vars);
1522 }
1523 else
1524 {
1525 17 execv(exe_path, process_args);
1526 }
1527
1528 17 ret = GATE_RESULT_UNKNOWNERROR;
1529
1530 } while (0);
1531
1532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (process_args_buffer)
1533 {
1534 gate_mem_dealloc(process_args_buffer);
1535 }
1536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (process_vars_buffer)
1537 {
1538 gate_mem_dealloc(process_vars_buffer);
1539 }
1540 return ret;
1541 }
1542
1543
1544 #define INVALID_FD (-1)
1545
1546 187 static void close_valid_fd(int fd)
1547 {
1548
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 152 times.
187 if (fd != INVALID_FD)
1549 {
1550 35 close(fd);
1551 }
1552 187 }
1553
1554 17 gate_result_t gate_process_start_ex(gate_string_t const* executablepath,
1555 gate_string_t const* args, gate_size_t argcount,
1556 gate_string_t const* workdir, gate_string_t const* envvars, gate_size_t envvarcount, gate_enumint_t flags,
1557 char const* sessionlocation,
1558 char const* username, char const* password,
1559 gate_process_handle_t* result_handle,
1560 gate_process_id_t* result_pid,
1561 gate_process_stream_t** result_stream
1562 )
1563 {
1564 17 gate_result_t ret = GATE_RESULT_FAILED;
1565 17 int failpipes[2] = { INVALID_FD, INVALID_FD };
1566 17 int std_in_pipe[2] = { INVALID_FD, INVALID_FD }; /* stdin of child process */
1567 17 int std_out_pipe[2] = { INVALID_FD, INVALID_FD }; /* stdout of child process */
1568 17 int std_err_pipe[2] = { INVALID_FD, INVALID_FD }; /* stderr of child process */
1569 17 pid_t process_id = -1;
1570 17 gate_process_stream_t* proc_stream = NULL;
1571 17 int term_master = INVALID_FD;
1572 17 int term_slave = INVALID_FD;
1573
1574 do
1575 {
1576 ssize_t readlen;
1577 17 gate_result_t process_exec_error = 0;
1578
1579
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (result_stream)
1580 {
1581
1582
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_START_NEWTERMINAL))
1583 {
1584 /* child process will run in a pseudo terminal, parent controls master-side */
1585 char term_slave_name[1024];
1586 /*char const* ptr_term_slave_name = NULL;*/
1587
1588
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!load_pty_functions())
1589 {
1590 GATE_DEBUG_TRACE("load_pty_functions() failed");
1591 ret = GATE_RESULT_NOTSUPPORTED;
1592 break;
1593 }
1594
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (openpty_func(&term_master, &term_slave, term_slave_name, NULL, NULL) == -1)
1595 {
1596 GATE_DEBUG_TRACE("openpty() failed");
1597 ret = GATE_RESULT_OUTOFRESOURCES;
1598 break;
1599 }
1600 /*ptr_term_slave_name = &term_slave_name[0];*/
1601
1602 /* parent process read/write ends are set to terminal-master */
1603 8 std_in_pipe[1] = term_master;
1604 8 std_out_pipe[0] = dup(term_master);
1605 8 std_err_pipe[0] = dup(term_master);
1606 8 term_master = INVALID_FD;
1607 }
1608 else
1609 {
1610 /* child process i/o will be directed to pipes controlled by parent */
1611
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if (pipe(std_in_pipe) != 0)
1612 {
1613 GATE_DEBUG_TRACE("pipe(std_in) failed");
1614 ret = GATE_RESULT_OUTOFRESOURCES;
1615 break;
1616 }
1617
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if (pipe(std_out_pipe) != 0)
1618 {
1619 GATE_DEBUG_TRACE("pipe(std_out) failed");
1620 ret = GATE_RESULT_OUTOFRESOURCES;
1621 break;
1622 }
1623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_START_USESTDERR))
1624 {
1625 if (pipe(std_err_pipe) != 0)
1626 {
1627 GATE_DEBUG_TRACE("pipe(std_err) failed");
1628 ret = GATE_RESULT_OUTOFRESOURCES;
1629 break;
1630 }
1631 }
1632 else
1633 {
1634 9 std_err_pipe[0] = dup(std_out_pipe[0]);
1635 9 std_err_pipe[1] = dup(std_out_pipe[1]);
1636 }
1637 }
1638
1639 17 proc_stream = gate_process_stream_create(std_in_pipe[1], std_out_pipe[0], std_err_pipe[0]);
1640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!proc_stream)
1641 {
1642 GATE_DEBUG_TRACE("gate_process_stream_create() failed");
1643 ret = GATE_RESULT_OUTOFMEMORY;
1644 break;
1645 }
1646 /* fds are now handled by stream object */
1647 17 std_in_pipe[1] = INVALID_FD;
1648 17 std_out_pipe[0] = INVALID_FD;
1649 17 std_err_pipe[0] = INVALID_FD;
1650 }
1651
1652
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if (pipe(failpipes) != 0)
1653 {
1654 GATE_DEBUG_TRACE("pipe(failpipes) failed");
1655 ret = GATE_RESULT_OUTOFRESOURCES;
1656 break;
1657 }
1658
1659 17 process_id = fork();
1660
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (process_id == -1)
1661 {
1662 /* fork has failed */
1663 GATE_DEBUG_TRACE("fork() failed");
1664 ret = GATE_RESULT_EXECUTIONFAILED;
1665 break;
1666 }
1667
1668
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
34 if (process_id == 0)
1669 {
1670 /* child process */
1671 ssize_t writelen;
1672 17 close(failpipes[0]); /* close read descriptor of fail-detector-pipe */
1673 17 failpipes[0] = INVALID_FD;
1674 17 fcntl(failpipes[1], F_SETFD, FD_CLOEXEC); /* auto-close on-exec on write descriptor of fail-detector-pipe */
1675
1676
3/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
34 process_exec_error = gate_process_exec(
1677 executablepath, args, argcount, workdir, envvars, envvarcount, flags,
1678 sessionlocation, username, password,
1679 failpipes[1], /* exclude failpipe from auto-close */
1680
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 (term_slave != INVALID_FD) ? &term_slave : NULL, /* use terminal-slave fd if available */
1681 result_stream ? &std_in_pipe[0] : NULL, /* redirect std-in */
1682 result_stream ? &std_out_pipe[1] : NULL, /* redirect std-out */
1683 result_stream ? &std_err_pipe[1] : NULL /* redirect std-err */
1684 );
1685
1686 writelen = write(failpipes[1], &process_exec_error, sizeof(process_exec_error));
1687 _exit(GATE_RESULT_TO_EXITCODE(process_exec_error));
1688 /* this code should not be reachable: */
1689 ret = GATE_RESULT_UNKNOWNERROR;
1690 break;
1691 }
1692
1693 /* parent process */
1694 17 close_valid_fd(term_slave);
1695 17 term_slave = INVALID_FD;
1696
1697 17 close(failpipes[1]); /* close write descriptor of fail-detector-pipe*/
1698 17 failpipes[1] = INVALID_FD;
1699
1700 17 readlen = read(failpipes[0], &process_exec_error, sizeof(process_exec_error));
1701 17 close(failpipes[0]);
1702 17 failpipes[0] = INVALID_FD;
1703
1704
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 13 times.
17 if (readlen > 0)
1705 {
1706 /* child process has failed to prepare exec */
1707 4 ret = process_exec_error;
1708 }
1709 else
1710 {
1711 /* when reading from 'failpipe' fails, then everything seems to be OK */
1712
1713
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (result_stream)
1714 {
1715 13 *result_stream = proc_stream;
1716
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (proc_stream)
1717 {
1718 13 ((gate_process_stream_impl_t*)proc_stream)->target_pid = process_id;
1719 13 gate_object_retain(proc_stream);
1720 }
1721 }
1722
1723
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 if (result_pid != NULL)
1724 {
1725 3 *result_pid = (gate_process_id_t)process_id;
1726 }
1727
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (result_handle != NULL)
1728 {
1729 13 *result_handle = (gate_process_handle_t)(gate_intptr_t)process_id;
1730 13 process_id = 0; /* protect process_id from being cleaned up */
1731 }
1732
1733 13 ret = GATE_RESULT_OK;
1734 }
1735 } while (0);
1736
1737
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 13 times.
17 if (process_id > 0)
1738 {
1739 /* detach process */
1740 4 int status = 0;
1741 4 waitpid(process_id, &status, WNOHANG);
1742 }
1743
1744 17 close_valid_fd(failpipes[1]);
1745 17 close_valid_fd(failpipes[0]);
1746 17 close_valid_fd(std_in_pipe[1]);
1747 17 close_valid_fd(std_in_pipe[0]);
1748 17 close_valid_fd(std_out_pipe[1]);
1749 17 close_valid_fd(std_out_pipe[0]);
1750 17 close_valid_fd(std_err_pipe[1]);
1751 17 close_valid_fd(std_err_pipe[0]);
1752 17 close_valid_fd(term_master);
1753 17 close_valid_fd(term_slave);
1754
1755
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (proc_stream)
1756 {
1757 17 gate_object_release(proc_stream);
1758 }
1759
1760 17 return ret;
1761 }
1762 12 gate_result_t gate_process_close(gate_process_handle_t* handle)
1763 {
1764 gate_result_t ret;
1765 pid_t pid;
1766 int status;
1767 pid_t result;
1768
1769
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1770 {
1771 ret = GATE_RESULT_OK;
1772 }
1773 else
1774 {
1775 12 pid = (pid_t)(gate_intptr_t)*handle;
1776 12 result = waitpid(pid, &status, WNOHANG);
1777
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (result == -1)
1778 {
1779 ret = GATE_RESULT_FAILED;
1780 }
1781 else
1782 {
1783 12 *handle = GATE_PROCESS_HANDLE_INVALID;
1784 12 ret = GATE_RESULT_OK;
1785 }
1786 }
1787
1788 12 return ret;
1789 }
1790 12 gate_result_t gate_process_get_exitcode(gate_process_handle_t* handle, int* exitcode)
1791 {
1792 12 gate_result_t ret = GATE_RESULT_FAILED;
1793 pid_t pid;
1794 12 siginfo_t si = GATE_INIT_EMPTY;
1795 int result;
1796 #if defined(GATE_SYS_OPENBSD)
1797 int status;
1798 #endif
1799 gate_timecounter_t tcbegin, tcnow;
1800 gate_int64_t timediff;
1801 gate_int64_t timeoutus;
1802 gate_uint32_t sleepcounter;
1803
1804
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1805 {
1806 return GATE_RESULT_INVALIDSTATE;
1807 }
1808
1809 12 pid = (pid_t)(gate_intptr_t)*handle;
1810
1811 #if defined(GATE_SYS_OPENBSD)
1812 result = waitpid(pid, &status, WNOHANG);
1813 if (-1 == result)
1814 {
1815 return GATE_RESULT_FAILED;
1816 }
1817 if (!WIFEXITED(status))
1818 {
1819 return GATE_RESULT_INVALIDSTATE;
1820 }
1821 if (exitcode)
1822 {
1823 *exitcode = WEXITSTATUS(status);
1824 }
1825 *handle = GATE_PROCESS_HANDLE_INVALID;
1826 return GATE_RESULT_OK;
1827 #else
1828 12 result = waitid(P_PID, pid, &si, WEXITED | WNOWAIT | WNOHANG);
1829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (result != 0)
1830 {
1831 return GATE_RESULT_NOTAVAILABLE;
1832 }
1833
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if(si.si_pid == 0)
1834 {
1835 /* exit status still not available, retry later */
1836 1 return GATE_RESULT_INVALIDSTATE;
1837 }
1838
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 else if (si.si_pid != pid)
1839 {
1840
1841 return GATE_RESULT_NOTAVAILABLE;
1842 }
1843
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (exitcode)
1844 {
1845 11 *exitcode = si.si_status;
1846 }
1847 11 return GATE_RESULT_OK;
1848 #endif
1849 }
1850
1851 4 gate_result_t gate_process_wait(gate_process_handle_t* handle, gate_uint32_t timeoutms)
1852 {
1853 4 gate_result_t ret = GATE_RESULT_FAILED;
1854 pid_t pid;
1855 siginfo_t si;
1856 int result;
1857 int status;
1858 gate_timecounter_t tcbegin, tcnow;
1859 gate_int64_t timediff;
1860 gate_int64_t timeoutus;
1861 gate_uint32_t sleepcounter;
1862
1863
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1864 {
1865 return GATE_RESULT_INVALIDSTATE;
1866 }
1867
1868 4 pid = (pid_t)(gate_intptr_t)*handle;
1869
1870
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (timeoutms == GATE_PROCESS_WAIT_INFINITE)
1871 {
1872 #if defined(GATE_SYS_OPENBSD)
1873 result = waitpid(pid, &status, 0);
1874 *handle = GATE_PROCESS_HANDLE_INVALID;
1875 #else
1876 2 result = waitid(P_PID, pid, &si, WEXITED | WNOWAIT);
1877 #endif
1878
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (result == 0)
1879 {
1880 2 ret = GATE_RESULT_OK;
1881 }
1882 else
1883 {
1884 gate_posix_errno(&ret);
1885 }
1886 }
1887 else
1888 {
1889
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (GATE_SUCCEEDED(gate_timecounter_now(&tcbegin)))
1890 {
1891 2 ret = GATE_RESULT_TIMEOUT;
1892 2 sleepcounter = 0;
1893 2 timeoutus = (gate_int64_t)timeoutms * 1000LL;
1894 do
1895 {
1896 #if defined(GATE_SYS_OPENBSD)
1897 result = waitpid(pid, &status, WNOHANG);
1898 *handle = GATE_PROCESS_HANDLE_INVALID;
1899 #else
1900 2 result = waitid(P_PID, pid, &si, WEXITED | WNOHANG | WNOWAIT);
1901 #endif
1902
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (result == 0)
1903 {
1904
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (si.si_pid == pid)
1905 {
1906
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if ((si.si_code == CLD_KILLED) || (si.si_code == CLD_EXITED))
1907 {
1908 1 ret = GATE_RESULT_OK;
1909 1 break;
1910 }
1911 }
1912 }
1913
1914 {
1915 /* reduce long-time polling */
1916 1 gate_thread_sleep((sleepcounter));
1917
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (sleepcounter < 100)
1918 {
1919 1 ++sleepcounter;
1920 }
1921 }
1922
1923
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GATE_FAILED(gate_timecounter_now(&tcnow)))
1924 {
1925 break;
1926 }
1927 1 timediff = gate_timecounter_diff(tcnow, tcbegin);
1928
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } while (timediff < timeoutus);
1929 }
1930 }
1931 4 return ret;
1932 }
1933 gate_result_t gate_process_wait_pid(gate_process_id_t pid, gate_uint32_t timeoutms)
1934 {
1935 return GATE_RESULT_NOTIMPLEMENTED;
1936 }
1937 1 gate_result_t gate_process_terminate(gate_process_handle_t* handle, gate_bool_t system_request)
1938 {
1939 gate_process_id_t pid;
1940
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1941 {
1942 return GATE_RESULT_INVALIDSTATE;
1943 }
1944 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1945 1 return gate_process_terminate_pid(pid, system_request);
1946 }
1947 2 gate_result_t gate_process_terminate_pid(gate_process_id_t pid, gate_bool_t system_request)
1948 {
1949 2 gate_result_t ret = GATE_RESULT_FAILED;
1950 2 pid_t proc_id = (pid_t)pid;
1951
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (-1 == kill(proc_id, system_request ? SIGTERM : SIGINT))
1952 {
1953 gate_posix_errno(&ret);
1954 }
1955 else
1956 {
1957 2 ret = GATE_RESULT_OK;
1958 }
1959 2 return ret;
1960 }
1961 1 gate_result_t gate_process_kill(gate_process_handle_t* handle)
1962 {
1963 gate_process_id_t pid;
1964
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1965 {
1966 return GATE_RESULT_INVALIDSTATE;
1967 }
1968 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1969 1 return gate_process_kill_pid(pid);
1970 }
1971 2 gate_result_t gate_process_kill_pid(gate_process_id_t pid)
1972 {
1973 2 gate_result_t ret = GATE_RESULT_FAILED;
1974 2 pid_t proc_id = (pid_t)pid;
1975
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (-1 == kill(proc_id, SIGKILL))
1976 {
1977 gate_posix_errno(&ret);
1978 }
1979 else
1980 {
1981 2 ret = GATE_RESULT_OK;
1982 }
1983 2 return ret;
1984 }
1985 1 gate_result_t gate_process_suspend(gate_process_handle_t* handle)
1986 {
1987 gate_process_id_t pid;
1988
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1989 {
1990 return GATE_RESULT_INVALIDSTATE;
1991 }
1992 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1993 1 return gate_process_suspend_pid(pid);
1994 }
1995 2 gate_result_t gate_process_suspend_pid(gate_process_id_t pid)
1996 {
1997 2 gate_result_t ret = GATE_RESULT_FAILED;
1998 2 pid_t proc_id = (pid_t)pid;
1999
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (-1 == kill(proc_id, SIGSTOP))
2000 {
2001 gate_posix_errno(&ret);
2002 }
2003 else
2004 {
2005 2 ret = GATE_RESULT_OK;
2006 }
2007 2 return ret;
2008 }
2009 1 gate_result_t gate_process_resume(gate_process_handle_t* handle)
2010 {
2011 gate_process_id_t pid;
2012
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
2013 {
2014 return GATE_RESULT_INVALIDSTATE;
2015 }
2016 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
2017 1 return gate_process_resume_pid(pid);
2018 }
2019 2 gate_result_t gate_process_resume_pid(gate_process_id_t pid)
2020 {
2021 2 gate_result_t ret = GATE_RESULT_FAILED;
2022 2 pid_t proc_id = (pid_t)pid;
2023
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (-1 == kill(proc_id, SIGCONT))
2024 {
2025 gate_posix_errno(&ret);
2026 }
2027 else
2028 {
2029 2 ret = GATE_RESULT_OK;
2030 }
2031 2 return ret;
2032 }
2033
2034 #endif /* GATE_SYS_POSIX */
2035