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