diff options
Diffstat (limited to 'src/util/os_priority.c')
-rw-r--r-- | src/util/os_priority.c | 1057 |
1 files changed, 0 insertions, 1057 deletions
diff --git a/src/util/os_priority.c b/src/util/os_priority.c deleted file mode 100644 index de4e6c395..000000000 --- a/src/util/os_priority.c +++ /dev/null | |||
@@ -1,1057 +0,0 @@ | |||
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 | ret = vfork (); | ||
440 | #else | ||
441 | ret = fork (); | ||
442 | #endif | ||
443 | if (-1 == ret) | ||
444 | { | ||
445 | int eno = errno; | ||
446 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork"); | ||
447 | GNUNET_array_grow (lscp, ls, 0); | ||
448 | if (NULL != childpipe_write) | ||
449 | GNUNET_DISK_file_close (childpipe_write); | ||
450 | if (0 <= childpipe_read_fd) | ||
451 | GNUNET_break (0 == close (childpipe_read_fd)); | ||
452 | errno = eno; | ||
453 | return NULL; | ||
454 | } | ||
455 | if (0 != ret) | ||
456 | { | ||
457 | unsetenv (GNUNET_OS_CONTROL_PIPE); | ||
458 | gnunet_proc = GNUNET_new (struct GNUNET_OS_Process); | ||
459 | gnunet_proc->pid = ret; | ||
460 | gnunet_proc->control_pipe = childpipe_write; | ||
461 | if (0 != (std_inheritance & GNUNET_OS_USE_PIPE_CONTROL)) | ||
462 | { | ||
463 | GNUNET_break (0 == close (childpipe_read_fd)); | ||
464 | } | ||
465 | GNUNET_array_grow (lscp, ls, 0); | ||
466 | return gnunet_proc; | ||
467 | } | ||
468 | if (0 <= childpipe_read_fd) | ||
469 | { | ||
470 | char fdbuf[100]; | ||
471 | #ifndef DARWIN | ||
472 | /* due to vfork, we must NOT free memory on DARWIN! */ | ||
473 | GNUNET_DISK_file_close (childpipe_write); | ||
474 | #endif | ||
475 | snprintf (fdbuf, 100, "%x", childpipe_read_fd); | ||
476 | setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1); | ||
477 | } | ||
478 | else | ||
479 | unsetenv (GNUNET_OS_CONTROL_PIPE); | ||
480 | if (NULL != pipe_stdin) | ||
481 | { | ||
482 | GNUNET_break (0 == close (fd_stdin_write)); | ||
483 | if (-1 == dup2 (fd_stdin_read, 0)) | ||
484 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
485 | GNUNET_break (0 == close (fd_stdin_read)); | ||
486 | } | ||
487 | else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN)) | ||
488 | { | ||
489 | GNUNET_break (0 == close (0)); | ||
490 | open_dev_null (0, O_RDONLY); | ||
491 | } | ||
492 | if (NULL != pipe_stdout) | ||
493 | { | ||
494 | GNUNET_break (0 == close (fd_stdout_read)); | ||
495 | if (-1 == dup2 (fd_stdout_write, 1)) | ||
496 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
497 | GNUNET_break (0 == close (fd_stdout_write)); | ||
498 | } | ||
499 | else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT)) | ||
500 | { | ||
501 | GNUNET_break (0 == close (1)); | ||
502 | open_dev_null (1, O_WRONLY); | ||
503 | } | ||
504 | if (NULL != pipe_stderr) | ||
505 | { | ||
506 | GNUNET_break (0 == close (fd_stderr_read)); | ||
507 | if (-1 == dup2 (fd_stderr_write, 2)) | ||
508 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
509 | GNUNET_break (0 == close (fd_stderr_write)); | ||
510 | } | ||
511 | else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR)) | ||
512 | { | ||
513 | GNUNET_break (0 == close (2)); | ||
514 | open_dev_null (2, O_WRONLY); | ||
515 | } | ||
516 | if (NULL != lscp) | ||
517 | { | ||
518 | /* read systemd documentation... */ | ||
519 | i = 0; | ||
520 | tgt = 3; | ||
521 | while (-1 != lscp[i]) | ||
522 | { | ||
523 | j = i + 1; | ||
524 | while (-1 != lscp[j]) | ||
525 | { | ||
526 | if (lscp[j] == tgt) | ||
527 | { | ||
528 | /* dup away */ | ||
529 | k = dup (lscp[j]); | ||
530 | GNUNET_assert (-1 != k); | ||
531 | GNUNET_assert (0 == close (lscp[j])); | ||
532 | lscp[j] = k; | ||
533 | break; | ||
534 | } | ||
535 | j++; | ||
536 | } | ||
537 | if (lscp[i] != tgt) | ||
538 | { | ||
539 | /* Bury any existing FD, no matter what; they should all be closed | ||
540 | * on exec anyway and the important ones have been dup'ed away */ | ||
541 | GNUNET_break (0 == close (tgt)); | ||
542 | GNUNET_assert (-1 != dup2 (lscp[i], tgt)); | ||
543 | } | ||
544 | /* unset close-on-exec flag */ | ||
545 | flags = fcntl (tgt, F_GETFD); | ||
546 | GNUNET_assert (flags >= 0); | ||
547 | flags &= ~FD_CLOEXEC; | ||
548 | fflush (stderr); | ||
549 | (void) fcntl (tgt, F_SETFD, flags); | ||
550 | tgt++; | ||
551 | i++; | ||
552 | } | ||
553 | GNUNET_snprintf (fds, sizeof(fds), "%u", i); | ||
554 | setenv ("LISTEN_FDS", fds, 1); | ||
555 | } | ||
556 | #ifndef DARWIN | ||
557 | /* due to vfork, we must NOT free memory on DARWIN! */ | ||
558 | GNUNET_array_grow (lscp, ls, 0); | ||
559 | #endif | ||
560 | execvp (filename, argv); | ||
561 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename); | ||
562 | _exit (1); | ||
563 | } | ||
564 | |||
565 | |||
566 | struct GNUNET_OS_Process * | ||
567 | GNUNET_OS_start_process_vap (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
568 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
569 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
570 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
571 | const char *filename, | ||
572 | char *const argv[]) | ||
573 | { | ||
574 | return start_process (std_inheritance, | ||
575 | pipe_stdin, | ||
576 | pipe_stdout, | ||
577 | pipe_stderr, | ||
578 | NULL, | ||
579 | filename, | ||
580 | argv); | ||
581 | } | ||
582 | |||
583 | |||
584 | struct GNUNET_OS_Process * | ||
585 | GNUNET_OS_start_process_va (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
586 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
587 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
588 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
589 | const char *filename, | ||
590 | va_list va) | ||
591 | { | ||
592 | struct GNUNET_OS_Process *ret; | ||
593 | va_list ap; | ||
594 | char **argv; | ||
595 | int argc; | ||
596 | |||
597 | argc = 0; | ||
598 | va_copy (ap, va); | ||
599 | while (NULL != va_arg (ap, char *)) | ||
600 | argc++; | ||
601 | va_end (ap); | ||
602 | argv = GNUNET_malloc (sizeof(char *) * (argc + 1)); | ||
603 | argc = 0; | ||
604 | va_copy (ap, va); | ||
605 | while (NULL != (argv[argc] = va_arg (ap, char *))) | ||
606 | argc++; | ||
607 | va_end (ap); | ||
608 | ret = GNUNET_OS_start_process_vap (std_inheritance, | ||
609 | pipe_stdin, | ||
610 | pipe_stdout, | ||
611 | pipe_stderr, | ||
612 | filename, | ||
613 | argv); | ||
614 | GNUNET_free (argv); | ||
615 | return ret; | ||
616 | } | ||
617 | |||
618 | |||
619 | struct GNUNET_OS_Process * | ||
620 | GNUNET_OS_start_process (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
621 | struct GNUNET_DISK_PipeHandle *pipe_stdin, | ||
622 | struct GNUNET_DISK_PipeHandle *pipe_stdout, | ||
623 | struct GNUNET_DISK_PipeHandle *pipe_stderr, | ||
624 | const char *filename, | ||
625 | ...) | ||
626 | { | ||
627 | struct GNUNET_OS_Process *ret; | ||
628 | va_list ap; | ||
629 | |||
630 | va_start (ap, filename); | ||
631 | ret = GNUNET_OS_start_process_va (std_inheritance, | ||
632 | pipe_stdin, | ||
633 | pipe_stdout, | ||
634 | pipe_stderr, | ||
635 | filename, | ||
636 | ap); | ||
637 | va_end (ap); | ||
638 | return ret; | ||
639 | } | ||
640 | |||
641 | |||
642 | struct GNUNET_OS_Process * | ||
643 | GNUNET_OS_start_process_v (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
644 | const int *lsocks, | ||
645 | const char *filename, | ||
646 | char *const argv[]) | ||
647 | { | ||
648 | return start_process (std_inheritance, | ||
649 | NULL, | ||
650 | NULL, | ||
651 | NULL, | ||
652 | lsocks, | ||
653 | filename, | ||
654 | argv); | ||
655 | } | ||
656 | |||
657 | |||
658 | struct GNUNET_OS_Process * | ||
659 | GNUNET_OS_start_process_s (enum GNUNET_OS_InheritStdioFlags std_inheritance, | ||
660 | const int *lsocks, | ||
661 | const char *filename, | ||
662 | ...) | ||
663 | { | ||
664 | va_list ap; | ||
665 | char **argv; | ||
666 | unsigned int argv_size; | ||
667 | const char *arg; | ||
668 | const char *rpos; | ||
669 | char *pos; | ||
670 | char *cp; | ||
671 | const char *last; | ||
672 | struct GNUNET_OS_Process *proc; | ||
673 | char *binary_path; | ||
674 | int quote_on; | ||
675 | unsigned int i; | ||
676 | size_t len; | ||
677 | |||
678 | argv_size = 1; | ||
679 | va_start (ap, filename); | ||
680 | arg = filename; | ||
681 | last = NULL; | ||
682 | do | ||
683 | { | ||
684 | rpos = arg; | ||
685 | quote_on = 0; | ||
686 | while ('\0' != *rpos) | ||
687 | { | ||
688 | if ('"' == *rpos) | ||
689 | { | ||
690 | if (1 == quote_on) | ||
691 | quote_on = 0; | ||
692 | else | ||
693 | quote_on = 1; | ||
694 | } | ||
695 | if ((' ' == *rpos) && (0 == quote_on)) | ||
696 | { | ||
697 | if (NULL != last) | ||
698 | argv_size++; | ||
699 | last = NULL; | ||
700 | rpos++; | ||
701 | while (' ' == *rpos) | ||
702 | rpos++; | ||
703 | } | ||
704 | if ((NULL == last) && ('\0' != *rpos)) // FIXME: == or !=? | ||
705 | last = rpos; | ||
706 | if ('\0' != *rpos) | ||
707 | rpos++; | ||
708 | } | ||
709 | if (NULL != last) | ||
710 | argv_size++; | ||
711 | } | ||
712 | while (NULL != (arg = (va_arg (ap, const char *)))); | ||
713 | va_end (ap); | ||
714 | |||
715 | argv = GNUNET_malloc (argv_size * sizeof(char *)); | ||
716 | argv_size = 0; | ||
717 | va_start (ap, filename); | ||
718 | arg = filename; | ||
719 | last = NULL; | ||
720 | do | ||
721 | { | ||
722 | cp = GNUNET_strdup (arg); | ||
723 | quote_on = 0; | ||
724 | pos = cp; | ||
725 | while ('\0' != *pos) | ||
726 | { | ||
727 | if ('"' == *pos) | ||
728 | { | ||
729 | if (1 == quote_on) | ||
730 | quote_on = 0; | ||
731 | else | ||
732 | quote_on = 1; | ||
733 | } | ||
734 | if ((' ' == *pos) && (0 == quote_on)) | ||
735 | { | ||
736 | *pos = '\0'; | ||
737 | if (NULL != last) | ||
738 | argv[argv_size++] = GNUNET_strdup (last); | ||
739 | last = NULL; | ||
740 | pos++; | ||
741 | while (' ' == *pos) | ||
742 | pos++; | ||
743 | } | ||
744 | if ((NULL == last) && ('\0' != *pos)) // FIXME: == or !=? | ||
745 | last = pos; | ||
746 | if ('\0' != *pos) | ||
747 | pos++; | ||
748 | } | ||
749 | if (NULL != last) | ||
750 | argv[argv_size++] = GNUNET_strdup (last); | ||
751 | last = NULL; | ||
752 | GNUNET_free (cp); | ||
753 | } | ||
754 | while (NULL != (arg = (va_arg (ap, const char *)))); | ||
755 | va_end (ap); | ||
756 | argv[argv_size] = NULL; | ||
757 | |||
758 | for (i = 0; i < argv_size; i++) | ||
759 | { | ||
760 | len = strlen (argv[i]); | ||
761 | if ((argv[i][0] == '"') && (argv[i][len - 1] == '"')) | ||
762 | { | ||
763 | memmove (&argv[i][0], &argv[i][1], len - 2); | ||
764 | argv[i][len - 2] = '\0'; | ||
765 | } | ||
766 | } | ||
767 | binary_path = argv[0]; | ||
768 | proc = GNUNET_OS_start_process_v (std_inheritance, | ||
769 | lsocks, | ||
770 | binary_path, | ||
771 | argv); | ||
772 | while (argv_size > 0) | ||
773 | GNUNET_free_nz (argv[--argv_size]); | ||
774 | GNUNET_free (argv); | ||
775 | return proc; | ||
776 | } | ||
777 | |||
778 | |||
779 | /** | ||
780 | * Retrieve the status of a process, waiting on it if dead. | ||
781 | * Nonblocking version. | ||
782 | * | ||
783 | * @param proc process ID | ||
784 | * @param type status type | ||
785 | * @param code return code/signal number | ||
786 | * @param options WNOHANG if non-blocking is desired | ||
787 | * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise | ||
788 | */ | ||
789 | static enum GNUNET_GenericReturnValue | ||
790 | process_status (struct GNUNET_OS_Process *proc, | ||
791 | enum GNUNET_OS_ProcessStatusType *type, | ||
792 | unsigned long *code, | ||
793 | int options) | ||
794 | { | ||
795 | int status; | ||
796 | int ret; | ||
797 | |||
798 | GNUNET_assert (0 != proc); | ||
799 | ret = waitpid (proc->pid, | ||
800 | &status, | ||
801 | options); | ||
802 | if (ret < 0) | ||
803 | { | ||
804 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, | ||
805 | "waitpid"); | ||
806 | return GNUNET_SYSERR; | ||
807 | } | ||
808 | if (0 == ret) | ||
809 | { | ||
810 | *type = GNUNET_OS_PROCESS_RUNNING; | ||
811 | *code = 0; | ||
812 | return GNUNET_NO; | ||
813 | } | ||
814 | if (proc->pid != ret) | ||
815 | { | ||
816 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, | ||
817 | "waitpid"); | ||
818 | return GNUNET_SYSERR; | ||
819 | } | ||
820 | if (WIFEXITED (status)) | ||
821 | { | ||
822 | *type = GNUNET_OS_PROCESS_EXITED; | ||
823 | *code = WEXITSTATUS (status); | ||
824 | } | ||
825 | else if (WIFSIGNALED (status)) | ||
826 | { | ||
827 | *type = GNUNET_OS_PROCESS_SIGNALED; | ||
828 | *code = WTERMSIG (status); | ||
829 | } | ||
830 | else if (WIFSTOPPED (status)) | ||
831 | { | ||
832 | *type = GNUNET_OS_PROCESS_SIGNALED; | ||
833 | *code = WSTOPSIG (status); | ||
834 | } | ||
835 | #ifdef WIFCONTINUED | ||
836 | else if (WIFCONTINUED (status)) | ||
837 | { | ||
838 | *type = GNUNET_OS_PROCESS_RUNNING; | ||
839 | *code = 0; | ||
840 | } | ||
841 | #endif | ||
842 | else | ||
843 | { | ||
844 | *type = GNUNET_OS_PROCESS_UNKNOWN; | ||
845 | *code = 0; | ||
846 | } | ||
847 | |||
848 | return GNUNET_OK; | ||
849 | } | ||
850 | |||
851 | |||
852 | enum GNUNET_GenericReturnValue | ||
853 | GNUNET_OS_process_status (struct GNUNET_OS_Process *proc, | ||
854 | enum GNUNET_OS_ProcessStatusType *type, | ||
855 | unsigned long *code) | ||
856 | { | ||
857 | return process_status (proc, type, code, WNOHANG); | ||
858 | } | ||
859 | |||
860 | |||
861 | enum GNUNET_GenericReturnValue | ||
862 | GNUNET_OS_process_wait_status (struct GNUNET_OS_Process *proc, | ||
863 | enum GNUNET_OS_ProcessStatusType *type, | ||
864 | unsigned long *code) | ||
865 | { | ||
866 | return process_status (proc, type, code, 0); | ||
867 | } | ||
868 | |||
869 | |||
870 | enum GNUNET_GenericReturnValue | ||
871 | GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc) | ||
872 | { | ||
873 | pid_t pid = proc->pid; | ||
874 | pid_t ret; | ||
875 | |||
876 | while ((pid != (ret = waitpid (pid, NULL, 0))) && (EINTR == errno)) | ||
877 | ; | ||
878 | if (pid != ret) | ||
879 | { | ||
880 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, | ||
881 | "waitpid"); | ||
882 | return GNUNET_SYSERR; | ||
883 | } | ||
884 | return GNUNET_OK; | ||
885 | } | ||
886 | |||
887 | |||
888 | /** | ||
889 | * Handle to a command. | ||
890 | */ | ||
891 | struct GNUNET_OS_CommandHandle | ||
892 | { | ||
893 | /** | ||
894 | * Process handle. | ||
895 | */ | ||
896 | struct GNUNET_OS_Process *eip; | ||
897 | |||
898 | /** | ||
899 | * Handle to the output pipe. | ||
900 | */ | ||
901 | struct GNUNET_DISK_PipeHandle *opipe; | ||
902 | |||
903 | /** | ||
904 | * Read-end of output pipe. | ||
905 | */ | ||
906 | const struct GNUNET_DISK_FileHandle *r; | ||
907 | |||
908 | /** | ||
909 | * Function to call on each line of output. | ||
910 | */ | ||
911 | GNUNET_OS_LineProcessor proc; | ||
912 | |||
913 | /** | ||
914 | * Closure for @e proc. | ||
915 | */ | ||
916 | void *proc_cls; | ||
917 | |||
918 | /** | ||
919 | * Buffer for the output. | ||
920 | */ | ||
921 | char buf[1024]; | ||
922 | |||
923 | /** | ||
924 | * Task reading from pipe. | ||
925 | */ | ||
926 | struct GNUNET_SCHEDULER_Task *rtask; | ||
927 | |||
928 | /** | ||
929 | * When to time out. | ||
930 | */ | ||
931 | struct GNUNET_TIME_Absolute timeout; | ||
932 | |||
933 | /** | ||
934 | * Current read offset in buf. | ||
935 | */ | ||
936 | size_t off; | ||
937 | }; | ||
938 | |||
939 | |||
940 | void | ||
941 | GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd) | ||
942 | { | ||
943 | if (NULL != cmd->proc) | ||
944 | { | ||
945 | GNUNET_assert (NULL != cmd->rtask); | ||
946 | GNUNET_SCHEDULER_cancel (cmd->rtask); | ||
947 | } | ||
948 | (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL); | ||
949 | GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip)); | ||
950 | GNUNET_OS_process_destroy (cmd->eip); | ||
951 | GNUNET_DISK_pipe_close (cmd->opipe); | ||
952 | GNUNET_free (cmd); | ||
953 | } | ||
954 | |||
955 | |||
956 | /** | ||
957 | * Read from the process and call the line processor. | ||
958 | * | ||
959 | * @param cls the `struct GNUNET_OS_CommandHandle *` | ||
960 | */ | ||
961 | static void | ||
962 | cmd_read (void *cls) | ||
963 | { | ||
964 | struct GNUNET_OS_CommandHandle *cmd = cls; | ||
965 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
966 | GNUNET_OS_LineProcessor proc; | ||
967 | char *end; | ||
968 | ssize_t ret; | ||
969 | |||
970 | cmd->rtask = NULL; | ||
971 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
972 | if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r)) | ||
973 | { | ||
974 | /* timeout */ | ||
975 | proc = cmd->proc; | ||
976 | cmd->proc = NULL; | ||
977 | proc (cmd->proc_cls, NULL); | ||
978 | return; | ||
979 | } | ||
980 | ret = GNUNET_DISK_file_read (cmd->r, | ||
981 | &cmd->buf[cmd->off], | ||
982 | sizeof(cmd->buf) - cmd->off); | ||
983 | if (ret <= 0) | ||
984 | { | ||
985 | if ((cmd->off > 0) && (cmd->off < sizeof(cmd->buf))) | ||
986 | { | ||
987 | cmd->buf[cmd->off] = '\0'; | ||
988 | cmd->proc (cmd->proc_cls, cmd->buf); | ||
989 | } | ||
990 | proc = cmd->proc; | ||
991 | cmd->proc = NULL; | ||
992 | proc (cmd->proc_cls, NULL); | ||
993 | return; | ||
994 | } | ||
995 | end = memchr (&cmd->buf[cmd->off], '\n', ret); | ||
996 | cmd->off += ret; | ||
997 | while (NULL != end) | ||
998 | { | ||
999 | *end = '\0'; | ||
1000 | cmd->proc (cmd->proc_cls, cmd->buf); | ||
1001 | memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf)); | ||
1002 | cmd->off -= (end + 1 - cmd->buf); | ||
1003 | end = memchr (cmd->buf, '\n', cmd->off); | ||
1004 | } | ||
1005 | cmd->rtask = | ||
1006 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining ( | ||
1007 | cmd->timeout), | ||
1008 | cmd->r, | ||
1009 | &cmd_read, | ||
1010 | cmd); | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | struct GNUNET_OS_CommandHandle * | ||
1015 | GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, | ||
1016 | void *proc_cls, | ||
1017 | struct GNUNET_TIME_Relative timeout, | ||
1018 | const char *binary, | ||
1019 | ...) | ||
1020 | { | ||
1021 | struct GNUNET_OS_CommandHandle *cmd; | ||
1022 | struct GNUNET_OS_Process *eip; | ||
1023 | struct GNUNET_DISK_PipeHandle *opipe; | ||
1024 | va_list ap; | ||
1025 | |||
1026 | opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); | ||
1027 | if (NULL == opipe) | ||
1028 | return NULL; | ||
1029 | va_start (ap, binary); | ||
1030 | /* redirect stdout, don't inherit stderr/stdin */ | ||
1031 | eip = | ||
1032 | GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_NONE, | ||
1033 | NULL, | ||
1034 | opipe, | ||
1035 | NULL, | ||
1036 | binary, | ||
1037 | ap); | ||
1038 | va_end (ap); | ||
1039 | if (NULL == eip) | ||
1040 | { | ||
1041 | GNUNET_DISK_pipe_close (opipe); | ||
1042 | return NULL; | ||
1043 | } | ||
1044 | GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE); | ||
1045 | cmd = GNUNET_new (struct GNUNET_OS_CommandHandle); | ||
1046 | cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout); | ||
1047 | cmd->eip = eip; | ||
1048 | cmd->opipe = opipe; | ||
1049 | cmd->proc = proc; | ||
1050 | cmd->proc_cls = proc_cls; | ||
1051 | cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ); | ||
1052 | cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd); | ||
1053 | return cmd; | ||
1054 | } | ||
1055 | |||
1056 | |||
1057 | /* end of os_priority.c */ | ||