diff options
Diffstat (limited to 'src/lib/util/os_priority.c')
-rw-r--r-- | src/lib/util/os_priority.c | 1063 |
1 files changed, 1063 insertions, 0 deletions
diff --git a/src/lib/util/os_priority.c b/src/lib/util/os_priority.c new file mode 100644 index 000000000..63d18dbe4 --- /dev/null +++ b/src/lib/util/os_priority.c | |||
@@ -0,0 +1,1063 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2002, 2003, 2004, 2005, 2006, 2011 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/os_priority.c | ||
23 | * @brief Methods to set process priority | ||
24 | * @author Nils Durner | ||
25 | */ | ||
26 | |||
27 | |||
28 | #include "platform.h" | ||
29 | #include "gnunet_util_lib.h" | ||
30 | #include "disk.h" | ||
31 | #include <unistr.h> | ||
32 | |||
33 | #define LOG(kind, ...) GNUNET_log_from (kind, "util-os-priority", __VA_ARGS__) | ||
34 | |||
35 | #define LOG_STRERROR(kind, syscall) \ | ||
36 | GNUNET_log_from_strerror (kind, "util-os-priority", syscall) | ||
37 | |||
38 | #define LOG_STRERROR_FILE(kind, syscall, filename) \ | ||
39 | GNUNET_log_from_strerror_file (kind, "util-os-priority", syscall, filename) | ||
40 | |||
41 | #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE" | ||
42 | |||
43 | |||
44 | struct GNUNET_OS_Process | ||
45 | { | ||
46 | /** | ||
47 | * PID of the process. | ||
48 | */ | ||
49 | pid_t pid; | ||
50 | |||
51 | /** | ||
52 | * Pipe we use to signal the process. | ||
53 | * NULL if unused, or if process was deemed uncontrollable. | ||
54 | */ | ||
55 | struct GNUNET_DISK_FileHandle *control_pipe; | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Handle for 'this' process. | ||
61 | */ | ||
62 | static struct GNUNET_OS_Process current_process; | ||
63 | |||
64 | /** | ||
65 | * Handle for the #parent_control_handler() Task. | ||
66 | */ | ||
67 | static struct GNUNET_SCHEDULER_Task *pch; | ||
68 | |||
69 | /** | ||
70 | * Handle for the #shutdown_pch() Task. | ||
71 | */ | ||
72 | static struct GNUNET_SCHEDULER_Task *spch; | ||
73 | |||
74 | |||
75 | /** | ||
76 | * This handler is called on shutdown to remove the #pch. | ||
77 | * | ||
78 | * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe | ||
79 | */ | ||
80 | static void | ||
81 | shutdown_pch (void *cls) | ||
82 | { | ||
83 | struct GNUNET_DISK_FileHandle *control_pipe = cls; | ||
84 | |||
85 | GNUNET_SCHEDULER_cancel (pch); | ||
86 | pch = NULL; | ||
87 | GNUNET_DISK_file_close (control_pipe); | ||
88 | control_pipe = NULL; | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * This handler is called when there are control data to be read on the pipe | ||
94 | * | ||
95 | * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe | ||
96 | */ | ||
97 | static void | ||
98 | parent_control_handler (void *cls) | ||
99 | { | ||
100 | struct GNUNET_DISK_FileHandle *control_pipe = cls; | ||
101 | char sig; | ||
102 | char *pipe_fd; | ||
103 | ssize_t ret; | ||
104 | |||
105 | pch = NULL; | ||
106 | ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof(sig)); | ||
107 | if (sizeof(sig) != ret) | ||
108 | { | ||
109 | if (-1 == ret) | ||
110 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read"); | ||
111 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing control pipe\n"); | ||
112 | GNUNET_DISK_file_close (control_pipe); | ||
113 | control_pipe = NULL; | ||
114 | GNUNET_SCHEDULER_cancel (spch); | ||
115 | spch = NULL; | ||
116 | return; | ||
117 | } | ||
118 | pipe_fd = getenv (GNUNET_OS_CONTROL_PIPE); | ||
119 | GNUNET_assert ((NULL == pipe_fd) || (strlen (pipe_fd) <= 0)); | ||
120 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
121 | "Got control code %d from parent via pipe %s\n", | ||
122 | sig, | ||
123 | pipe_fd); | ||
124 | pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
125 | control_pipe, | ||
126 | &parent_control_handler, | ||
127 | control_pipe); | ||
128 | GNUNET_SIGNAL_raise ((int) sig); | ||
129 | } | ||
130 | |||
131 | |||
132 | void | ||
133 | GNUNET_OS_install_parent_control_handler (void *cls) | ||
134 | { | ||
135 | const char *env_buf; | ||
136 | char *env_buf_end; | ||
137 | struct GNUNET_DISK_FileHandle *control_pipe; | ||
138 | uint64_t pipe_fd; | ||
139 | |||
140 | (void) cls; | ||
141 | if (NULL != pch) | ||
142 | { | ||
143 | /* already done, we've been called twice... */ | ||
144 | GNUNET_break (0); | ||
145 | return; | ||
146 | } | ||
147 | env_buf = getenv (GNUNET_OS_CONTROL_PIPE); | ||
148 | if ((NULL == env_buf) || (strlen (env_buf) <= 0)) | ||
149 | { | ||
150 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
151 | "Not installing a handler because $%s is empty\n", | ||
152 | GNUNET_OS_CONTROL_PIPE); | ||
153 | putenv (GNUNET_OS_CONTROL_PIPE "="); | ||
154 | return; | ||
155 | } | ||
156 | errno = 0; | ||
157 | pipe_fd = strtoull (env_buf, &env_buf_end, 16); | ||
158 | if ((0 != errno) || (env_buf == env_buf_end)) | ||
159 | { | ||
160 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "strtoull", env_buf); | ||
161 | putenv (GNUNET_OS_CONTROL_PIPE "="); | ||
162 | return; | ||
163 | } | ||
164 | if (pipe_fd >= FD_SETSIZE) | ||
165 | { | ||
166 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
167 | "GNUNET_OS_CONTROL_PIPE `%s' contains garbage?\n", | ||
168 | env_buf); | ||
169 | putenv (GNUNET_OS_CONTROL_PIPE "="); | ||
170 | return; | ||
171 | } | ||
172 | |||
173 | control_pipe = GNUNET_DISK_get_handle_from_int_fd ((int) pipe_fd); | ||
174 | |||
175 | if (NULL == control_pipe) | ||
176 | { | ||
177 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf); | ||
178 | putenv (GNUNET_OS_CONTROL_PIPE "="); | ||
179 | return; | ||
180 | } | ||
181 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
182 | "Adding parent control handler pipe `%s' to the scheduler\n", | ||
183 | env_buf); | ||
184 | pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
185 | control_pipe, | ||
186 | &parent_control_handler, | ||
187 | control_pipe); | ||
188 | spch = GNUNET_SCHEDULER_add_shutdown (&shutdown_pch, control_pipe); | ||
189 | putenv (GNUNET_OS_CONTROL_PIPE "="); | ||
190 | } | ||
191 | |||
192 | |||
193 | /** | ||
194 | * Get process structure for current process | ||
195 | * | ||
196 | * The pointer it returns points to static memory location and must | ||
197 | * not be deallocated/closed. | ||
198 | * | ||
199 | * @return pointer to the process sturcutre for this process | ||
200 | */ | ||
201 | struct GNUNET_OS_Process * | ||
202 | GNUNET_OS_process_current () | ||
203 | { | ||
204 | current_process.pid = 0; | ||
205 | return ¤t_process; | ||
206 | } | ||
207 | |||
208 | |||
209 | int | ||
210 | GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, | ||
211 | int sig) | ||
212 | { | ||
213 | int ret; | ||
214 | char csig; | ||
215 | |||
216 | csig = (char) sig; | ||
217 | if (NULL != proc->control_pipe) | ||
218 | { | ||
219 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
220 | "Sending signal %d to pid: %u via pipe\n", | ||
221 | sig, | ||
222 | proc->pid); | ||
223 | ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof(csig)); | ||
224 | if (sizeof(csig) == ret) | ||
225 | return 0; | ||
226 | } | ||
227 | /* pipe failed or non-existent, try other methods */ | ||
228 | switch (sig) | ||
229 | { | ||
230 | case SIGHUP: | ||
231 | case SIGINT: | ||
232 | case SIGKILL: | ||
233 | case SIGTERM: | ||
234 | #if (SIGTERM != GNUNET_TERM_SIG) | ||
235 | case GNUNET_TERM_SIG: | ||
236 | #endif | ||
237 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
238 | "Sending signal %d to pid: %u via system call\n", | ||
239 | sig, | ||
240 | proc->pid); | ||
241 | return kill (proc->pid, sig); | ||
242 | default: | ||
243 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
244 | "Sending signal %d to pid: %u via system call\n", | ||
245 | sig, | ||
246 | proc->pid); | ||
247 | return kill (proc->pid, sig); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | |||
252 | pid_t | ||
253 | GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc) | ||
254 | { | ||
255 | return proc->pid; | ||
256 | } | ||
257 | |||
258 | |||
259 | void | ||
260 | GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc) | ||
261 | { | ||
262 | if (NULL != proc->control_pipe) | ||
263 | GNUNET_DISK_file_close (proc->control_pipe); | ||
264 | |||
265 | GNUNET_free (proc); | ||
266 | } | ||
267 | |||
268 | |||
269 | /** | ||
270 | * Open '/dev/null' and make the result the given | ||
271 | * file descriptor. | ||
272 | * | ||
273 | * @param target_fd desired FD to point to /dev/null | ||
274 | * @param flags open flags (O_RDONLY, O_WRONLY) | ||
275 | */ | ||
276 | static void | ||
277 | open_dev_null (int target_fd, | ||
278 | int flags) | ||
279 | { | ||
280 | int fd; | ||
281 | |||
282 | fd = open ("/dev/null", flags); | ||
283 | if (-1 == fd) | ||
284 | { | ||
285 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null"); | ||
286 | return; | ||
287 | } | ||
288 | if (fd == target_fd) | ||
289 | return; | ||
290 | if (-1 == dup2 (fd, target_fd)) | ||
291 | { | ||
292 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
293 | GNUNET_break (0 == close (fd)); | ||
294 | return; | ||
295 | } | ||
296 | GNUNET_break (0 == close (fd)); | ||
297 | } | ||
298 | |||
299 | |||
300 | /** | ||
301 | * Start a process. | ||
302 | * | ||
303 | * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which | ||
304 | * std handles of the parent are inherited by the child. | ||
305 | * pipe_stdin and pipe_stdout take priority over std_inheritance | ||
306 | * (when they are non-NULL). | ||
307 | * @param pipe_stdin pipe to use to send input to child process (or NULL) | ||
308 | * @param pipe_stdout pipe to use to get output from child process (or NULL) | ||
309 | * @param pipe_stderr pipe to use for stderr for child process (or NULL) | ||
310 | * @param lsocks array of listen sockets to dup systemd-style (or NULL); | ||
311 | * must be NULL on platforms where dup is not supported | ||
312 | * @param filename name of the binary | ||
313 | * @param argv NULL-terminated list of arguments to the process | ||
314 | * @return process ID of the new process, -1 on error | ||
315 | */ | ||
316 | static struct GNUNET_OS_Process * | ||
317 | start_process (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
318 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
319 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
320 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
321 | const int *lsocks, | ||
322 | const char *filename, | ||
323 | char *const argv[]) | ||
324 | { | ||
325 | pid_t ret; | ||
326 | char fds[16]; | ||
327 | struct GNUNET_OS_Process *gnunet_proc; | ||
328 | struct GNUNET_DISK_FileHandle *childpipe_read; | ||
329 | struct GNUNET_DISK_FileHandle *childpipe_write; | ||
330 | int childpipe_read_fd; | ||
331 | int i; | ||
332 | int j; | ||
333 | int k; | ||
334 | int tgt; | ||
335 | int flags; | ||
336 | int *lscp; | ||
337 | unsigned int ls; | ||
338 | int fd_stdout_write; | ||
339 | int fd_stdout_read; | ||
340 | int fd_stderr_write; | ||
341 | int fd_stderr_read; | ||
342 | int fd_stdin_read; | ||
343 | int fd_stdin_write; | ||
344 | |||
345 | if (GNUNET_SYSERR == | ||
346 | GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL)) | ||
347 | return NULL; /* not executable */ | ||
348 | if (0 != (std_inheritance & GNUNET_OS_USE_PIPE_CONTROL)) | ||
349 | { | ||
350 | struct GNUNET_DISK_PipeHandle *childpipe; | ||
351 | int dup_childpipe_read_fd = -1; | ||
352 | |||
353 | childpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); | ||
354 | if (NULL == childpipe) | ||
355 | return NULL; | ||
356 | childpipe_read = | ||
357 | GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ); | ||
358 | childpipe_write = | ||
359 | GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE); | ||
360 | GNUNET_DISK_pipe_close (childpipe); | ||
361 | if ((NULL == childpipe_read) || (NULL == childpipe_write) || | ||
362 | (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read, | ||
363 | &childpipe_read_fd, | ||
364 | sizeof(int))) || | ||
365 | (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd)))) | ||
366 | { | ||
367 | if (NULL != childpipe_read) | ||
368 | GNUNET_DISK_file_close (childpipe_read); | ||
369 | if (NULL != childpipe_write) | ||
370 | GNUNET_DISK_file_close (childpipe_write); | ||
371 | if (0 <= dup_childpipe_read_fd) | ||
372 | GNUNET_break (0 == close (dup_childpipe_read_fd)); | ||
373 | return NULL; | ||
374 | } | ||
375 | childpipe_read_fd = dup_childpipe_read_fd; | ||
376 | GNUNET_DISK_file_close (childpipe_read); | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | childpipe_write = NULL; | ||
381 | childpipe_read_fd = -1; | ||
382 | } | ||
383 | if (NULL != pipe_stdin) | ||
384 | { | ||
385 | GNUNET_assert ( | ||
386 | GNUNET_OK == | ||
387 | GNUNET_DISK_internal_file_handle_ ( | ||
388 | GNUNET_DISK_pipe_handle (pipe_stdin, GNUNET_DISK_PIPE_END_READ), | ||
389 | &fd_stdin_read, | ||
390 | sizeof(int))); | ||
391 | GNUNET_assert ( | ||
392 | GNUNET_OK == | ||
393 | GNUNET_DISK_internal_file_handle_ ( | ||
394 | GNUNET_DISK_pipe_handle (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE), | ||
395 | &fd_stdin_write, | ||
396 | sizeof(int))); | ||
397 | } | ||
398 | if (NULL != pipe_stdout) | ||
399 | { | ||
400 | GNUNET_assert ( | ||
401 | GNUNET_OK == | ||
402 | GNUNET_DISK_internal_file_handle_ ( | ||
403 | GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_WRITE), | ||
404 | &fd_stdout_write, | ||
405 | sizeof(int))); | ||
406 | GNUNET_assert ( | ||
407 | GNUNET_OK == | ||
408 | GNUNET_DISK_internal_file_handle_ ( | ||
409 | GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_READ), | ||
410 | &fd_stdout_read, | ||
411 | sizeof(int))); | ||
412 | } | ||
413 | if (NULL != pipe_stderr) | ||
414 | { | ||
415 | GNUNET_assert ( | ||
416 | GNUNET_OK == | ||
417 | GNUNET_DISK_internal_file_handle_ ( | ||
418 | GNUNET_DISK_pipe_handle (pipe_stderr, GNUNET_DISK_PIPE_END_READ), | ||
419 | &fd_stderr_read, | ||
420 | sizeof(int))); | ||
421 | GNUNET_assert ( | ||
422 | GNUNET_OK == | ||
423 | GNUNET_DISK_internal_file_handle_ ( | ||
424 | GNUNET_DISK_pipe_handle (pipe_stderr, GNUNET_DISK_PIPE_END_WRITE), | ||
425 | &fd_stderr_write, | ||
426 | sizeof(int))); | ||
427 | } | ||
428 | lscp = NULL; | ||
429 | ls = 0; | ||
430 | if (NULL != lsocks) | ||
431 | { | ||
432 | i = 0; | ||
433 | while (-1 != (k = lsocks[i++])) | ||
434 | GNUNET_array_append (lscp, ls, k); | ||
435 | GNUNET_array_append (lscp, ls, -1); | ||
436 | } | ||
437 | #if DARWIN | ||
438 | /* see https://web.archive.org/web/20150924082249/gnunet.org/vfork */ | ||
439 | #pragma GCC diagnostic push | ||
440 | #pragma GCC diagnostic ignored "-Wdeprecated" | ||
441 | #pragma clang diagnostic push | ||
442 | #pragma clang diagnostic ignored "-Wdeprecated" | ||
443 | ret = vfork (); | ||
444 | #pragma clang diagnostic pop | ||
445 | #pragma GCC diagnostic pop | ||
446 | #else | ||
447 | ret = fork (); | ||
448 | #endif | ||
449 | if (-1 == ret) | ||
450 | { | ||
451 | int eno = errno; | ||
452 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork"); | ||
453 | GNUNET_array_grow (lscp, ls, 0); | ||
454 | if (NULL != childpipe_write) | ||
455 | GNUNET_DISK_file_close (childpipe_write); | ||
456 | if (0 <= childpipe_read_fd) | ||
457 | GNUNET_break (0 == close (childpipe_read_fd)); | ||
458 | errno = eno; | ||
459 | return NULL; | ||
460 | } | ||
461 | if (0 != ret) | ||
462 | { | ||
463 | unsetenv (GNUNET_OS_CONTROL_PIPE); | ||
464 | gnunet_proc = GNUNET_new (struct GNUNET_OS_Process); | ||
465 | gnunet_proc->pid = ret; | ||
466 | gnunet_proc->control_pipe = childpipe_write; | ||
467 | if (0 != (std_inheritance & GNUNET_OS_USE_PIPE_CONTROL)) | ||
468 | { | ||
469 | GNUNET_break (0 == close (childpipe_read_fd)); | ||
470 | } | ||
471 | GNUNET_array_grow (lscp, ls, 0); | ||
472 | return gnunet_proc; | ||
473 | } | ||
474 | if (0 <= childpipe_read_fd) | ||
475 | { | ||
476 | char fdbuf[100]; | ||
477 | #ifndef DARWIN | ||
478 | /* due to vfork, we must NOT free memory on DARWIN! */ | ||
479 | GNUNET_DISK_file_close (childpipe_write); | ||
480 | #endif | ||
481 | snprintf (fdbuf, 100, "%x", childpipe_read_fd); | ||
482 | setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1); | ||
483 | } | ||
484 | else | ||
485 | unsetenv (GNUNET_OS_CONTROL_PIPE); | ||
486 | if (NULL != pipe_stdin) | ||
487 | { | ||
488 | GNUNET_break (0 == close (fd_stdin_write)); | ||
489 | if (-1 == dup2 (fd_stdin_read, 0)) | ||
490 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
491 | GNUNET_break (0 == close (fd_stdin_read)); | ||
492 | } | ||
493 | else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN)) | ||
494 | { | ||
495 | GNUNET_break (0 == close (0)); | ||
496 | open_dev_null (0, O_RDONLY); | ||
497 | } | ||
498 | if (NULL != pipe_stdout) | ||
499 | { | ||
500 | GNUNET_break (0 == close (fd_stdout_read)); | ||
501 | if (-1 == dup2 (fd_stdout_write, 1)) | ||
502 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
503 | GNUNET_break (0 == close (fd_stdout_write)); | ||
504 | } | ||
505 | else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)) | ||
506 | { | ||
507 | GNUNET_break (0 == close (1)); | ||
508 | open_dev_null (1, O_WRONLY); | ||
509 | } | ||
510 | if (NULL != pipe_stderr) | ||
511 | { | ||
512 | GNUNET_break (0 == close (fd_stderr_read)); | ||
513 | if (-1 == dup2 (fd_stderr_write, 2)) | ||
514 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
515 | GNUNET_break (0 == close (fd_stderr_write)); | ||
516 | } | ||
517 | else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)) | ||
518 | { | ||
519 | GNUNET_break (0 == close (2)); | ||
520 | open_dev_null (2, O_WRONLY); | ||
521 | } | ||
522 | if (NULL != lscp) | ||
523 | { | ||
524 | /* read systemd documentation... */ | ||
525 | i = 0; | ||
526 | tgt = 3; | ||
527 | while (-1 != lscp[i]) | ||
528 | { | ||
529 | j = i + 1; | ||
530 | while (-1 != lscp[j]) | ||
531 | { | ||
532 | if (lscp[j] == tgt) | ||
533 | { | ||
534 | /* dup away */ | ||
535 | k = dup (lscp[j]); | ||
536 | GNUNET_assert (-1 != k); | ||
537 | GNUNET_assert (0 == close (lscp[j])); | ||
538 | lscp[j] = k; | ||
539 | break; | ||
540 | } | ||
541 | j++; | ||
542 | } | ||
543 | if (lscp[i] != tgt) | ||
544 | { | ||
545 | /* Bury any existing FD, no matter what; they should all be closed | ||
546 | * on exec anyway and the important ones have been dup'ed away */ | ||
547 | GNUNET_break (0 == close (tgt)); | ||
548 | GNUNET_assert (-1 != dup2 (lscp[i], tgt)); | ||
549 | } | ||
550 | /* unset close-on-exec flag */ | ||
551 | flags = fcntl (tgt, F_GETFD); | ||
552 | GNUNET_assert (flags >= 0); | ||
553 | flags &= ~FD_CLOEXEC; | ||
554 | fflush (stderr); | ||
555 | (void) fcntl (tgt, F_SETFD, flags); | ||
556 | tgt++; | ||
557 | i++; | ||
558 | } | ||
559 | GNUNET_snprintf (fds, sizeof(fds), "%u", i); | ||
560 | setenv ("LISTEN_FDS", fds, 1); | ||
561 | } | ||
562 | #ifndef DARWIN | ||
563 | /* due to vfork, we must NOT free memory on DARWIN! */ | ||
564 | GNUNET_array_grow (lscp, ls, 0); | ||
565 | #endif | ||
566 | execvp (filename, argv); | ||
567 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename); | ||
568 | _exit (1); | ||
569 | } | ||
570 | |||
571 | |||
572 | struct GNUNET_OS_Process * | ||
573 | GNUNET_OS_start_process_vap (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
574 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
575 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
576 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
577 | const char *filename, | ||
578 | char *const argv[]) | ||
579 | { | ||
580 | return start_process (std_inheritance, | ||
581 | pipe_stdin, | ||
582 | pipe_stdout, | ||
583 | pipe_stderr, | ||
584 | NULL, | ||
585 | filename, | ||
586 | argv); | ||
587 | } | ||
588 | |||
589 | |||
590 | struct GNUNET_OS_Process * | ||
591 | GNUNET_OS_start_process_va (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
592 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
593 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
594 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
595 | const char *filename, | ||
596 | va_list va) | ||
597 | { | ||
598 | struct GNUNET_OS_Process *ret; | ||
599 | va_list ap; | ||
600 | char **argv; | ||
601 | int argc; | ||
602 | |||
603 | argc = 0; | ||
604 | va_copy (ap, va); | ||
605 | while (NULL != va_arg (ap, char *)) | ||
606 | argc++; | ||
607 | va_end (ap); | ||
608 | argv = GNUNET_malloc (sizeof(char *) * (argc + 1)); | ||
609 | argc = 0; | ||
610 | va_copy (ap, va); | ||
611 | while (NULL != (argv[argc] = va_arg (ap, char *))) | ||
612 | argc++; | ||
613 | va_end (ap); | ||
614 | ret = GNUNET_OS_start_process_vap (std_inheritance, | ||
615 | pipe_stdin, | ||
616 | pipe_stdout, | ||
617 | pipe_stderr, | ||
618 | filename, | ||
619 | argv); | ||
620 | GNUNET_free (argv); | ||
621 | return ret; | ||
622 | } | ||
623 | |||
624 | |||
625 | struct GNUNET_OS_Process * | ||
626 | GNUNET_OS_start_process (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
627 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
628 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
629 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
630 | const char *filename, | ||
631 | ...) | ||
632 | { | ||
633 | struct GNUNET_OS_Process *ret; | ||
634 | va_list ap; | ||
635 | |||
636 | va_start (ap, filename); | ||
637 | ret = GNUNET_OS_start_process_va (std_inheritance, | ||
638 | pipe_stdin, | ||
639 | pipe_stdout, | ||
640 | pipe_stderr, | ||
641 | filename, | ||
642 | ap); | ||
643 | va_end (ap); | ||
644 | return ret; | ||
645 | } | ||
646 | |||
647 | |||
648 | struct GNUNET_OS_Process * | ||
649 | GNUNET_OS_start_process_v (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
650 | const int *lsocks, | ||
651 | const char *filename, | ||
652 | char *const argv[]) | ||
653 | { | ||
654 | return start_process (std_inheritance, | ||
655 | NULL, | ||
656 | NULL, | ||
657 | NULL, | ||
658 | lsocks, | ||
659 | filename, | ||
660 | argv); | ||
661 | } | ||
662 | |||
663 | |||
664 | struct GNUNET_OS_Process * | ||
665 | GNUNET_OS_start_process_s (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
666 | const int *lsocks, | ||
667 | const char *filename, | ||
668 | ...) | ||
669 | { | ||
670 | va_list ap; | ||
671 | char **argv; | ||
672 | unsigned int argv_size; | ||
673 | const char *arg; | ||
674 | const char *rpos; | ||
675 | char *pos; | ||
676 | char *cp; | ||
677 | const char *last; | ||
678 | struct GNUNET_OS_Process *proc; | ||
679 | char *binary_path; | ||
680 | int quote_on; | ||
681 | unsigned int i; | ||
682 | size_t len; | ||
683 | |||
684 | argv_size = 1; | ||
685 | va_start (ap, filename); | ||
686 | arg = filename; | ||
687 | last = NULL; | ||
688 | do | ||
689 | { | ||
690 | rpos = arg; | ||
691 | quote_on = 0; | ||
692 | while ('\0' != *rpos) | ||
693 | { | ||
694 | if ('"' == *rpos) | ||
695 | { | ||
696 | if (1 == quote_on) | ||
697 | quote_on = 0; | ||
698 | else | ||
699 | quote_on = 1; | ||
700 | } | ||
701 | if ((' ' == *rpos) && (0 == quote_on)) | ||
702 | { | ||
703 | if (NULL != last) | ||
704 | argv_size++; | ||
705 | last = NULL; | ||
706 | rpos++; | ||
707 | while (' ' == *rpos) | ||
708 | rpos++; | ||
709 | } | ||
710 | if ((NULL == last) && ('\0' != *rpos)) // FIXME: == or !=? | ||
711 | last = rpos; | ||
712 | if ('\0' != *rpos) | ||
713 | rpos++; | ||
714 | } | ||
715 | if (NULL != last) | ||
716 | argv_size++; | ||
717 | } | ||
718 | while (NULL != (arg = (va_arg (ap, const char *)))); | ||
719 | va_end (ap); | ||
720 | |||
721 | argv = GNUNET_malloc (argv_size * sizeof(char *)); | ||
722 | argv_size = 0; | ||
723 | va_start (ap, filename); | ||
724 | arg = filename; | ||
725 | last = NULL; | ||
726 | do | ||
727 | { | ||
728 | cp = GNUNET_strdup (arg); | ||
729 | quote_on = 0; | ||
730 | pos = cp; | ||
731 | while ('\0' != *pos) | ||
732 | { | ||
733 | if ('"' == *pos) | ||
734 | { | ||
735 | if (1 == quote_on) | ||
736 | quote_on = 0; | ||
737 | else | ||
738 | quote_on = 1; | ||
739 | } | ||
740 | if ((' ' == *pos) && (0 == quote_on)) | ||
741 | { | ||
742 | *pos = '\0'; | ||
743 | if (NULL != last) | ||
744 | argv[argv_size++] = GNUNET_strdup (last); | ||
745 | last = NULL; | ||
746 | pos++; | ||
747 | while (' ' == *pos) | ||
748 | pos++; | ||
749 | } | ||
750 | if ((NULL == last) && ('\0' != *pos)) // FIXME: == or !=? | ||
751 | last = pos; | ||
752 | if ('\0' != *pos) | ||
753 | pos++; | ||
754 | } | ||
755 | if (NULL != last) | ||
756 | argv[argv_size++] = GNUNET_strdup (last); | ||
757 | last = NULL; | ||
758 | GNUNET_free (cp); | ||
759 | } | ||
760 | while (NULL != (arg = (va_arg (ap, const char *)))); | ||
761 | va_end (ap); | ||
762 | argv[argv_size] = NULL; | ||
763 | |||
764 | for (i = 0; i < argv_size; i++) | ||
765 | { | ||
766 | len = strlen (argv[i]); | ||
767 | if ((argv[i][0] == '"') && (argv[i][len - 1] == '"')) | ||
768 | { | ||
769 | memmove (&argv[i][0], &argv[i][1], len - 2); | ||
770 | argv[i][len - 2] = '\0'; | ||
771 | } | ||
772 | } | ||
773 | binary_path = argv[0]; | ||
774 | proc = GNUNET_OS_start_process_v (std_inheritance, | ||
775 | lsocks, | ||
776 | binary_path, | ||
777 | argv); | ||
778 | while (argv_size > 0) | ||
779 | GNUNET_free_nz (argv[--argv_size]); | ||
780 | GNUNET_free (argv); | ||
781 | return proc; | ||
782 | } | ||
783 | |||
784 | |||
785 | /** | ||
786 | * Retrieve the status of a process, waiting on it if dead. | ||
787 | * Nonblocking version. | ||
788 | * | ||
789 | * @param proc process ID | ||
790 | * @param type status type | ||
791 | * @param code return code/signal number | ||
792 | * @param options WNOHANG if non-blocking is desired | ||
793 | * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise | ||
794 | */ | ||
795 | static enum GNUNET_GenericReturnValue | ||
796 | process_status (struct GNUNET_OS_Process *proc, | ||
797 | enum GNUNET_OS_ProcessStatusType *type, | ||
798 | unsigned long *code, | ||
799 | int options) | ||
800 | { | ||
801 | int status; | ||
802 | int ret; | ||
803 | |||
804 | GNUNET_assert (0 != proc); | ||
805 | ret = waitpid (proc->pid, | ||
806 | &status, | ||
807 | options); | ||
808 | if (ret < 0) | ||
809 | { | ||
810 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, | ||
811 | "waitpid"); | ||
812 | return GNUNET_SYSERR; | ||
813 | } | ||
814 | if (0 == ret) | ||
815 | { | ||
816 | *type = GNUNET_OS_PROCESS_RUNNING; | ||
817 | *code = 0; | ||
818 | return GNUNET_NO; | ||
819 | } | ||
820 | if (proc->pid != ret) | ||
821 | { | ||
822 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, | ||
823 | "waitpid"); | ||
824 | return GNUNET_SYSERR; | ||
825 | } | ||
826 | if (WIFEXITED (status)) | ||
827 | { | ||
828 | *type = GNUNET_OS_PROCESS_EXITED; | ||
829 | *code = WEXITSTATUS (status); | ||
830 | } | ||
831 | else if (WIFSIGNALED (status)) | ||
832 | { | ||
833 | *type = GNUNET_OS_PROCESS_SIGNALED; | ||
834 | *code = WTERMSIG (status); | ||
835 | } | ||
836 | else if (WIFSTOPPED (status)) | ||
837 | { | ||
838 | *type = GNUNET_OS_PROCESS_SIGNALED; | ||
839 | *code = WSTOPSIG (status); | ||
840 | } | ||
841 | #ifdef WIFCONTINUED | ||
842 | else if (WIFCONTINUED (status)) | ||
843 | { | ||
844 | *type = GNUNET_OS_PROCESS_RUNNING; | ||
845 | *code = 0; | ||
846 | } | ||
847 | #endif | ||
848 | else | ||
849 | { | ||
850 | *type = GNUNET_OS_PROCESS_UNKNOWN; | ||
851 | *code = 0; | ||
852 | } | ||
853 | |||
854 | return GNUNET_OK; | ||
855 | } | ||
856 | |||
857 | |||
858 | enum GNUNET_GenericReturnValue | ||
859 | GNUNET_OS_process_status (struct GNUNET_OS_Process *proc, | ||
860 | enum GNUNET_OS_ProcessStatusType *type, | ||
861 | unsigned long *code) | ||
862 | { | ||
863 | return process_status (proc, type, code, WNOHANG); | ||
864 | } | ||
865 | |||
866 | |||
867 | enum GNUNET_GenericReturnValue | ||
868 | GNUNET_OS_process_wait_status (struct GNUNET_OS_Process *proc, | ||
869 | enum GNUNET_OS_ProcessStatusType *type, | ||
870 | unsigned long *code) | ||
871 | { | ||
872 | return process_status (proc, type, code, 0); | ||
873 | } | ||
874 | |||
875 | |||
876 | enum GNUNET_GenericReturnValue | ||
877 | GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc) | ||
878 | { | ||
879 | pid_t pid = proc->pid; | ||
880 | pid_t ret; | ||
881 | |||
882 | while ((pid != (ret = waitpid (pid, NULL, 0))) && (EINTR == errno)) | ||
883 | ; | ||
884 | if (pid != ret) | ||
885 | { | ||
886 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, | ||
887 | "waitpid"); | ||
888 | return GNUNET_SYSERR; | ||
889 | } | ||
890 | return GNUNET_OK; | ||
891 | } | ||
892 | |||
893 | |||
894 | /** | ||
895 | * Handle to a command. | ||
896 | */ | ||
897 | struct GNUNET_OS_CommandHandle | ||
898 | { | ||
899 | /** | ||
900 | * Process handle. | ||
901 | */ | ||
902 | struct GNUNET_OS_Process *eip; | ||
903 | |||
904 | /** | ||
905 | * Handle to the output pipe. | ||
906 | */ | ||
907 | struct GNUNET_DISK_PipeHandle *opipe; | ||
908 | |||
909 | /** | ||
910 | * Read-end of output pipe. | ||
911 | */ | ||
912 | const struct GNUNET_DISK_FileHandle *r; | ||
913 | |||
914 | /** | ||
915 | * Function to call on each line of output. | ||
916 | */ | ||
917 | GNUNET_OS_LineProcessor proc; | ||
918 | |||
919 | /** | ||
920 | * Closure for @e proc. | ||
921 | */ | ||
922 | void *proc_cls; | ||
923 | |||
924 | /** | ||
925 | * Buffer for the output. | ||
926 | */ | ||
927 | char buf[1024]; | ||
928 | |||
929 | /** | ||
930 | * Task reading from pipe. | ||
931 | */ | ||
932 | struct GNUNET_SCHEDULER_Task *rtask; | ||
933 | |||
934 | /** | ||
935 | * When to time out. | ||
936 | */ | ||
937 | struct GNUNET_TIME_Absolute timeout; | ||
938 | |||
939 | /** | ||
940 | * Current read offset in buf. | ||
941 | */ | ||
942 | size_t off; | ||
943 | }; | ||
944 | |||
945 | |||
946 | void | ||
947 | GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd) | ||
948 | { | ||
949 | if (NULL != cmd->proc) | ||
950 | { | ||
951 | GNUNET_assert (NULL != cmd->rtask); | ||
952 | GNUNET_SCHEDULER_cancel (cmd->rtask); | ||
953 | } | ||
954 | (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL); | ||
955 | GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip)); | ||
956 | GNUNET_OS_process_destroy (cmd->eip); | ||
957 | GNUNET_DISK_pipe_close (cmd->opipe); | ||
958 | GNUNET_free (cmd); | ||
959 | } | ||
960 | |||
961 | |||
962 | /** | ||
963 | * Read from the process and call the line processor. | ||
964 | * | ||
965 | * @param cls the `struct GNUNET_OS_CommandHandle *` | ||
966 | */ | ||
967 | static void | ||
968 | cmd_read (void *cls) | ||
969 | { | ||
970 | struct GNUNET_OS_CommandHandle *cmd = cls; | ||
971 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
972 | GNUNET_OS_LineProcessor proc; | ||
973 | char *end; | ||
974 | ssize_t ret; | ||
975 | |||
976 | cmd->rtask = NULL; | ||
977 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
978 | if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r)) | ||
979 | { | ||
980 | /* timeout */ | ||
981 | proc = cmd->proc; | ||
982 | cmd->proc = NULL; | ||
983 | proc (cmd->proc_cls, NULL); | ||
984 | return; | ||
985 | } | ||
986 | ret = GNUNET_DISK_file_read (cmd->r, | ||
987 | &cmd->buf[cmd->off], | ||
988 | sizeof(cmd->buf) - cmd->off); | ||
989 | if (ret <= 0) | ||
990 | { | ||
991 | if ((cmd->off > 0) && (cmd->off < sizeof(cmd->buf))) | ||
992 | { | ||
993 | cmd->buf[cmd->off] = '\0'; | ||
994 | cmd->proc (cmd->proc_cls, cmd->buf); | ||
995 | } | ||
996 | proc = cmd->proc; | ||
997 | cmd->proc = NULL; | ||
998 | proc (cmd->proc_cls, NULL); | ||
999 | return; | ||
1000 | } | ||
1001 | end = memchr (&cmd->buf[cmd->off], '\n', ret); | ||
1002 | cmd->off += ret; | ||
1003 | while (NULL != end) | ||
1004 | { | ||
1005 | *end = '\0'; | ||
1006 | cmd->proc (cmd->proc_cls, cmd->buf); | ||
1007 | memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf)); | ||
1008 | cmd->off -= (end + 1 - cmd->buf); | ||
1009 | end = memchr (cmd->buf, '\n', cmd->off); | ||
1010 | } | ||
1011 | cmd->rtask = | ||
1012 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining ( | ||
1013 | cmd->timeout), | ||
1014 | cmd->r, | ||
1015 | &cmd_read, | ||
1016 | cmd); | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | struct GNUNET_OS_CommandHandle * | ||
1021 | GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, | ||
1022 | void *proc_cls, | ||
1023 | struct GNUNET_TIME_Relative timeout, | ||
1024 | const char *binary, | ||
1025 | ...) | ||
1026 | { | ||
1027 | struct GNUNET_OS_CommandHandle *cmd; | ||
1028 | struct GNUNET_OS_Process *eip; | ||
1029 | struct GNUNET_DISK_PipeHandle *opipe; | ||
1030 | va_list ap; | ||
1031 | |||
1032 | opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); | ||
1033 | if (NULL == opipe) | ||
1034 | return NULL; | ||
1035 | va_start (ap, binary); | ||
1036 | /* redirect stdout, don't inherit stderr/stdin */ | ||
1037 | eip = | ||
1038 | GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_NONE, | ||
1039 | NULL, | ||
1040 | opipe, | ||
1041 | NULL, | ||
1042 | binary, | ||
1043 | ap); | ||
1044 | va_end (ap); | ||
1045 | if (NULL == eip) | ||
1046 | { | ||
1047 | GNUNET_DISK_pipe_close (opipe); | ||
1048 | return NULL; | ||
1049 | } | ||
1050 | GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE); | ||
1051 | cmd = GNUNET_new (struct GNUNET_OS_CommandHandle); | ||
1052 | cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1053 | cmd->eip = eip; | ||
1054 | cmd->opipe = opipe; | ||
1055 | cmd->proc = proc; | ||
1056 | cmd->proc_cls = proc_cls; | ||
1057 | cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ); | ||
1058 | cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd); | ||
1059 | return cmd; | ||
1060 | } | ||
1061 | |||
1062 | |||
1063 | /* end of os_priority.c */ | ||