aboutsummaryrefslogtreecommitdiff
path: root/src/util/os_priority.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/os_priority.c')
-rw-r--r--src/util/os_priority.c1190
1 files changed, 0 insertions, 1190 deletions
diff --git a/src/util/os_priority.c b/src/util/os_priority.c
deleted file mode 100644
index 843b4734c..000000000
--- a/src/util/os_priority.c
+++ /dev/null
@@ -1,1190 +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
43struct 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 */
61static struct GNUNET_OS_Process current_process;
62
63/**
64 * Handle for the #parent_control_handler() Task.
65 */
66static struct GNUNET_SCHEDULER_Task *pch;
67
68/**
69 * Handle for the #shutdown_pch() Task.
70 */
71static 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 */
79static void
80shutdown_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 */
96static void
97parent_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 */
139void
140GNUNET_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 */
208struct GNUNET_OS_Process *
209GNUNET_OS_process_current ()
210{
211 current_process.pid = 0;
212 return &current_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 */
223int
224GNUNET_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 */
272pid_t
273GNUNET_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 */
285void
286GNUNET_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 */
302static void
303open_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 */
342static struct GNUNET_OS_Process *
343start_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 */
603struct GNUNET_OS_Process *
604GNUNET_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 */
632struct GNUNET_OS_Process *
633GNUNET_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 */
677struct GNUNET_OS_Process *
678GNUNET_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 */
713struct GNUNET_OS_Process *
714GNUNET_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 */
746struct GNUNET_OS_Process *
747GNUNET_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 */
877static enum GNUNET_GenericReturnValue
878process_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,
888 &status,
889 options);
890 if (ret < 0)
891 {
892 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
893 "waitpid");
894 return GNUNET_SYSERR;
895 }
896 if (0 == ret)
897 {
898 *type = GNUNET_OS_PROCESS_RUNNING;
899 *code = 0;
900 return GNUNET_NO;
901 }
902 if (proc->pid != ret)
903 {
904 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
905 "waitpid");
906 return GNUNET_SYSERR;
907 }
908 if (WIFEXITED (status))
909 {
910 *type = GNUNET_OS_PROCESS_EXITED;
911 *code = WEXITSTATUS (status);
912 }
913 else if (WIFSIGNALED (status))
914 {
915 *type = GNUNET_OS_PROCESS_SIGNALED;
916 *code = WTERMSIG (status);
917 }
918 else if (WIFSTOPPED (status))
919 {
920 *type = GNUNET_OS_PROCESS_SIGNALED;
921 *code = WSTOPSIG (status);
922 }
923#ifdef WIFCONTINUED
924 else if (WIFCONTINUED (status))
925 {
926 *type = GNUNET_OS_PROCESS_RUNNING;
927 *code = 0;
928 }
929#endif
930 else
931 {
932 *type = GNUNET_OS_PROCESS_UNKNOWN;
933 *code = 0;
934 }
935
936 return GNUNET_OK;
937}
938
939
940/**
941 * Retrieve the status of a process.
942 * Nonblocking version.
943 *
944 * @param proc process ID
945 * @param type status type
946 * @param code return code/signal number
947 * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
948 */
949enum GNUNET_GenericReturnValue
950GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
951 enum GNUNET_OS_ProcessStatusType *type,
952 unsigned long *code)
953{
954 return process_status (proc, type, code, WNOHANG);
955}
956
957
958/**
959 * Retrieve the status of a process, waiting on it if dead.
960 * Blocking version.
961 *
962 * @param proc pointer to process structure
963 * @param type status type
964 * @param code return code/signal number
965 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
966 */
967enum GNUNET_GenericReturnValue
968GNUNET_OS_process_wait_status (struct GNUNET_OS_Process *proc,
969 enum GNUNET_OS_ProcessStatusType *type,
970 unsigned long *code)
971{
972 return process_status (proc, type, code, 0);
973}
974
975
976/**
977 * Wait for a process to terminate. The return code is discarded.
978 * You must not use #GNUNET_OS_process_status() on the same process
979 * after calling this function! This function is blocking and should
980 * thus only be used if the child process is known to have terminated
981 * or to terminate very soon.
982 *
983 * @param proc pointer to process structure
984 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
985 */
986int
987GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
988{
989 pid_t pid = proc->pid;
990 pid_t ret;
991
992 while ((pid != (ret = waitpid (pid, NULL, 0))) && (EINTR == errno))
993 ;
994 if (pid != ret)
995 {
996 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
997 return GNUNET_SYSERR;
998 }
999 return GNUNET_OK;
1000}
1001
1002
1003/**
1004 * Handle to a command.
1005 */
1006struct GNUNET_OS_CommandHandle
1007{
1008 /**
1009 * Process handle.
1010 */
1011 struct GNUNET_OS_Process *eip;
1012
1013 /**
1014 * Handle to the output pipe.
1015 */
1016 struct GNUNET_DISK_PipeHandle *opipe;
1017
1018 /**
1019 * Read-end of output pipe.
1020 */
1021 const struct GNUNET_DISK_FileHandle *r;
1022
1023 /**
1024 * Function to call on each line of output.
1025 */
1026 GNUNET_OS_LineProcessor proc;
1027
1028 /**
1029 * Closure for @e proc.
1030 */
1031 void *proc_cls;
1032
1033 /**
1034 * Buffer for the output.
1035 */
1036 char buf[1024];
1037
1038 /**
1039 * Task reading from pipe.
1040 */
1041 struct GNUNET_SCHEDULER_Task *rtask;
1042
1043 /**
1044 * When to time out.
1045 */
1046 struct GNUNET_TIME_Absolute timeout;
1047
1048 /**
1049 * Current read offset in buf.
1050 */
1051 size_t off;
1052};
1053
1054
1055/**
1056 * Stop/kill a command. Must ONLY be called either from
1057 * the callback after 'NULL' was passed for 'line' *OR*
1058 * from an independent task (not within the line processor).
1059 *
1060 * @param cmd handle to the process
1061 */
1062void
1063GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1064{
1065 if (NULL != cmd->proc)
1066 {
1067 GNUNET_assert (NULL != cmd->rtask);
1068 GNUNET_SCHEDULER_cancel (cmd->rtask);
1069 }
1070 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1071 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1072 GNUNET_OS_process_destroy (cmd->eip);
1073 GNUNET_DISK_pipe_close (cmd->opipe);
1074 GNUNET_free (cmd);
1075}
1076
1077
1078/**
1079 * Read from the process and call the line processor.
1080 *
1081 * @param cls the `struct GNUNET_OS_CommandHandle *`
1082 */
1083static void
1084cmd_read (void *cls)
1085{
1086 struct GNUNET_OS_CommandHandle *cmd = cls;
1087 const struct GNUNET_SCHEDULER_TaskContext *tc;
1088 GNUNET_OS_LineProcessor proc;
1089 char *end;
1090 ssize_t ret;
1091
1092 cmd->rtask = NULL;
1093 tc = GNUNET_SCHEDULER_get_task_context ();
1094 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1095 {
1096 /* timeout */
1097 proc = cmd->proc;
1098 cmd->proc = NULL;
1099 proc (cmd->proc_cls, NULL);
1100 return;
1101 }
1102 ret = GNUNET_DISK_file_read (cmd->r,
1103 &cmd->buf[cmd->off],
1104 sizeof(cmd->buf) - cmd->off);
1105 if (ret <= 0)
1106 {
1107 if ((cmd->off > 0) && (cmd->off < sizeof(cmd->buf)))
1108 {
1109 cmd->buf[cmd->off] = '\0';
1110 cmd->proc (cmd->proc_cls, cmd->buf);
1111 }
1112 proc = cmd->proc;
1113 cmd->proc = NULL;
1114 proc (cmd->proc_cls, NULL);
1115 return;
1116 }
1117 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1118 cmd->off += ret;
1119 while (NULL != end)
1120 {
1121 *end = '\0';
1122 cmd->proc (cmd->proc_cls, cmd->buf);
1123 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1124 cmd->off -= (end + 1 - cmd->buf);
1125 end = memchr (cmd->buf, '\n', cmd->off);
1126 }
1127 cmd->rtask =
1128 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (
1129 cmd->timeout),
1130 cmd->r,
1131 &cmd_read,
1132 cmd);
1133}
1134
1135
1136/**
1137 * Run the given command line and call the given function
1138 * for each line of the output.
1139 *
1140 * @param proc function to call for each line of the output
1141 * @param proc_cls closure for @a proc
1142 * @param timeout when to time out
1143 * @param binary command to run
1144 * @param ... arguments to command
1145 * @return NULL on error
1146 */
1147struct GNUNET_OS_CommandHandle *
1148GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1149 void *proc_cls,
1150 struct GNUNET_TIME_Relative timeout,
1151 const char *binary,
1152 ...)
1153{
1154 struct GNUNET_OS_CommandHandle *cmd;
1155 struct GNUNET_OS_Process *eip;
1156 struct GNUNET_DISK_PipeHandle *opipe;
1157 va_list ap;
1158
1159 opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
1160 if (NULL == opipe)
1161 return NULL;
1162 va_start (ap, binary);
1163 /* redirect stdout, don't inherit stderr/stdin */
1164 eip =
1165 GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_NONE,
1166 NULL,
1167 opipe,
1168 NULL,
1169 binary,
1170 ap);
1171 va_end (ap);
1172 if (NULL == eip)
1173 {
1174 GNUNET_DISK_pipe_close (opipe);
1175 return NULL;
1176 }
1177 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1178 cmd = GNUNET_new (struct GNUNET_OS_CommandHandle);
1179 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1180 cmd->eip = eip;
1181 cmd->opipe = opipe;
1182 cmd->proc = proc;
1183 cmd->proc_cls = proc_cls;
1184 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1185 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1186 return cmd;
1187}
1188
1189
1190/* end of os_priority.c */