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