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