GCC Code Coverage Report


Directory: src/gate/
File: src/gate/processes_posix.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 293 588 49.8%
Functions: 23 42 54.8%
Branches: 114 332 34.3%

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 5 static void gate_process_stream_release(void* thisptr)
88 {
89 5 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
90
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if (gate_atomic_int_dec(&stream->ref_counter) == 0)
91 {
92
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (stream->fd_stdin_write >= 0)
93 {
94 1 close(stream->fd_stdin_write);
95 1 stream->fd_stdin_write = -1;
96 }
97
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (stream->fd_stdout_read >= 0)
98 {
99 1 close(stream->fd_stdout_read);
100 1 stream->fd_stdout_read = -1;
101 }
102
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (stream->fd_stderr_read >= 0)
103 {
104 1 close(stream->fd_stderr_read);
105 1 stream->fd_stderr_read = -1;
106 }
107 1 gate_mem_dealloc(stream);
108 }
109 5 }
110 static char const* gate_process_stream_interface_name(void* thisptr)
111 {
112 (void)thisptr;
113 return GATE_INTERFACE_NAME_PROCESSSTREAM;
114 }
115 4 static int gate_process_stream_retain(void* thisptr)
116 {
117 4 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
118 4 return gate_atomic_int_inc(&stream->ref_counter);
119 }
120
121 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 gate_mem_clear(&info, sizeof(info));
127 result = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
128 if (0 == result)
129 {
130 if ((info.si_code == CLD_EXITED)
131 || (info.si_code == CLD_KILLED)
132 || (info.si_code == CLD_DUMPED)
133 )
134 {
135 *ptr_terminated = true;
136 }
137 else
138 {
139 *ptr_terminated = false;
140 }
141 return GATE_RESULT_OK;
142 }
143 else
144 {
145 /* error */
146 return gate_platform_print_last_error(NULL, 0);
147 }
148 }
149
150 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 timeout.tv_sec = 0;
157 timeout.tv_usec = 0;
158
159 FD_ZERO(&read_set);
160 FD_SET(read_fd, &read_set);
161
162 result = select(read_fd + 1, &read_set, NULL, NULL, &timeout);
163 if (result < 0)
164 {
165 /* error */
166 return false;
167 }
168 if (FD_ISSET(read_fd, &read_set))
169 {
170 return true;
171 }
172 return false;
173 }
174
175 static int await_read_fd_or_process_exit(pid_t pid, int read_fd)
176 {
177 fd_set read_set;
178 fd_set err_set;
179 struct timeval timeout;
180 int result;
181
182 for (;;)
183 {
184 timeout.tv_sec = 0;
185 timeout.tv_usec = 100000;
186
187 FD_ZERO(&read_set);
188 FD_ZERO(&err_set);
189 FD_SET(read_fd, &read_set);
190 FD_SET(read_fd, &err_set);
191
192 result = select(read_fd + 1, &read_set, NULL, &err_set, &timeout);
193 if (result < 0)
194 {
195 /* error */
196 return -1;
197 }
198 if (FD_ISSET(read_fd, &err_set))
199 {
200 /* error */
201 return -1;
202 }
203 if (FD_ISSET(read_fd, &read_set))
204 {
205 /* can read */
206 return 1;
207 }
208
209 if (pid > 0)
210 {
211 gate_bool_t is_terminated = false;
212 gate_result_t wait_result = is_process_termined(pid, &is_terminated);
213
214 if (GATE_FAILED(wait_result))
215 {
216 /* failed to detect process state */
217 return -1;
218 }
219 else
220 {
221 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 static gate_result_t gate_process_stream_read(void* thisptr, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
234 {
235 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
236 gate_result_t ret = GATE_RESULT_FAILED;
237 gate_int32_t errcode;
238 ptrdiff_t result;
239 int wait_result;
240
241 if (stream->peek_buffer_used > 0)
242 {
243 /* there is something in the peek buffer */
244
245 if (bufferlength >= stream->peek_buffer_used)
246 {
247 /* we can read the whole peek-buffer */
248 gate_mem_copy(buffer, stream->peek_buffer, stream->peek_buffer_used);
249 if (returned) *returned = stream->peek_buffer_used;
250 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 return GATE_RESULT_OK;
261 }
262
263 wait_result = await_read_fd_or_process_exit(stream->target_pid, stream->fd_stdout_read);
264 if (wait_result < 0)
265 {
266 return GATE_RESULT_FAILED;
267 }
268 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 result = gate_posix_read(stream->fd_stdout_read, buffer, bufferlength);
279 if (result < 0)
280 {
281 errcode = gate_platform_get_last_error();
282 if (errcode == EIO)
283 {
284 gate_bool_t terminated = false;
285 gate_result_t wait_result = is_process_termined(stream->target_pid, &terminated);
286 if (GATE_SUCCEEDED(wait_result) && terminated)
287 {
288 /* signal end-of-stream, as process was terminated */
289 if (returned)
290 {
291 *returned = 0;
292 }
293 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 if (returned) *returned = (gate_size_t)result;
302 ret = GATE_RESULT_OK;
303 }
304 return ret;
305 }
306 static gate_result_t gate_process_stream_peek(void* thisptr, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
307 {
308 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
309 gate_size_t copylen;
310 ptrdiff_t result;
311 gate_int32_t errcode;
312
313 if (stream->peek_buffer_used > 0)
314 {
315 /* there is already something in the peek buffer */
316 copylen = bufferlength < stream->peek_buffer_used ? bufferlength : stream->peek_buffer_used;
317 gate_mem_copy(buffer, &stream->peek_buffer[0], copylen);
318 if (returned) *returned = copylen;
319 return GATE_RESULT_OK;
320 }
321
322 if (can_read_from_fd(stream->fd_stdout_read))
323 {
324 /* read some bytes into peek-buffer */
325 copylen = bufferlength < sizeof(stream->peek_buffer) ? bufferlength : sizeof(stream->peek_buffer);
326 result = gate_posix_read(stream->fd_stdout_read, &stream->peek_buffer[0], copylen);
327 if (result < 0)
328 {
329 /* read error */
330 errcode = gate_platform_get_last_error();
331 if (errcode == EIO)
332 {
333 gate_bool_t terminated = false;
334 gate_result_t wait_result = is_process_termined(stream->target_pid, &terminated);
335 if (GATE_SUCCEEDED(wait_result) && terminated)
336 {
337 /* signal end-of-stream, as process was terminated */
338 if (returned)
339 {
340 *returned = 0;
341 }
342 return GATE_RESULT_ENDOFSTREAM;
343 }
344 }
345 /* otherwise return native error info */
346 return gate_platform_print_last_error(NULL, 0);
347 }
348 else if (result > 0)
349 {
350 /* peek-buffer is filled, return a copy via caller-supplied buffer */
351 stream->peek_buffer_used = copylen;
352 gate_mem_copy(buffer, &stream->peek_buffer[0], copylen);
353 if (returned) *returned = copylen;
354 return GATE_RESULT_OK;
355 }
356 }
357
358 /* else: nothing available to read*/
359 if (returned) *returned = 0;
360 return GATE_RESULT_OK;
361 }
362 static gate_result_t gate_process_stream_write(void* thisptr, char const* buffer, gate_size_t bufferlength, gate_size_t* written)
363 {
364 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
365 gate_result_t ret = GATE_RESULT_FAILED;
366 gate_int32_t errcode;
367 ptrdiff_t result = gate_posix_write(stream->fd_stdin_write, buffer, bufferlength);
368 if (result <= 0)
369 {
370 errcode = gate_platform_get_last_error();
371 GATE_DEBUG_TRACE_MSG_VALUE("gate_process_stream_write() failed", errcode);
372 ret = gate_platform_print_error(errcode, NULL, 0);
373 }
374 else
375 {
376 if (written) *written = (gate_size_t)result;
377 ret = GATE_RESULT_OK;
378 }
379 return ret;
380 }
381 static gate_result_t gate_process_stream_flush(void* thisptr)
382 {
383 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
384 fsync(stream->fd_stdin_write);
385 return GATE_RESULT_OK;
386 }
387 static gate_result_t gate_process_stream_read_err(void* thisptr, char* buffer, gate_size_t bufferlength, gate_size_t* returned)
388 {
389 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
390 gate_result_t ret = GATE_RESULT_FAILED;
391 gate_int32_t errcode;
392 ptrdiff_t result = gate_posix_read(stream->fd_stderr_read, buffer, bufferlength);
393 if (result < 0)
394 {
395 errcode = gate_platform_get_last_error();
396 GATE_DEBUG_TRACE_MSG_VALUE("gate_process_stream_read() failed", errcode);
397 ret = gate_platform_print_error(errcode, NULL, 0);
398 }
399 else
400 {
401 if (returned) *returned = (gate_size_t)result;
402 ret = GATE_RESULT_OK;
403 }
404 return ret;
405 }
406 static gate_result_t gate_process_stream_get_resource(void* thisptr, gate_enumint_t resource_type, gate_uintptr_t* resource)
407 {
408 gate_process_stream_impl_t* stream = (gate_process_stream_impl_t*)thisptr;
409 gate_result_t ret;
410 switch (resource_type)
411 {
412 case GATE_STREAM_RESOURCE_INPUT:
413 {
414 *resource = (gate_uintptr_t)stream->fd_stdin_write;
415 ret = GATE_RESULT_OK;
416 break;
417 }
418 case GATE_STREAM_RESOURCE_OUTPUT:
419 {
420 *resource = (gate_uintptr_t)stream->fd_stdout_read;
421 ret = GATE_RESULT_OK;
422 break;
423 }
424 case GATE_STREAM_RESOURCE_ERROR:
425 {
426 *resource = (gate_uintptr_t)stream->fd_stderr_read;
427 ret = GATE_RESULT_OK;
428 break;
429 }
430 default:
431 {
432 ret = GATE_RESULT_NOTSUPPORTED;
433 break;
434 }
435 }
436 return ret;
437 }
438
439 static GATE_INTERFACE_VTBL(gate_process_stream) gate_process_stream_vtbl;
440 1 static void gate_init_process_stream_vtbl()
441 {
442
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!gate_process_stream_vtbl.get_interface_name)
443 {
444 GATE_INTERFACE_VTBL(gate_process_stream) const local_vtbl =
445 {
446 &gate_process_stream_interface_name,
447 &gate_process_stream_release,
448 &gate_process_stream_retain,
449 &gate_process_stream_read,
450 &gate_process_stream_peek,
451 &gate_process_stream_write,
452 &gate_process_stream_flush,
453 &gate_process_stream_get_resource,
454 &gate_process_stream_read_err
455 };
456 1 gate_process_stream_vtbl = local_vtbl;
457 }
458 1 }
459
460 1 static gate_process_stream_t* gate_process_stream_create(int fd_stdin_write, int fd_stdout_read, int fd_stderr_read)
461 {
462 1 gate_process_stream_impl_t* stream = gate_mem_alloc(sizeof(gate_process_stream_impl_t));
463
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (stream)
464 {
465 1 gate_init_process_stream_vtbl();
466 1 stream->vbl = &gate_process_stream_vtbl;
467 1 gate_atomic_int_set(&stream->ref_counter, 1);
468 1 stream->target_pid = 0;
469 1 stream->fd_stdin_write = fd_stdin_write;
470 1 stream->fd_stdout_read = fd_stdout_read;
471 1 stream->fd_stderr_read = fd_stderr_read;
472 }
473 1 return (gate_process_stream_t*)stream;
474 }
475
476
477 /*
478 typedef struct rlimit_setting
479 {
480 int setting;
481 struct rlimit limit;
482 } rlimit_setting_t;
483
484 typedef struct gate_process_exec_config
485 {
486 gate_bool_t set_uid;
487 uid_t uid;
488 gate_bool_t set_gid;
489 gid_t gid;
490
491 gate_bool_t new_session;
492 gate_bool_t new_terminal;
493
494 rlimit_setting_t limits[8];
495 gate_size_t limit_count;
496
497 } gate_process_exec_config_t;
498
499 gate_result_t gate_process_apply_exec_config(gate_process_exec_config_t const* config)
500 {
501 gate_result_t ret = GATE_RESULT_OK;
502 gate_size_t ndx;
503 int result;
504 char user_name[512];
505 gate_size_t user_name_used;
506
507 do
508 {
509 if(config == NULL) break;
510
511 for(ndx = 0; ndx != config->limit_count; ++ndx)
512 {
513 if(-1 == setrlimit(config->limits[ndx].setting, &config->limits[ndx].limit))
514 {
515 ret = GATE_RESULT_PREPARATIONFAILED;
516 break;
517 }
518 }
519 GATE_BREAK_IF_FAILED(ret);
520
521 if(config->set_uid && config->set_gid)
522 {
523 user_name_used = gate_posix_get_user_name(config->set_uid, user_name, sizeof(user_name));
524 if(user_name_used != 0)
525 {
526 result = initgroups(user_name, config->gid);
527 if(result != 0)
528 {
529 GATE_DEBUG_TRACE("Failed to initialize groups");
530 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
531 break;
532 }
533 }
534 }
535
536 if(config->set_gid)
537 {
538 result = setgid(config->gid);
539 if(result != 0)
540 {
541 GATE_DEBUG_TRACE("setgid() failed");
542 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
543 ret = GATE_RESULT_FAILED;
544 break;
545 }
546 }
547
548 if(config->set_uid)
549 {
550 result = setuid(config->uid);
551 if(result != 0)
552 {
553 GATE_DEBUG_TRACE("setuid() failed");
554 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
555 ret = GATE_RESULT_FAILED;
556 break;
557 }
558 }
559
560 if(config->new_session)
561 {
562 result = setsid();
563 if(result != 0)
564 {
565 GATE_DEBUG_TRACE("setsid() failed");
566 GATE_DEBUG_TRACE_VALUE(gate_posix_errno(NULL));
567 ret = GATE_RESULT_FAILED;
568 break;
569 }
570 }
571
572 } while(0);
573
574 return ret;
575 }
576 */
577
578 8 gate_result_t gate_process_get_id(gate_process_id_t* pid)
579 {
580 gate_result_t ret;
581 8 pid_t p = getpid();
582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (pid == NULL)
583 {
584 ret = GATE_RESULT_INVALIDARG;
585 }
586 else
587 {
588 8 *pid = (gate_process_id_t)p;
589 8 ret = GATE_RESULT_OK;
590 }
591 8 return ret;
592 }
593 void gate_process_quit(int exitcode)
594 {
595 _exit(exitcode);
596 }
597
598 #if defined(GATE_SYS_BSD) || defined(GATE_SYS_DARWIN)
599
600 #include <stdlib.h>
601 #include <sys/types.h>
602 #include <sys/sysctl.h>
603
604 #if defined(GATE_SYS_NETBSD)
605 /* NetBSD is special here and uses kinfo_proc2 for usermode, BSD's kinfo_proc is for kernel-mode only */
606
607 # include <kvm.h>
608
609 typedef kvm_t* (*kvm_open_func_t)(char const* execfile, char const* corefile, char* swapfile, int flags, char* errstr);
610 typedef int (*kvm_close_func_t)(kvm_t* kd);
611 typedef struct kinfo_proc2* (*kvm_getproc2_func_t)(kvm_t* kd, int op, int arg, size_t elemsize, int* cnt);
612
613 static kvm_open_func_t kvm_open_func;
614 static kvm_close_func_t kvm_close_func;
615 static kvm_getproc2_func_t kvm_getproc2_func;
616
617 static void* volatile kvm_lib = NULL;
618
619 static void close_kvm_lib_atexit()
620 {
621 if (NULL != kvm_lib)
622 {
623 void* handle = kvm_lib;
624 kvm_lib = NULL;
625 gate_posix_dlclose(handle);
626 }
627 }
628
629 static gate_result_t load_kvm_library()
630 {
631 if (!kvm_lib)
632 {
633 kvm_lib = gate_posix_dlopen("libkvm.so", RTLD_GLOBAL);
634 if (kvm_lib == NULL)
635 {
636 return GATE_RESULT_NOTAVAILABLE;
637 }
638 gate_platform_atexit(&close_kvm_lib_atexit);
639 }
640 if (!kvm_open_func)
641 {
642 kvm_open_func = (kvm_open_func_t)gate_posix_dlsym(kvm_lib, "kvm_open");
643 }
644 if (!kvm_close_func)
645 {
646 kvm_close_func = (kvm_close_func_t)gate_posix_dlsym(kvm_lib, "kvm_close");
647 }
648 if (!kvm_getproc2_func)
649 {
650 kvm_getproc2_func = (kvm_getproc2_func_t)gate_posix_dlsym(kvm_lib, "kvm_getproc2");
651 }
652 return (kvm_open_func && kvm_close_func && kvm_getproc2_func) ? GATE_RESULT_OK : GATE_RESULT_NOTAVAILABLE;
653 }
654
655 static gate_result_t load_process_infobuffer(gate_process_infobuffer_t* target, struct kinfo_proc2 const* src)
656 {
657 target->process.pid = src->p_pid;
658 target->process.parent_pid = src->p_ppid;
659 target->process.memory_used = src->p_vm_rssize;
660 target->process.uptime = src->p_ustart_sec * 1000;
661 target->process.cputime = src->p_uutime_sec * 1000;
662 target->process.resources = 1;
663 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->p_comm, gate_str_length(src->p_comm));
664 target->process.name = target->namebuffer;
665 gate_str_print_int64(target->ownerbuffer, sizeof(target->ownerbuffer), src->p_ruid);
666 target->process.owner = target->ownerbuffer;
667 return GATE_RESULT_OK;
668 }
669
670
671 gate_result_t gate_process_getinfo(gate_process_infobuffer_t* infobuffer, gate_process_id_t proc_id, gate_enumint_t flags)
672 {
673 gate_result_t ret = GATE_RESULT_FAILED;
674 kvm_t* kvm = NULL;
675 struct kinfo_proc2* procs = NULL;
676 int procs_count = 0;
677 char errmsg[_POSIX2_LINE_MAX] = GATE_INIT_EMPTY;
678
679 do
680 {
681 ret = load_kvm_library();
682 GATE_BREAK_IF_FAILED(ret);
683
684 kvm = kvm_open_func(NULL, NULL, NULL, O_RDONLY, errmsg);
685 if (kvm == NULL)
686 {
687 GATE_DEBUG_TRACE("kvm_open() failed");
688 errmsg[_POSIX2_LINE_MAX - 1] = 0;
689 GATE_DEBUG_TRACE(errmsg);
690 ret = GATE_RESULT_NOTAVAILABLE;
691 break;
692 }
693 procs = kvm_getproc2_func(kvm, KERN_PROC_PID, (int)proc_id, sizeof(struct kinfo_proc2), &procs_count);
694 if ((procs == NULL) || (procs_count == 0))
695 {
696 GATE_DEBUG_TRACE("kvm_getprocs(KERN_PROC_PID) failed");
697 ret = GATE_RESULT_NOMATCH;
698 break;
699 }
700
701 ret = load_process_infobuffer(infobuffer, procs);
702 } while (0);
703
704 if (kvm != NULL)
705 {
706 kvm_close_func(kvm);
707 }
708
709 return ret;
710 }
711
712 #else /* OpenBSD and FreeBSD: */
713
714 #include <sys/user.h>
715
716 static gate_result_t load_process_infobuffer(gate_process_infobuffer_t* target, struct kinfo_proc const* src)
717 {
718 gate_result_t ret = GATE_RESULT_OK;
719 gate_mem_clear(target, sizeof(gate_process_infobuffer_t));
720 #if defined(GATE_SYS_OPENBSD)
721 target->process.pid = src->p_pid;
722 target->process.parent_pid = src->p_ppid;
723 target->process.memory_used = src->p_vm_rssize;
724 target->process.uptime = src->p_uutime_sec * 1000 + src->p_ustime_sec * 1000 + src->p_rtime_sec * 1000;
725 target->process.resources = src->p_tid;
726 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->p_comm, gate_str_length(src->p_comm));
727 target->process.name = target->namebuffer;
728 gate_str_print_int64(target->ownerbuffer, sizeof(target->ownerbuffer), src->p_ruid);
729 target->process.owner = target->ownerbuffer;
730 #elif defined(GATE_SYS_DARWIN)
731 target->process.pid = src->kp_proc.p_pid;
732 target->process.parent_pid = src->kp_eproc.e_ppid;
733 target->process.memory_used = 1; /* TODO */
734 target->process.uptime = src->kp_proc.p_swtime;
735 target->process.cputime = src->kp_proc.p_rtime.tv_sec * 1000 + src->kp_proc.p_rtime.tv_usec / 1000;
736 target->process.resources = 1; /* TODO */
737 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->kp_proc.p_comm, gate_str_length(src->kp_proc.p_comm));
738 target->process.name = target->namebuffer;
739 gate_str_print_text(target->ownerbuffer, sizeof(target->ownerbuffer), src->kp_eproc.e_login, gate_str_length(src->kp_eproc.e_login));
740 target->process.owner = target->ownerbuffer;
741 #else
742 target->process.pid = src->ki_pid;
743 target->process.parent_pid = src->ki_ppid;
744 target->process.memory_used = src->ki_size;
745 target->process.uptime = src->ki_start.tv_sec * 1000 + src->ki_start.tv_usec / 1000;
746 target->process.cputime = src->ki_runtime;
747 target->process.resources = src->ki_numthreads;
748 gate_str_print_text(target->namebuffer, sizeof(target->namebuffer), src->ki_comm, gate_str_length(src->ki_comm));
749 target->process.name = target->namebuffer;
750 gate_str_print_text(target->ownerbuffer, sizeof(target->ownerbuffer), src->ki_login, gate_str_length(src->ki_login));
751 target->process.owner = target->ownerbuffer;
752 #endif
753
754 return ret;
755 }
756
757 gate_result_t gate_process_getinfo(gate_process_infobuffer_t* infobuffer, gate_process_id_t proc_id, gate_enumint_t flags)
758 {
759 gate_result_t ret = GATE_RESULT_FAILED;
760 int names[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)proc_id, sizeof(struct kinfo_proc), 1 };
761 #if defined(GATE_SYS_OPENBSD)
762 unsigned name_len = 6;
763 #else
764 unsigned name_len = 4;
765 #endif
766 char buffer[8192 + 4096];
767 size_t buffer_len = sizeof(buffer);
768 int result;
769
770 do
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_size_t ndx;
840 gate_result_t result = gate_darwin_list_pids(&pids[0], sizeof(pids) / sizeof(pids[0]), &pids_used);
841 GATE_RETURN_IF_FAILED(result);
842 if (callback)
843 {
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);
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 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), 0 };
931 unsigned mib_len = sizeof(mib) / sizeof(mib[0]);
932 gate_process_infobuffer_t info_buffer;
933 char buffer[8192 + 4096];
934 char* ptr_buffer = &buffer[0];
935 size_t bufferlen = 0;
936 struct kinfo_proc* procs;
937 size_t procs_count;
938
939 do
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_result_t result = load_process_infobuffer(&info_buffer, procs);
974 if (GATE_SUCCEEDED(result) && (callback != NULL))
975 {
976 if (!callback(&info_buffer.process, userparam))
977 {
978 break;
979 }
980 }
981 ++procs;
982 }
983 ret = GATE_RESULT_OK;
984 } while (0);
985
986 if ((ptr_buffer != NULL) && (ptr_buffer != &buffer[0]))
987 {
988 /* free allocated memory */
989 gate_mem_dealloc(ptr_buffer);
990 }
991 return ret;
992 }
993
994 #elif defined(GATE_SYS_NETBSD)
995
996 static gate_result_t netbsd_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
997 {
998 gate_result_t ret = GATE_RESULT_FAILED;
999 kvm_t* kvm = NULL;
1000 struct kinfo_proc2* procs = NULL;
1001 int procs_count = 0;
1002 int ndx;
1003 gate_process_infobuffer_t infobuffer;
1004 char errbuf[_POSIX2_LINE_MAX];
1005
1006 do
1007 {
1008 ret = load_kvm_library();
1009 GATE_BREAK_IF_FAILED(ret);
1010
1011 kvm = kvm_open_func(NULL, NULL, NULL, O_RDONLY, errbuf);
1012 if (kvm == NULL)
1013 {
1014 GATE_DEBUG_TRACE("kvm_open() failed");
1015 errbuf[_POSIX2_LINE_MAX - 1] = 0;
1016 GATE_DEBUG_TRACE(errbuf);
1017 ret = GATE_RESULT_NOTAVAILABLE;
1018 break;
1019 }
1020
1021 procs = kvm_getproc2_func(kvm, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &procs_count);
1022 if ((procs == NULL) || (procs_count == 0))
1023 {
1024 /* finding no processes is an error, we should at least find our own process */
1025 GATE_DEBUG_TRACE("kvm_getprocs(KERN_PROC_ALL) failed");
1026 ret = GATE_RESULT_FAILED;
1027 }
1028
1029 /* success case: iterator over infos */
1030 ret = GATE_RESULT_OK;
1031 for (ndx = 0; ndx != procs_count; ++ndx, ++procs)
1032 {
1033 gate_result_t result = load_process_infobuffer(&infobuffer, procs);
1034 if (GATE_FAILED(result))
1035 {
1036 continue;
1037 }
1038 if (callback)
1039 {
1040 if (!callback(&infobuffer.process, userparam))
1041 {
1042 break;
1043 }
1044 }
1045 }
1046 } while (0);
1047
1048 if (kvm != NULL)
1049 {
1050 kvm_close_func(kvm);
1051 }
1052 return ret;
1053 }
1054
1055 #endif
1056
1057 #else /* Linux PROCFS */
1058
1059 8 gate_result_t gate_process_getinfo(gate_process_infobuffer_t* infobuffer, gate_process_id_t proc_id, gate_enumint_t flags)
1060 {
1061 8 gate_result_t ret = GATE_RESULT_NOTAVAILABLE;
1062 gate_size_t namepos;
1063 gate_procfs_proc_stat_t proc_stat;
1064 gate_procfs_memstat_t mem_stat;
1065 gate_uint64_t page_size;
1066 gate_uint64_t ticks_per_second;
1067 gate_uint64_t starttime_after_boot_ms;
1068 gate_int64_t system_up_time_ms;
1069 gate_posix_user_info_t user_info;
1070 gate_result_t result;
1071 int proc_uid;
1072
1073 8 gate_mem_clear(infobuffer, sizeof(gate_process_infobuffer_t));
1074
1075 8 infobuffer->process.pid = proc_id;
1076
1077
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)))
1078 {
1079 8 infobuffer->process.path = infobuffer->pathbuffer;
1080 8 ret = GATE_RESULT_OK;
1081 }
1082
1083
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_load_proc_stat(proc_id, &proc_stat))
1084 {
1085 8 infobuffer->process.parent_pid = proc_stat.ppid;
1086
1087
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_TIMES))
1088 {
1089 8 ticks_per_second = (gate_uint64_t)sysconf(_SC_CLK_TCK);
1090 8 starttime_after_boot_ms = (gate_int64_t)proc_stat.starttime * 1000ULL / ticks_per_second;
1091
1092 8 system_up_time_ms = gate_procfs_get_uptime_ms();
1093
1094 8 infobuffer->process.uptime = system_up_time_ms - starttime_after_boot_ms;
1095
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.uptime < 0) infobuffer->process.uptime = 0;
1096 8 infobuffer->process.cputime = (gate_uint64_t)(proc_stat.utime + proc_stat.stime) * 1000ULL / ticks_per_second;
1097
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.cputime < 0) infobuffer->process.cputime = 0;
1098 }
1099 8 ret = GATE_RESULT_OK;
1100 }
1101
1102
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_NAME))
1103 {
1104
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (infobuffer->process.path != NULL)
1105 {
1106 8 namepos = gate_str_char_pos_last(infobuffer->process.path, gate_str_length(infobuffer->process.path), '/');
1107
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (namepos != GATE_STR_NPOS)
1108 {
1109 8 infobuffer->process.name = &infobuffer->process.path[namepos + 1];
1110 }
1111 }
1112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.name == NULL)
1113 {
1114 gate_str_print_uint64(infobuffer->namebuffer, sizeof(infobuffer->namebuffer), proc_id);
1115 infobuffer->process.name = infobuffer->namebuffer;
1116 }
1117 }
1118
1119
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_OWNER))
1120 {
1121
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_get_exe_owner(proc_id, &proc_uid, NULL))
1122 {
1123 8 result = gate_posix_get_user_info(proc_uid, &user_info);
1124
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_SUCCEEDED(result))
1125 {
1126 8 gate_str_print_text(infobuffer->ownerbuffer, sizeof(infobuffer->ownerbuffer),
1127 user_info.name, gate_str_length(user_info.name));
1128 8 infobuffer->process.owner = infobuffer->ownerbuffer;
1129 }
1130 }
1131 }
1132
1133
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_MEMORY))
1134 {
1135
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (gate_procfs_get_memstat(proc_id, &mem_stat))
1136 {
1137 8 page_size = sysconf(_SC_PAGESIZE);
1138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (page_size == 0)
1139 {
1140 page_size = 4096;
1141 }
1142 8 infobuffer->process.memory_used = (gate_uint64_t)mem_stat.resident * page_size;
1143 }
1144 }
1145
1146
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_ENUM_RESOURCES))
1147 {
1148 8 infobuffer->process.resources = gate_procfs_get_open_fds(proc_id);
1149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (infobuffer->process.resources < 0) infobuffer->process.resources = 0;
1150 }
1151
1152 8 return ret;
1153 }
1154
1155 #endif
1156
1157 1 gate_result_t gate_process_enum(gate_process_enum_callback_t callback, void* userparam, gate_enumint_t flags)
1158 {
1159 1 gate_result_t ret = GATE_RESULT_FAILED;
1160 DIR* dir;
1161
1162 #if defined(GATE_SYS_DARWIN)
1163 ret = darwin_process_enum(callback, userparam, flags);
1164 #elif defined(GATE_SYS_BSD)
1165
1166 #if defined(GATE_SYS_FREEBSD)
1167 ret = freebsd_process_enum(callback, userparam, flags);
1168 #elif defined(GATE_SYS_OPENBSD)
1169 ret = openbsd_process_enum(callback, userparam, flags);
1170 #elif defined(GATE_SYS_NETBSD)
1171 ret = netbsd_process_enum(callback, userparam, flags);
1172 #endif
1173
1174 if (GATE_SUCCEEDED(ret))
1175 {
1176 /* we have a result, we are done */
1177 return GATE_RESULT_OK;
1178 }
1179 /* otherwise, continue with linux/procfs approach */
1180
1181 #endif /* GATE_SYS_BSD */
1182
1183 /* linux procfs: */
1184 1 dir = opendir("/proc");
1185
1186
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (dir != NULL)
1187 {
1188 struct dirent* entry;
1189
2/2
✓ Branch 1 taken 65 times.
✓ Branch 2 taken 1 times.
66 while (NULL != (entry = readdir(dir)))
1190 {
1191 gate_process_infobuffer_t proc_data;
1192 65 gate_uint64_t procid = 0;
1193 65 gate_size_t entrylen = gate_str_length(entry->d_name);
1194 65 gate_size_t parselen = gate_str_parse_uint64(entry->d_name, entrylen, &procid);
1195
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 58 times.
65 if (parselen == entrylen)
1196 {
1197 /* seems to be a numeric process id */
1198 7 ret = gate_process_getinfo(&proc_data, (gate_process_id_t)procid, flags);
1199
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (GATE_SUCCEEDED(ret))
1200 {
1201
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (false == callback(&proc_data.process, userparam))
1202 {
1203 break;
1204 }
1205 }
1206 }
1207 }
1208 1 closedir(dir);
1209 1 ret = GATE_RESULT_OK;
1210 }
1211 1 return ret;
1212 }
1213
1214
1215 2 gate_result_t gate_process_start(gate_string_t const* executablepath,
1216 gate_string_t const* args, gate_size_t argcount,
1217 gate_string_t const* workdir, gate_string_t const* envvars, gate_size_t envvarcount, gate_enumint_t flags,
1218 gate_process_handle_t* result_handle,
1219 gate_process_id_t* result_pid,
1220 gate_process_stream_t** result_stream
1221 )
1222 {
1223 2 return gate_process_start_ex(executablepath, args, argcount,
1224 workdir, envvars, envvarcount, flags,
1225 NULL, NULL, NULL,
1226 result_handle, result_pid, result_stream);
1227 }
1228
1229
1230 4 static char* gate_process_build_argarray(gate_string_t const* args, gate_size_t argcount, char** buffer, gate_size_t maxargs)
1231 {
1232 char* ret;
1233 char* ptr;
1234 gate_size_t ndx;
1235 4 gate_size_t byte_size = 0;
1236 gate_size_t len;
1237 4 gate_size_t buffer_ndx = 0;
1238
1239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (argcount >= maxargs) argcount = maxargs - 1;
1240
1241
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 for (ndx = 0; ndx < argcount; ++ndx)
1242 {
1243 2 byte_size += args[ndx].length + 1;
1244 }
1245 4 byte_size += 2;
1246
1247 4 ret = gate_mem_alloc(byte_size);
1248
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (ret != NULL)
1249 {
1250 4 ptr = ret;
1251
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 for (ndx = 0; ndx < argcount; ++ndx)
1252 {
1253 2 len = gate_str_print_text(ptr, args[ndx].length + 1, args[ndx].str, args[ndx].length);
1254 2 buffer[buffer_ndx++] = ptr;
1255 2 ptr += (len + 1);
1256 }
1257 4 ptr[0] = 0;
1258 4 buffer[buffer_ndx] = NULL;
1259 }
1260 4 return ret;
1261 }
1262
1263 2 static int gate_get_open_max(void)
1264 {
1265 #ifdef OPEN_MAX
1266 return OPEN_MAX;
1267 #else
1268 static volatile int cached_max = 0;
1269 long cnf_open_max;
1270
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (cached_max == 0)
1271 {
1272 2 cnf_open_max = sysconf(_SC_OPEN_MAX);
1273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (cnf_open_max <= 0)
1274 {
1275 cached_max = 1024;
1276 }
1277 else
1278 {
1279 2 cached_max = (int)cnf_open_max;
1280 }
1281 }
1282 2 return cached_max;
1283 #endif
1284 }
1285
1286
1287 2 static void close_private_file_descriptor(gate_bool_t exclude_inheritable_fds, int preserved_fd)
1288 {
1289 int inheritable_fds[1024];
1290 2 gate_size_t inheritable_count = 0;
1291 2 int fd_max = gate_get_open_max();
1292 int fd;
1293 size_t index;
1294 2 gate_bool_t exclude_fd = false;
1295
1296 2 inheritable_fds[0] = preserved_fd;
1297 2 inheritable_count = 1;
1298
1299
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (exclude_inheritable_fds)
1300 {
1301 2 inheritable_count = gate_posix_get_inheritable_fds(
1302 &inheritable_fds[1], sizeof(inheritable_fds) / sizeof(inheritable_fds[0]) - 1);
1303 2 ++inheritable_count;
1304 }
1305
1306
1307
2/2
✓ Branch 0 taken 2097146 times.
✓ Branch 1 taken 2 times.
2097148 for (fd = STDERR_FILENO + 1; fd < fd_max; ++fd)
1308 {
1309 2097146 exclude_fd = false;
1310
2/2
✓ Branch 0 taken 2097146 times.
✓ Branch 1 taken 2097144 times.
4194290 for (index = 0; index != inheritable_count; ++index)
1311 {
1312
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2097144 times.
2097146 if (fd == inheritable_fds[index])
1313 {
1314 2 exclude_fd = true;
1315 2 break;
1316 }
1317 }
1318
2/2
✓ Branch 0 taken 2097144 times.
✓ Branch 1 taken 2 times.
2097146 if (!exclude_fd)
1319 {
1320 2097144 gate_posix_close(fd);
1321 }
1322 }
1323 2 }
1324
1325 static gate_result_t gate_process_switch_user(char const* username)
1326 {
1327 gate_result_t ret;
1328 gate_posix_user_info_t user_info;
1329 int result;
1330
1331 do
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 static gate_bool_t are_pty_functions_loaded()
1370 {
1371 return (openpty_func != NULL) && (login_tty_func != NULL);
1372 }
1373
1374 static gate_bool_t try_load_pty_functions(void* libhandle)
1375 {
1376 openpty_func = (openpty_func_t)gate_posix_dlsym(libhandle, "openpty");
1377 login_tty_func = (login_tty_func_t)gate_posix_dlsym(libhandle, "login_tty");
1378 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 static void close_pty_lib_atexit()
1391 {
1392 if (NULL != pty_lib_handle)
1393 {
1394 void* handle = pty_lib_handle;
1395 pty_lib_handle = NULL;
1396 gate_posix_dlclose(handle);
1397 }
1398 }
1399
1400 static gate_bool_t load_pty_functions()
1401 {
1402 if (are_pty_functions_loaded())
1403 {
1404 return true;
1405 }
1406
1407 openpty_func = (openpty_func_t)gate_posix_global_sym("openpty");
1408 login_tty_func = (login_tty_func_t)gate_posix_global_sym("login_tty");
1409 if (are_pty_functions_loaded())
1410 {
1411 return true;
1412 }
1413
1414 unsigned ndx;
1415 for (ndx = 0; ndx != util_libs_count; ++ndx)
1416 {
1417 void* lib_handle = gate_posix_dlopen(util_libs[ndx], RTLD_GLOBAL);
1418 if (lib_handle)
1419 {
1420 if (try_load_pty_functions(lib_handle))
1421 {
1422 pty_lib_handle = lib_handle;
1423 gate_platform_atexit(&close_pty_lib_atexit);
1424 return true;
1425 }
1426 else
1427 {
1428 gate_posix_dlclose(lib_handle);
1429 }
1430 }
1431 }
1432 return false;
1433 }
1434
1435 2 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 2 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 2 char* process_args_buffer = NULL;
1450 2 char* process_vars_buffer = NULL;
1451
1452 do
1453 {
1454
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (!gate_string_is_empty(workdir))
1455 {
1456 2 GATE_STRING_TO_BUFFER(workdir, new_work_dir);
1457
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 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 2 times.
2 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 2 times.
2 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 2 process_args_buffer = gate_process_build_argarray(args, argcount, &process_args[1], sizeof(process_args) / sizeof(process_args[0]) - 1);
1482 2 process_vars_buffer = gate_process_build_argarray(envvars, envvarcount, process_vars, sizeof(process_vars) / sizeof(process_vars[0]));
1483
1484 2 GATE_STRING_TO_BUFFER(executablepath, exe_path);
1485 2 process_args[0] = exe_path;
1486
1487
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ptr_tty_fd)
1488 {
1489 /* login to terminal and set STD* to terminal fd */
1490 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
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (new_std_in)
1501 {
1502 1 dup2(*new_std_in, STDIN_FILENO);
1503 1 close(*new_std_in);
1504 }
1505
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (new_std_out)
1506 {
1507 1 dup2(*new_std_out, STDOUT_FILENO);
1508 1 close(*new_std_out);
1509 }
1510
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (new_std_err)
1511 {
1512 1 dup2(*new_std_err, STDERR_FILENO);
1513 1 close(*new_std_err);
1514 }
1515 }
1516
1517 2 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 2 times.
2 if (envvars)
1520 {
1521 execve(exe_path, process_args, process_vars);
1522 }
1523 else
1524 {
1525 2 execv(exe_path, process_args);
1526 }
1527
1528 2 ret = GATE_RESULT_UNKNOWNERROR;
1529
1530 } while (0);
1531
1532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (process_args_buffer)
1533 {
1534 gate_mem_dealloc(process_args_buffer);
1535 }
1536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 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 22 static void close_valid_fd(int fd)
1547 {
1548
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if (fd != INVALID_FD)
1549 {
1550 3 close(fd);
1551 }
1552 22 }
1553
1554 2 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 2 gate_result_t ret = GATE_RESULT_FAILED;
1565 2 int failpipes[2] = { INVALID_FD, INVALID_FD };
1566 2 int std_in_pipe[2] = { INVALID_FD, INVALID_FD }; /* stdin of child process */
1567 2 int std_out_pipe[2] = { INVALID_FD, INVALID_FD }; /* stdout of child process */
1568 2 int std_err_pipe[2] = { INVALID_FD, INVALID_FD }; /* stderr of child process */
1569 2 pid_t process_id = -1;
1570 2 int term_master = INVALID_FD;
1571 2 int term_slave = INVALID_FD;
1572 char term_slave_name[1024];
1573 2 char const* ptr_term_slave_name = NULL;
1574 2 gate_process_stream_t* proc_stream = NULL;
1575
1576 2 int status = 0;
1577 ssize_t readlen;
1578 ssize_t writelen;
1579 2 gate_result_t process_exec_error = 0;
1580
1581 do
1582 {
1583
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (result_stream)
1584 {
1585
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_START_NEWTERMINAL))
1586 {
1587 /* child process will run in a pseudo terminal, parent controls master-side */
1588 if (!load_pty_functions())
1589 {
1590 ret = GATE_RESULT_NOTSUPPORTED;
1591 break;
1592 }
1593 if (openpty_func(&term_master, &term_slave, term_slave_name, NULL, NULL) == -1)
1594 {
1595 ret = GATE_RESULT_OUTOFRESOURCES;
1596 break;
1597 }
1598 ptr_term_slave_name = &term_slave_name[0];
1599
1600 /* parent process read/write ends are set to terminal-master */
1601 std_in_pipe[1] = term_master;
1602 std_out_pipe[0] = dup(term_master);
1603 std_err_pipe[0] = dup(term_master);
1604 term_master = INVALID_FD;
1605 }
1606 else
1607 {
1608 /* child process i/o will be directed to pipes controlled by parent */
1609
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (pipe(std_in_pipe) != 0)
1610 {
1611 ret = GATE_RESULT_OUTOFRESOURCES;
1612 break;
1613 }
1614
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (pipe(std_out_pipe) != 0)
1615 {
1616 ret = GATE_RESULT_OUTOFRESOURCES;
1617 break;
1618 }
1619
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FLAG_ENABLED(flags, GATE_PROCESS_START_USESTDERR))
1620 {
1621 if (pipe(std_err_pipe) != 0)
1622 {
1623 ret = GATE_RESULT_OUTOFRESOURCES;
1624 break;
1625 }
1626 }
1627 else
1628 {
1629 1 std_err_pipe[0] = dup(std_out_pipe[0]);
1630 1 std_err_pipe[1] = dup(std_out_pipe[1]);
1631 }
1632 }
1633
1634 1 proc_stream = gate_process_stream_create(std_in_pipe[1], std_out_pipe[0], std_err_pipe[0]);
1635
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!proc_stream)
1636 {
1637 ret = GATE_RESULT_OUTOFMEMORY;
1638 break;
1639 }
1640 /* fds are now handled by stream object */
1641 1 std_in_pipe[1] = INVALID_FD;
1642 1 std_out_pipe[0] = INVALID_FD;
1643 1 std_err_pipe[0] = INVALID_FD;
1644 }
1645
1646
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (pipe(failpipes) != 0)
1647 {
1648 ret = GATE_RESULT_OUTOFRESOURCES;
1649 break;
1650 }
1651
1652 2 process_id = fork();
1653
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (process_id == -1)
1654 {
1655 /* fork has failed */
1656 ret = GATE_RESULT_EXECUTIONFAILED;
1657 break;
1658 }
1659
1660
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (process_id == 0)
1661 {
1662 /* child process */
1663 2 close(failpipes[0]); /* close read descriptor of fail-detector-pipe */
1664 2 failpipes[0] = INVALID_FD;
1665 2 fcntl(failpipes[1], F_SETFD, FD_CLOEXEC); /* auto-close on-exec on write descriptor of fail-detector-pipe */
1666
1667
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
4 process_exec_error = gate_process_exec(
1668 executablepath, args, argcount, workdir, envvars, envvarcount, flags,
1669 sessionlocation, username, password,
1670 failpipes[1], /* exclude failpipe from auto-close */
1671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 (term_slave != INVALID_FD) ? &term_slave : NULL, /* use terminal-slave fd if available */
1672 result_stream ? &std_in_pipe[0] : NULL, /* redirect std-in */
1673 result_stream ? &std_out_pipe[1] : NULL, /* redirect std-out */
1674 result_stream ? &std_err_pipe[1] : NULL /* redirect std-err */
1675 );
1676
1677 writelen = write(failpipes[1], &process_exec_error, sizeof(process_exec_error));
1678 _exit(GATE_RESULT_TO_EXITCODE(process_exec_error));
1679 ret = GATE_RESULT_UNKNOWNERROR;
1680 break;
1681 }
1682
1683 /* parent process */
1684 2 close_valid_fd(term_slave);
1685 2 term_slave = INVALID_FD;
1686
1687 2 close(failpipes[1]); /* close write descriptor of fail-detector-pipe*/
1688 2 failpipes[1] = INVALID_FD;
1689
1690 2 readlen = read(failpipes[0], &process_exec_error, sizeof(process_exec_error));
1691 2 close(failpipes[0]);
1692 2 failpipes[0] = INVALID_FD;
1693
1694
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (readlen > 0)
1695 {
1696 /* child process has failed to prepare exec */
1697 ret = process_exec_error;
1698 }
1699 else
1700 {
1701 /* when reading from 'failpipe' fails, then everything seems to be OK */
1702
1703
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (result_stream)
1704 {
1705 1 *result_stream = proc_stream;
1706
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (proc_stream)
1707 {
1708 1 ((gate_process_stream_impl_t*)proc_stream)->target_pid = process_id;
1709 1 gate_object_retain(proc_stream);
1710 }
1711 }
1712
1713
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (result_pid != NULL)
1714 {
1715 1 *result_pid = (gate_process_id_t)process_id;
1716 }
1717
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (result_handle != NULL)
1718 {
1719 2 *result_handle = (gate_process_handle_t)(gate_intptr_t)process_id;
1720 2 process_id = 0; /* protect process_id from being cleaned up */
1721 }
1722
1723 2 ret = GATE_RESULT_OK;
1724 }
1725 } while (0);
1726
1727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (process_id > 0)
1728 {
1729 /* detach process */
1730 waitpid(process_id, &status, WNOHANG);
1731 }
1732
1733 2 close_valid_fd(failpipes[1]);
1734 2 close_valid_fd(failpipes[0]);
1735 2 close_valid_fd(std_in_pipe[1]);
1736 2 close_valid_fd(std_in_pipe[0]);
1737 2 close_valid_fd(std_out_pipe[1]);
1738 2 close_valid_fd(std_out_pipe[0]);
1739 2 close_valid_fd(std_err_pipe[1]);
1740 2 close_valid_fd(std_err_pipe[0]);
1741 2 close_valid_fd(term_master);
1742 2 close_valid_fd(term_slave);
1743
1744
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (proc_stream)
1745 {
1746 1 gate_object_release(proc_stream);
1747 }
1748
1749 2 return ret;
1750 }
1751 2 gate_result_t gate_process_close(gate_process_handle_t* handle)
1752 {
1753 gate_result_t ret;
1754 pid_t pid;
1755 int status;
1756 pid_t result;
1757
1758
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1759 {
1760 ret = GATE_RESULT_OK;
1761 }
1762 else
1763 {
1764 2 pid = (pid_t)(gate_intptr_t)*handle;
1765 2 result = waitpid(pid, &status, WNOHANG);
1766
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (result == -1)
1767 {
1768 ret = GATE_RESULT_FAILED;
1769 }
1770 else
1771 {
1772 2 *handle = GATE_PROCESS_HANDLE_INVALID;
1773 2 ret = GATE_RESULT_OK;
1774 }
1775 }
1776
1777 2 return ret;
1778 }
1779 1 gate_result_t gate_process_get_exitcode(gate_process_handle_t* handle, int* exitcode)
1780 {
1781 1 gate_result_t ret = GATE_RESULT_FAILED;
1782 pid_t pid;
1783 1 siginfo_t si = GATE_INIT_EMPTY;
1784 int result;
1785 int status;
1786 gate_timecounter_t tcbegin, tcnow;
1787 gate_int64_t timediff;
1788 gate_int64_t timeoutus;
1789 gate_uint32_t sleepcounter;
1790
1791
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))
1792 {
1793 return GATE_RESULT_INVALIDSTATE;
1794 }
1795
1796 1 pid = (pid_t)(gate_intptr_t)*handle;
1797
1798 #if defined(GATE_SYS_OPENBSD)
1799 result = waitpid(pid, &status, WNOHANG);
1800 if (-1 == result)
1801 {
1802 return GATE_RESULT_FAILED;
1803 }
1804 if (!WIFEXITED(status))
1805 {
1806 return GATE_RESULT_INVALIDSTATE;
1807 }
1808 if (exitcode)
1809 {
1810 *exitcode = WEXITSTATUS(status);
1811 }
1812 *handle = GATE_PROCESS_HANDLE_INVALID;
1813 return GATE_RESULT_OK;
1814 #else
1815 1 result = waitid(P_PID, pid, &si, WEXITED | WNOWAIT | WNOHANG);
1816
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (result != 0)
1817 {
1818 return GATE_RESULT_NOTAVAILABLE;
1819 }
1820
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (si.si_pid != pid)
1821 {
1822 return GATE_RESULT_NOTAVAILABLE;
1823 }
1824
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (exitcode)
1825 {
1826 1 *exitcode = si.si_status;
1827 }
1828 1 return GATE_RESULT_OK;
1829 #endif
1830 }
1831
1832 1 gate_result_t gate_process_wait(gate_process_handle_t* handle, gate_uint32_t timeoutms)
1833 {
1834 1 gate_result_t ret = GATE_RESULT_FAILED;
1835 pid_t pid;
1836 siginfo_t si;
1837 int result;
1838 int status;
1839 gate_timecounter_t tcbegin, tcnow;
1840 gate_int64_t timediff;
1841 gate_int64_t timeoutus;
1842 gate_uint32_t sleepcounter;
1843
1844
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))
1845 {
1846 return GATE_RESULT_INVALIDSTATE;
1847 }
1848
1849 1 pid = (pid_t)(gate_intptr_t)*handle;
1850
1851
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (timeoutms == GATE_PROCESS_WAIT_INFINITE)
1852 {
1853 #if defined(GATE_SYS_OPENBSD)
1854 result = waitpid(pid, &status, 0);
1855 *handle = GATE_PROCESS_HANDLE_INVALID;
1856 #else
1857 1 result = waitid(P_PID, pid, &si, WEXITED | WNOWAIT);
1858 #endif
1859
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (result == 0)
1860 {
1861 1 ret = GATE_RESULT_OK;
1862 }
1863 else
1864 {
1865 gate_posix_errno(&ret);
1866 }
1867 }
1868 else
1869 {
1870 if (GATE_SUCCEEDED(gate_timecounter_now(&tcbegin)))
1871 {
1872 ret = GATE_RESULT_TIMEOUT;
1873 sleepcounter = 0;
1874 timeoutus = (gate_int64_t)timeoutms * 1000LL;
1875 do
1876 {
1877 #if defined(GATE_SYS_OPENBSD)
1878 result = waitpid(pid, &status, WNOHANG);
1879 *handle = GATE_PROCESS_HANDLE_INVALID;
1880 #else
1881 result = waitid(P_PID, pid, &si, WEXITED | WNOHANG | WNOWAIT);
1882 #endif
1883 if (result == 0)
1884 {
1885 if (si.si_pid == pid)
1886 {
1887 if ((si.si_code == CLD_KILLED) || (si.si_code == CLD_EXITED))
1888 {
1889 ret = GATE_RESULT_OK;
1890 break;
1891 }
1892 }
1893 }
1894
1895 {
1896 /* reduce long-time polling */
1897 gate_thread_sleep((sleepcounter));
1898 if (sleepcounter < 100)
1899 {
1900 ++sleepcounter;
1901 }
1902 }
1903
1904 if (GATE_FAILED(gate_timecounter_now(&tcnow)))
1905 {
1906 break;
1907 }
1908 timediff = gate_timecounter_diff(tcnow, tcbegin);
1909 } while (timediff < timeoutus);
1910 }
1911 }
1912 1 return ret;
1913 }
1914 gate_result_t gate_process_wait_pid(gate_process_id_t pid, gate_uint32_t timeoutms)
1915 {
1916 return GATE_RESULT_NOTIMPLEMENTED;
1917 }
1918 gate_result_t gate_process_terminate(gate_process_handle_t* handle, gate_bool_t system_request)
1919 {
1920 gate_process_id_t pid;
1921 if (!handle || (*handle == GATE_PROCESS_HANDLE_INVALID))
1922 {
1923 return GATE_RESULT_INVALIDSTATE;
1924 }
1925 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1926 return gate_process_terminate_pid(pid, system_request);
1927 }
1928 gate_result_t gate_process_terminate_pid(gate_process_id_t pid, gate_bool_t system_request)
1929 {
1930 gate_result_t ret = GATE_RESULT_FAILED;
1931 pid_t proc_id = (pid_t)pid;
1932 if (-1 == kill(proc_id, system_request ? SIGTERM : SIGINT))
1933 {
1934 gate_posix_errno(&ret);
1935 }
1936 else
1937 {
1938 ret = GATE_RESULT_OK;
1939 }
1940 return ret;
1941 }
1942 1 gate_result_t gate_process_kill(gate_process_handle_t* handle)
1943 {
1944 gate_process_id_t pid;
1945
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))
1946 {
1947 return GATE_RESULT_INVALIDSTATE;
1948 }
1949 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1950 1 return gate_process_kill_pid(pid);
1951 }
1952 1 gate_result_t gate_process_kill_pid(gate_process_id_t pid)
1953 {
1954 1 gate_result_t ret = GATE_RESULT_FAILED;
1955 1 pid_t proc_id = (pid_t)pid;
1956
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (-1 == kill(proc_id, SIGKILL))
1957 {
1958 gate_posix_errno(&ret);
1959 }
1960 else
1961 {
1962 1 ret = GATE_RESULT_OK;
1963 }
1964 1 return ret;
1965 }
1966 1 gate_result_t gate_process_suspend(gate_process_handle_t* handle)
1967 {
1968 gate_process_id_t pid;
1969
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))
1970 {
1971 return GATE_RESULT_INVALIDSTATE;
1972 }
1973 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1974 1 return gate_process_suspend_pid(pid);
1975 }
1976 1 gate_result_t gate_process_suspend_pid(gate_process_id_t pid)
1977 {
1978 1 gate_result_t ret = GATE_RESULT_FAILED;
1979 1 pid_t proc_id = (pid_t)pid;
1980
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (-1 == kill(proc_id, SIGSTOP))
1981 {
1982 gate_posix_errno(&ret);
1983 }
1984 else
1985 {
1986 1 ret = GATE_RESULT_OK;
1987 }
1988 1 return ret;
1989 }
1990 1 gate_result_t gate_process_resume(gate_process_handle_t* handle)
1991 {
1992 gate_process_id_t pid;
1993
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))
1994 {
1995 return GATE_RESULT_INVALIDSTATE;
1996 }
1997 1 pid = (gate_process_id_t)(gate_intptr_t)*handle;
1998 1 return gate_process_resume_pid(pid);
1999 }
2000 1 gate_result_t gate_process_resume_pid(gate_process_id_t pid)
2001 {
2002 1 gate_result_t ret = GATE_RESULT_FAILED;
2003 1 pid_t proc_id = (pid_t)pid;
2004
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (-1 == kill(proc_id, SIGCONT))
2005 {
2006 gate_posix_errno(&ret);
2007 }
2008 else
2009 {
2010 1 ret = GATE_RESULT_OK;
2011 }
2012 1 return ret;
2013 }
2014
2015 #endif /* GATE_SYS_POSIX */
2016