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.c1184
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
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
216int
217GNUNET_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 */
266pid_t
267GNUNET_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 */
279void
280GNUNET_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 */
296static void
297open_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 */
336static struct GNUNET_OS_Process *
337start_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 */
597struct GNUNET_OS_Process *
598GNUNET_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 */
626struct GNUNET_OS_Process *
627GNUNET_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 */
671struct GNUNET_OS_Process *
672GNUNET_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 */
707struct GNUNET_OS_Process *
708GNUNET_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 */
740struct GNUNET_OS_Process *
741GNUNET_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 */
871static enum GNUNET_GenericReturnValue
872process_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 */
943enum GNUNET_GenericReturnValue
944GNUNET_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 */
961enum GNUNET_GenericReturnValue
962GNUNET_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 */
980int
981GNUNET_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 */
1000struct 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 */
1056void
1057GNUNET_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 */
1077static void
1078cmd_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 */
1141struct GNUNET_OS_CommandHandle *
1142GNUNET_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 */