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