GCC Code Coverage Report


Directory: src/gate/
File: src/gate/processes_posix.c
Date: 2026-02-03 22:06:38
Exec Total Coverage
Lines: 448 597 75.0%
Functions: 35 42 83.3%
Branches: 189 336 56.2%

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