GNUnet 0.28.1-dev.5-1-gae1c02d74
 
Loading...
Searching...
No Matches
os_process.c File Reference

process management More...

#include "platform.h"
#include "gnunet_util_lib.h"
#include "disk.h"
Include dependency graph for os_process.c:

Go to the source code of this file.

Data Structures

struct  ProcessFileMapEntry
 Mapping of file descriptors of the current process to (desired) file descriptors of the child process. More...
 
struct  EnviEntry
 Key-value pairs for setenv(). More...
 
struct  GNUNET_Process
 

Macros

#define LOG(kind, ...)   GNUNET_log_from (kind, "util-os-process", __VA_ARGS__)
 
#define LOG_STRERROR(kind, syscall)    GNUNET_log_from_strerror (kind, "util-os-process", syscall)
 
#define LOG_STRERROR_FILE(kind, syscall, filename)
 
#define GNUNET_OS_CONTROL_PIPE   "GNUNET_OS_CONTROL_PIPE"
 

Functions

static void shutdown_pch (void *cls)
 This handler is called on shutdown to remove the pch.
 
static void parent_control_handler (void *cls)
 This handler is called when there are control data to be read on the pipe.
 
void GNUNET_process_install_parent_control_handler ()
 Connects this process to its parent via pipe; essentially, the parent control handler will read signal numbers from the GNUNET_OS_CONTROL_PIPE (as given in an environment variable) and raise those signals.
 
struct GNUNET_ProcessGNUNET_process_current ()
 Get process structure for current process.
 
enum GNUNET_GenericReturnValue GNUNET_process_kill (struct GNUNET_Process *proc, int sig)
 Sends a signal to the process.
 
pid_t GNUNET_process_get_pid (const struct GNUNET_Process *proc)
 Get the pid of the process in question.
 
void GNUNET_process_destroy (struct GNUNET_Process *proc)
 Cleans up process structure contents (OS-dependent) and deallocates it.
 
static int safe_dup2 (int oldfd, int newfd)
 Call dup2() in a way that races and interrupts are safely handled.
 
static enum GNUNET_GenericReturnValue open_dev_null (int target_fd, int flags)
 Open '/dev/null' and make the result the given file descriptor.
 
struct GNUNET_ProcessGNUNET_process_create (enum GNUNET_OS_InheritStdioFlags std_inheritance)
 Create a process handle.
 
static enum GNUNET_GenericReturnValue clear_cloexec (int fd)
 Clear the close-on-exec flag of fd.
 
static enum GNUNET_GenericReturnValue map_std (struct GNUNET_Process *proc, int fd, int flags)
 Map the standard input/output/error file descriptor fd based on the mapping given in proc.
 
static enum GNUNET_GenericReturnValue process_start (struct GNUNET_Process *proc)
 
enum GNUNET_GenericReturnValue GNUNET_process_run_command_argv (struct GNUNET_Process *p, const char *filename, const char **argv)
 Set the command and start a process.
 
enum GNUNET_GenericReturnValue GNUNET_process_run_command_ap (struct GNUNET_Process *p, const char *filename, va_list va)
 Set the command and start a process.
 
enum GNUNET_GenericReturnValue GNUNET_process_run_command_va (struct GNUNET_Process *p, const char *filename,...)
 Set the command and start a process.
 
enum GNUNET_GenericReturnValue GNUNET_process_run_command (struct GNUNET_Process *p, const char *command)
 Set the command and start a process.
 
enum GNUNET_GenericReturnValue GNUNET_process_set_options_ (struct GNUNET_Process *proc, unsigned int num_options, const struct GNUNET_ProcessOptionValue options[])
 Set the requested options for the process.
 
enum GNUNET_GenericReturnValue GNUNET_process_wait (struct GNUNET_Process *proc, bool blocking, enum GNUNET_OS_ProcessStatusType *type, unsigned long *code)
 Wait for a process to terminate.
 

Variables

static struct GNUNET_Process current_process
 Handle for 'this' process.
 
static struct GNUNET_SCHEDULER_Taskpch
 Handle for the parent_control_handler() Task.
 
static struct GNUNET_SCHEDULER_Taskspch
 Handle for the shutdown_pch() Task.
 

Detailed Description

process management

Author
Nils Durner
Christian Grothoff

Definition in file os_process.c.

Macro Definition Documentation

◆ LOG

#define LOG (   kind,
  ... 
)    GNUNET_log_from (kind, "util-os-process", __VA_ARGS__)

Definition at line 31 of file os_process.c.

◆ LOG_STRERROR

#define LOG_STRERROR (   kind,
  syscall 
)     GNUNET_log_from_strerror (kind, "util-os-process", syscall)

Definition at line 33 of file os_process.c.

47{
51 int target_fd;
52
56 int parent_fd;
57
62 bool systemd_listen_socket;
63
67 bool owned;
68};
69
70
74struct EnviEntry
75{
79 char *key;
80
84 char *value;
85};
86
87
88struct GNUNET_Process
89{
93 pid_t pid;
94
100
106
110 unsigned int map_size;
111
115 struct ProcessFileMapEntry *map;
116
120 unsigned int envs_size;
121
125 struct EnviEntry *envs;
126
130 char *filename;
131
135 char **argv;
136
141
145 unsigned long exit_code;
146};
147
148
152static struct GNUNET_Process current_process;
153
157static struct GNUNET_SCHEDULER_Task *pch;
158
162static struct GNUNET_SCHEDULER_Task *spch;
163
164
170static void
171shutdown_pch (void *cls)
172{
173 struct GNUNET_DISK_FileHandle *control_pipe = cls;
174
176 pch = NULL;
177 GNUNET_DISK_file_close (control_pipe);
178 control_pipe = NULL;
179}
180
181
187static void
188parent_control_handler (void *cls)
189{
190 struct GNUNET_DISK_FileHandle *control_pipe = cls;
191 char sig;
192 ssize_t ret;
193
194 pch = NULL;
195 ret = GNUNET_DISK_file_read (control_pipe,
196 &sig,
197 sizeof(sig));
198 if (sizeof(sig) != ret)
199 {
200 if (-1 == ret)
202 "GNUNET_DISK_file_read");
204 "Closing control pipe\n");
205 GNUNET_DISK_file_close (control_pipe);
207 spch = NULL;
208 return;
209 }
211 "Got control code %d from parent via pipe\n",
212 sig);
214 control_pipe,
216 control_pipe);
217 GNUNET_SIGNAL_raise ((int) sig);
218}
219
220
221void
223{
224 const char *env_buf;
225 char *env_buf_end;
226 struct GNUNET_DISK_FileHandle *control_pipe;
227 uint64_t pipe_fd;
228
229 if (NULL != pch)
230 {
231 /* already done, we've been called twice... */
232 GNUNET_break (0);
233 return;
234 }
235 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
236 if ( (NULL == env_buf) ||
237 (strlen (env_buf) <= 0) )
238 {
240 "Not installing a handler because $%s is empty\n",
242 setenv (GNUNET_OS_CONTROL_PIPE, "", 1);
243 return;
244 }
245 errno = 0;
246 pipe_fd = strtoull (env_buf,
247 &env_buf_end,
248 16);
249 if ( (0 != errno) ||
250 (env_buf == env_buf_end) )
251 {
253 "strtoull",
254 env_buf);
256 "",
257 1);
258 return;
259 }
260 if (pipe_fd >= FD_SETSIZE)
261 {
263 "GNUNET_OS_CONTROL_PIPE `%s' contains garbage?\n",
264 env_buf);
266 "",
267 1);
268 return;
269 }
270
271 control_pipe = GNUNET_DISK_get_handle_from_int_fd ((int) pipe_fd);
272 if (NULL == control_pipe)
273 {
275 "open",
276 env_buf);
278 "",
279 1);
280 return;
281 }
283 "Adding parent control handler pipe `%s' to the scheduler\n",
284 env_buf);
286 control_pipe,
288 control_pipe);
290 control_pipe);
292 "",
293 1);
294}
295
296
297struct GNUNET_Process *
299{
301 return &current_process;
302}
303
304
307 int sig)
308{
309 if (NULL != proc->control_pipe)
310 {
311 char csig = (char) sig;
312 ssize_t iret;
313
315 "Sending signal %d to pid: %u via pipe\n",
316 sig,
317 proc->pid);
319 &csig,
320 sizeof(csig));
321 if (sizeof(csig) == iret)
322 return GNUNET_OK;
323 }
324 /* pipe failed or non-existent, try other methods */
325 if (-1 == proc->pid)
326 {
328 "Refusing to send signal %d process `%s': not running\n",
329 sig,
330 proc->filename);
331 return GNUNET_NO; /* -1 means process is not running, refuse... */
332 }
334 "Sending signal %d to pid: %u via system call\n",
335 sig,
336 proc->pid);
337 {
338 int ret;
339
340 ret = kill (proc->pid,
341 sig);
342 if (0 != ret)
343 {
345 "kill");
346 }
347 return (0 == ret)
348 ? GNUNET_OK
350 }
351}
352
353
354pid_t
355GNUNET_process_get_pid (const struct GNUNET_Process *proc)
356{
357 return proc->pid;
358}
359
360
361void
363{
364 if (NULL != proc->control_pipe)
366 GNUNET_free (proc->filename);
367 for (unsigned int i = 0; i < proc->envs_size; i++)
368 {
369 GNUNET_free (proc->envs[i].key);
370 GNUNET_free (proc->envs[i].value);
371 }
372 GNUNET_free (proc->envs);
373 for (unsigned int i = 0; i < proc->map_size; i++)
374 {
375 struct ProcessFileMapEntry *me = &proc->map[i];
376 int pfd = me->parent_fd;
377
378 if (-1 == pfd)
379 continue;
380 if (! me->owned)
381 continue;
382 GNUNET_break (0 == close (pfd));
383 }
384 GNUNET_free (proc->map);
385 if (NULL != proc->argv)
386 {
387 for (unsigned int i = 0; NULL != proc->argv[i]; i++)
388 GNUNET_free (proc->argv[i]);
389 GNUNET_free (proc->argv);
390 }
391 GNUNET_free (proc);
392}
393
394
402static int
403safe_dup2 (int oldfd,
404 int newfd)
405{
406 int ret;
407
408 while (1)
409 {
410 ret = dup2 (oldfd,
411 newfd);
412 if (-1 != ret)
413 break;
414 if ( (EBADF == errno) ||
415 (EINVAL == errno) ||
416 (EMFILE == errno) )
417 break;
418 /* should be EBUSY/EINTR, so try again */
420 "dup2");
421 }
422 return ret;
423}
424
425
435 int flags)
436{
437 int fd;
438
439 fd = open ("/dev/null",
440 flags);
441 if (-1 == fd)
442 {
444 "open",
445 "/dev/null");
446 return GNUNET_SYSERR;
447 }
448 if (fd == target_fd)
449 return GNUNET_OK;
450 if (-1 == dup2 (fd,
451 target_fd))
452 {
454 "dup2");
455 GNUNET_break (0 == close (fd));
456 return GNUNET_SYSERR;
457 }
458 GNUNET_break (0 == close (fd));
459 return GNUNET_OK;
460}
461
462
463struct GNUNET_Process *
465{
466 struct GNUNET_Process *p;
467
468 p = GNUNET_new (struct GNUNET_Process);
469 p->pid = -1;
471 return p;
472}
473
474
482clear_cloexec (int fd)
483{
484 int flags;
485
486 flags = fcntl (fd,
487 F_GETFD);
488 if (flags == -1)
489 {
491 "fcntl(F_GETFD)");
492 return GNUNET_SYSERR;
493 }
494
495 flags &= ~FD_CLOEXEC;
496
497 if (-1 ==
498 fcntl (fd,
499 F_SETFD,
500 flags))
501 {
503 "fcntl(F_SETFD)");
504 return GNUNET_SYSERR;
505 }
506 return GNUNET_OK;
507}
508
509
521map_std (struct GNUNET_Process *proc,
522 int fd,
523 int flags)
524{
525 for (unsigned int i=0; i<proc->map_size; i++)
526 {
527 struct ProcessFileMapEntry *me = &proc->map[i];
528
529 if (fd == me->target_fd)
530 {
531 if (fd !=
532 safe_dup2 (me->parent_fd,
533 fd))
534 return GNUNET_SYSERR;
535 if (me->owned)
536 GNUNET_break (0 == close (me->parent_fd));
537 me->parent_fd = -1;
538 return GNUNET_OK;
539 }
540 }
541 return open_dev_null (fd,
542 flags);
543}
544
545
547process_start (struct GNUNET_Process *proc)
548{
549 pid_t ret;
550 int childpipe_read_fd;
551
552 if (-1 != proc->pid)
553 {
554 GNUNET_break (0);
555 return GNUNET_SYSERR;
556 }
557 if (NULL == proc->filename)
558 {
559 GNUNET_break (0);
560 return GNUNET_SYSERR;
561 }
562
564 {
565 struct GNUNET_DISK_PipeHandle *childpipe;
566 struct GNUNET_DISK_FileHandle *childpipe_read;
567 int dup_childpipe_read_fd = -1;
568
570 if (NULL == childpipe)
571 {
572 GNUNET_break (0);
573 return GNUNET_SYSERR;
574 }
575 childpipe_read =
578 proc->control_pipe =
581 GNUNET_DISK_pipe_close (childpipe);
582 if ( (NULL == childpipe_read) ||
583 (NULL == proc->control_pipe) ||
584 (GNUNET_OK !=
585 GNUNET_DISK_internal_file_handle_ (childpipe_read,
586 &childpipe_read_fd)) ||
587 (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd))) )
588 {
589 GNUNET_break (0);
590 if (NULL != childpipe_read)
591 GNUNET_DISK_file_close (childpipe_read);
592 if (NULL != proc->control_pipe)
593 {
595 proc->control_pipe = NULL;
596 }
597 return GNUNET_SYSERR;
598 }
599 childpipe_read_fd = dup_childpipe_read_fd;
600 GNUNET_DISK_file_close (childpipe_read);
601 }
602 else
603 {
604 childpipe_read_fd = -1;
605 }
606
607 {
608 /* Make sure none of the parent_fds conflict with the target_fds in the client process */
609 unsigned int max_fd = 3; /* never use stdin/stdout/stderr */
610 unsigned int pos;
611
612 for (unsigned int i=0; i<proc->map_size; i++)
613 {
614 struct ProcessFileMapEntry *me = &proc->map[i];
615
616 max_fd = GNUNET_MAX (max_fd,
617 me->target_fd);
618 }
619 pos = max_fd + 1;
620 for (unsigned int i=0; i<proc->map_size; i++)
621 {
622 struct ProcessFileMapEntry *me = &proc->map[i];
623
624 if (me->parent_fd < pos)
625 {
626 if (me->target_fd == me->parent_fd)
627 {
628 /* same FD, so no conflict, but make sure FD is not closed
629 on exec() */
630 clear_cloexec (me->parent_fd);
631 }
632 else
633 {
634 int dst;
635
636 /* search for FD that is not yet open */
637 while (-1 !=
638 fcntl (pos,
639 F_GETFD))
640 pos++;
641 while (1)
642 {
643 dst = dup2 (me->parent_fd,
644 pos);
645 if (-1 != dst)
646 break;
647 if ( (EBADF == errno) ||
648 (EINVAL == errno) ||
649 (EMFILE == errno) )
650 {
651 GNUNET_break (0);
652 return GNUNET_SYSERR;
653 }
654 /* leaves interrupt/busy, try again */
655 }
656 if (me->owned)
657 GNUNET_break (0 == close (me->parent_fd));
658 me->parent_fd = dst;
659 me->owned = true;
660 }
661 }
662 }
663 }
664
665 ret = fork ();
666 if (-1 == ret)
667 {
668 int eno = errno;
669
671 "fork");
672 if (NULL != proc->control_pipe)
673 {
675 proc->control_pipe = NULL;
676 }
677 if (0 <= childpipe_read_fd)
678 GNUNET_break (0 == close (childpipe_read_fd));
679 errno = eno;
680 return GNUNET_SYSERR;
681 }
682 if (0 != ret)
683 {
684 proc->pid = ret;
685 if (0 <= childpipe_read_fd)
686 GNUNET_break (0 == close (childpipe_read_fd));
687 for (unsigned int i=0; i<proc->map_size; i++)
688 {
689 struct ProcessFileMapEntry *me = &proc->map[i];
690
691 if (! me->owned)
692 continue;
693 if (-1 != me->parent_fd)
694 {
695 GNUNET_assert (0 ==
696 close (me->parent_fd));
697 me->parent_fd = -1;
698 }
699 }
700 return GNUNET_OK;
701 }
702
703 /* in child process! */
704
705 /* deploy control pipe */
706 if (0 <= childpipe_read_fd)
707 {
708 char fdbuf[100];
709
711 snprintf (fdbuf,
712 sizeof (fdbuf),
713 "%x",
714 childpipe_read_fd);
715 GNUNET_assert (0 ==
717 fdbuf,
718 1));
719 }
720 else
721 {
722 GNUNET_assert (0 ==
723 unsetenv (GNUNET_OS_CONTROL_PIPE));
724 }
725
726 /* map stdin/stdout/stderr */
727 if (0 == (proc->std_inheritance & GNUNET_OS_INHERIT_STD_IN))
728 {
730 map_std (proc,
731 STDIN_FILENO,
732 O_RDONLY));
733 }
735 {
737 map_std (proc,
738 STDOUT_FILENO,
739 O_WRONLY));
740 }
742 {
744 map_std (proc,
745 STDERR_FILENO,
746 O_WRONLY));
747 }
748
749 /* map all other file descriptors */
750 {
751 char *fdnames = GNUNET_strdup ("");
752 unsigned int total_lfds = 0;
753
754 for (unsigned int i=0; i<proc->map_size; i++)
755 {
756 struct ProcessFileMapEntry *me = &proc->map[i];
757
758 if (-1 == me->parent_fd)
759 continue; /* already taken care of */
760 if (-1 == me->target_fd)
761 {
762 me->target_fd = dup (me->parent_fd);
763 GNUNET_assert (-1 != me->target_fd);
764 if (me->owned)
765 GNUNET_assert (0 == close (me->parent_fd));
766 }
767 else if (me->parent_fd != me->target_fd)
768 {
769 GNUNET_assert (me->target_fd ==
770 safe_dup2 (me->parent_fd,
771 me->target_fd));
772 if (me->owned)
773 GNUNET_assert (0 == close (me->parent_fd));
774 }
775 else
776 {
777 GNUNET_assert (! me->systemd_listen_socket);
778 GNUNET_assert (me->owned);
780 clear_cloexec (me->target_fd));
781 }
782 me->parent_fd = -1;
783 if (me->systemd_listen_socket)
784 {
785 char *tmp;
786
787 GNUNET_asprintf (&tmp,
788 "%s:%d",
789 fdnames,
790 me->target_fd);
791 GNUNET_free (fdnames);
792 fdnames = tmp;
793 total_lfds++;
794 }
795 }
796 if (0 != total_lfds)
797 {
798 char fds[16];
799
800 GNUNET_snprintf (fds,
801 sizeof(fds),
802 "%u",
803 total_lfds);
804 GNUNET_assert (0 ==
805 setenv ("LISTEN_FDS",
806 fds,
807 1));
808 GNUNET_assert (0 ==
809 setenv ("LISTEN_FDNAMES",
810 fdnames + 1, /* skip leading ':' */
811 1));
812 }
813 GNUNET_free (fdnames);
814 }
815
816 /* setup environment */
817 for (unsigned int i=0; i<proc->envs_size; i++)
818 {
819 struct EnviEntry *ee = &proc->envs[i];
820
821 if (NULL == ee->value)
822 GNUNET_assert (0 ==
823 unsetenv (ee->key));
824 else
825 GNUNET_assert (0 ==
826 setenv (ee->key,
827 ee->value,
828 1));
829 }
830
831 /* finally execute */
832 execvp (proc->filename,
833 proc->argv);
835 "execvp",
836 proc->filename);
837 _exit (1);
838}
839
840
843 struct GNUNET_Process *p,
844 const char *filename,
845 const char **argv)
846{
847 int argc;
848
849 if (GNUNET_SYSERR ==
851 GNUNET_NO,
852 NULL))
853 return GNUNET_SYSERR; /* not executable */
854 GNUNET_assert (NULL == p->argv);
856 argc = 0;
857 while (NULL != argv[argc])
858 argc++;
859 p->argv = GNUNET_new_array (argc + 1,
860 char *);
861 for (argc = 0; NULL != argv[argc]; argc++)
862 p->argv[argc] = GNUNET_strdup (argv[argc]);
863 return process_start (p);
864}
865
866
869 struct GNUNET_Process *p,
870 const char *filename,
871 va_list va)
872{
873 va_list ap;
874 const char *av;
875 int argc;
876
877 if (GNUNET_SYSERR ==
879 GNUNET_NO,
880 NULL))
881 return GNUNET_SYSERR; /* not executable */
882 GNUNET_assert (NULL == p->argv);
884 argc = 0;
885 va_copy (ap,
886 va);
887 while (NULL != va_arg (ap,
888 const char *))
889 argc++;
890 va_end (ap);
891 p->argv = GNUNET_new_array (argc + 1,
892 char *);
893 argc = 0;
894 va_copy (ap,
895 va);
896 while (NULL != (av = va_arg (ap,
897 const char *)))
898 p->argv[argc++] = GNUNET_strdup (av);
899 va_end (ap);
900 return process_start (p);
901}
902
903
906 const char *filename,
907 ...)
908{
910 va_list ap;
911
912 va_start (ap,
913 filename);
915 filename,
916 ap);
917 va_end (ap);
918 return ret;
919}
920
921
924 const char *command)
925{
926 char *cmd = GNUNET_strdup (command);
927 size_t len = strlen (command);
928 size_t cnt = 1;
929 size_t start = 0;
930 bool quote_on = false;
931 size_t i;
932 size_t skip = 0;
933
934 GNUNET_assert (NULL == p->argv);
935 for (i=0; i<len; i++)
936 {
937 char c = cmd[i];
938
939 switch (c)
940 {
941 case '"':
942 quote_on = ! quote_on;
943 break;
944 case '\\':
945 i++;
946 break;
947 case ' ':
948 if (! quote_on)
949 cnt++;
950 while (' ' == cmd[i + 1])
951 i++;
952 break;
953 }
954 }
955
956 p->argv = GNUNET_new_array (cnt + 1,
957 char *);
958 cnt = 0;
959 for (i=0; i<len; i++)
960 {
961 char c = cmd[i];
962
963 switch (c)
964 {
965 case '"':
966 quote_on = ! quote_on;
967 skip++;
968 break;
969 case ' ':
970 if ( (! quote_on) &&
971 (i != start) )
972 {
973 p->argv[cnt] = GNUNET_strndup (&cmd[start],
974 i - start - skip);
975 cnt++;
976 skip = 0;
977 }
978 while (' ' == cmd[i + 1])
979 i++;
980 if (! quote_on)
981 start = i + 1;
982 break;
983 case '\\':
984 i++;
985 skip++;
986 cmd[i - skip] = cmd[i];
987 break;
988 default:
989 cmd[i - skip] = c;
990 break;
991 }
992 }
993 if (i != start)
994 p->argv[cnt] = GNUNET_strndup (&cmd[start],
995 i - start);
996 if (quote_on)
997 {
999 "Cmd `%s' has imbalanced quotes\n",
1000 cmd);
1001 }
1002 GNUNET_free (cmd);
1003 if (NULL == p->argv[0])
1004 {
1006 "Empty command specified, cannot execute\n");
1007 return GNUNET_SYSERR;
1008 }
1009 if (GNUNET_SYSERR ==
1011 GNUNET_NO,
1012 NULL))
1013 {
1015 "Specified binary `%s' is not executable\n",
1016 p->argv[0]);
1017 return GNUNET_SYSERR;
1018 }
1019 p->filename = GNUNET_strdup (p->argv[0]);
1020 return process_start (p);
1021}
1022
1023
1026 struct GNUNET_Process *proc,
1027 unsigned int num_options,
1028 const struct GNUNET_ProcessOptionValue options[])
1029{
1030 for (unsigned int i=0; i<num_options; i++)
1031 {
1032 const struct GNUNET_ProcessOptionValue *ov = &options[i];
1033
1034 switch (ov->option)
1035 {
1037 return GNUNET_OK;
1039 {
1040 struct EnviEntry ee = {
1042 };
1043
1044 if (NULL != ov->details.set_environment.value)
1047 proc->envs_size,
1048 ee);
1049 }
1050 continue;
1052 {
1053 struct ProcessFileMapEntry pme = {
1055 .parent_fd = ov->details.inherit_fd.parent_fd,
1056 .owned = true
1057 };
1058
1059 GNUNET_array_append (proc->map,
1060 proc->map_size,
1061 pme);
1062 }
1063 continue;
1065 {
1066 struct ProcessFileMapEntry pme = {
1067 .target_fd = -1, /* any */
1068 .parent_fd = ov->details.inherit_lsock,
1069 .systemd_listen_socket = true,
1070 .owned = false
1071 };
1072
1073 GNUNET_array_append (proc->map,
1074 proc->map_size,
1075 pme);
1076 }
1077 continue;
1078 }
1079 GNUNET_break (0);
1080 return GNUNET_SYSERR;
1081 }
1082 return GNUNET_OK;
1083}
1084
1085
1088 bool blocking,
1090 unsigned long *code)
1091{
1092 pid_t ret;
1093 int status;
1094
1095 if (-1 == proc->pid)
1096 {
1097 if (NULL != type)
1098 *type = proc->exit_type;
1099 if (NULL != code)
1100 *code = proc->exit_code;
1101 return GNUNET_OK;
1102 }
1103 while (proc->pid !=
1104 (ret = waitpid (proc->pid,
1105 &status,
1106 blocking ? 0 : WNOHANG)))
1107 {
1108 if ( (! blocking) &&
1109 (EINTR == errno) )
1110 {
1111 ret = 0;
1112 break;
1113 }
1114 if (EINTR != errno)
1115 break;
1116 }
1117 if (0 == ret)
1118 {
1119 if (NULL != type)
1121 if (NULL != code)
1122 *code = 0;
1123 return GNUNET_NO;
1124 }
1125#ifdef WIFCONTINUED
1126 if ( (proc->pid == ret) &&
1127 (WIFCONTINUED (status)) )
1128 {
1129 if (NULL != type)
1131 if (NULL != code)
1132 *code = 0;
1133 return GNUNET_NO;
1134 }
1135#endif
1136 if (proc->pid != ret)
1137 {
1139 "waitpid");
1140 return GNUNET_SYSERR;
1141 }
1142 /* process did exit! */
1143 proc->pid = -1;
1144 if (WIFEXITED (status))
1145 {
1147 proc->exit_code = WEXITSTATUS (status);
1148 }
1149 else if (WIFSIGNALED (status))
1150 {
1152 proc->exit_code = WTERMSIG (status);
1153 }
1154 else if (WIFSTOPPED (status))
1155 {
1157 proc->exit_code = WSTOPSIG (status);
1158 }
1159 else
1160 {
1162 proc->exit_code = 0;
1163 }
1164 if (NULL != type)
1165 *type = proc->exit_type;
1166 if (NULL != code)
1167 *code = proc->exit_code;
1168 return GNUNET_OK;
1169}
1170
1171
1172/* end of os_process.c */
struct GNUNET_GETOPT_CommandLineOption options[]
Definition 002.c:5
enum GNUNET_GenericReturnValue GNUNET_DISK_internal_file_handle_(const struct GNUNET_DISK_FileHandle *fh, int *dst)
Retrieve OS file handle.
Definition disk.c:1720
char * getenv()
static int start
Set if we are to start default services (including ARM).
Definition gnunet-arm.c:38
static int ret
Final status code.
Definition gnunet-arm.c:93
static GNUNET_NETWORK_STRUCT_END struct GNUNET_PeerIdentity me
Our own peer identity.
static char * filename
static uint32_t type
Type string converted to DNS type value.
static int status
The program status; 0 for success.
Definition gnunet-nse.c:39
static struct GNUNET_Process * p
Helper process we started.
Definition gnunet-uri.c:38
struct GNUNET_DISK_FileHandle * GNUNET_DISK_pipe_detach_end(struct GNUNET_DISK_PipeHandle *p, enum GNUNET_DISK_PipeEnd end)
Detaches one of the ends from the pipe.
Definition disk.c:1644
ssize_t GNUNET_DISK_file_write(const struct GNUNET_DISK_FileHandle *h, const void *buffer, size_t n)
Write a buffer to a file.
Definition disk.c:745
struct GNUNET_DISK_PipeHandle * GNUNET_DISK_pipe(enum GNUNET_DISK_PipeFlags pf)
Creates an interprocess channel.
Definition disk.c:1524
enum GNUNET_GenericReturnValue GNUNET_DISK_pipe_close(struct GNUNET_DISK_PipeHandle *p)
Closes an interprocess channel.
Definition disk.c:1671
enum GNUNET_GenericReturnValue GNUNET_DISK_file_close(struct GNUNET_DISK_FileHandle *h)
Close an open file.
Definition disk.c:1386
struct GNUNET_DISK_FileHandle * GNUNET_DISK_get_handle_from_int_fd(int fno)
Get a handle from a native integer FD.
Definition disk.c:1410
ssize_t GNUNET_DISK_file_read(const struct GNUNET_DISK_FileHandle *h, void *result, size_t len)
Read the contents of a binary file into a buffer.
Definition disk.c:704
@ GNUNET_DISK_PF_NONE
No special options, use non-blocking read/write operations.
@ GNUNET_DISK_PIPE_END_WRITE
The writing-end of a pipe.
@ GNUNET_DISK_PIPE_END_READ
The reading-end of a pipe.
#define GNUNET_log(kind,...)
#define GNUNET_MAX(a, b)
GNUNET_GenericReturnValue
Named constants for return values.
@ GNUNET_OK
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
#define GNUNET_log_strerror_file(level, cmd, filename)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
#define GNUNET_strndup(a, length)
Wrapper around GNUNET_xstrndup_.
int GNUNET_snprintf(char *buf, size_t size, const char *format,...) __attribute__((format(printf
Like snprintf, just aborts if the buffer is of insufficient size.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
#define GNUNET_array_append(arr, len, element)
Append an element to an array (growing the array by one).
#define GNUNET_free(ptr)
Wrapper around free.
GNUNET_OS_InheritStdioFlags
Flags that determine which of the standard streams should be inherited by the child process.
void GNUNET_process_install_parent_control_handler()
Connects this process to its parent via pipe; essentially, the parent control handler will read signa...
Definition os_process.c:223
enum GNUNET_GenericReturnValue GNUNET_process_run_command_va(struct GNUNET_Process *p, const char *filename,...)
Set the command and start a process.
Definition os_process.c:906
enum GNUNET_GenericReturnValue GNUNET_process_run_command(struct GNUNET_Process *p, const char *command)
Set the command and start a process.
Definition os_process.c:924
enum GNUNET_GenericReturnValue GNUNET_process_run_command_argv(struct GNUNET_Process *p, const char *filename, const char **argv)
Set the command and start a process.
Definition os_process.c:843
enum GNUNET_GenericReturnValue GNUNET_process_wait(struct GNUNET_Process *proc, bool blocking, enum GNUNET_OS_ProcessStatusType *type, unsigned long *code)
Wait for a process to terminate.
enum GNUNET_GenericReturnValue GNUNET_process_set_options_(struct GNUNET_Process *proc, unsigned int num_options, const struct GNUNET_ProcessOptionValue options[])
Set the requested options for the process.
enum GNUNET_GenericReturnValue GNUNET_process_run_command_ap(struct GNUNET_Process *p, const char *filename, va_list va)
Set the command and start a process.
Definition os_process.c:869
void GNUNET_process_destroy(struct GNUNET_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition os_process.c:363
struct GNUNET_Process * GNUNET_process_current()
Get process structure for current process.
Definition os_process.c:299
GNUNET_OS_ProcessStatusType
Process status types.
enum GNUNET_GenericReturnValue GNUNET_OS_check_helper_binary(const char *binary, bool check_suid, const char *params)
Check whether an executable exists and possibly if the suid bit is set on the file.
pid_t GNUNET_process_get_pid(const struct GNUNET_Process *proc)
Get the pid of the process in question.
Definition os_process.c:356
enum GNUNET_GenericReturnValue GNUNET_process_kill(struct GNUNET_Process *proc, int sig)
Sends a signal to the process.
Definition os_process.c:307
struct GNUNET_Process * GNUNET_process_create(enum GNUNET_OS_InheritStdioFlags std_inheritance)
Create a process handle.
Definition os_process.c:465
@ GNUNET_OS_INHERIT_STD_IN
When this flag is set, the child process will inherit stdin of the parent.
@ GNUNET_OS_INHERIT_STD_OUT
When this flag is set, the child process will inherit stdout of the parent.
@ GNUNET_OS_INHERIT_STD_ERR
When this flag is set, the child process will inherit stderr of the parent.
@ GNUNET_OS_USE_PIPE_CONTROL
Should a pipe be used to send signals to the child?
@ GNUNET_PROCESS_OPTION_INHERIT_FD
Option to inherit file descriptors.
@ GNUNET_PROCESS_OPTION_END
End of list of options.
@ GNUNET_PROCESS_OPTION_INHERIT_LSOCK
Option to inherit a listen socket systemd-style.
@ GNUNET_PROCESS_OPTION_SET_ENVIRONMENT
Option to set environment variables.
@ GNUNET_OS_PROCESS_SIGNALED
The process was killed by a signal.
@ GNUNET_OS_PROCESS_EXITED
The process exited with a return code.
@ GNUNET_OS_PROCESS_UNKNOWN
The process is not known to the OS (or at least not one of our children).
@ GNUNET_OS_PROCESS_RUNNING
The process is still running.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_read_file(struct GNUNET_TIME_Relative delay, const struct GNUNET_DISK_FileHandle *rfd, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when the specified file descriptor is ready f...
Definition scheduler.c:1667
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run on shutdown, that is when a CTRL-C signal is received,...
Definition scheduler.c:1345
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition scheduler.c:986
void GNUNET_SIGNAL_raise(const int sig)
Raise the given signal by calling the installed signal handlers.
Definition signal.c:98
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
#define GNUNET_OS_CONTROL_PIPE
Definition os_process.c:40
static void parent_control_handler(void *cls)
This handler is called when there are control data to be read on the pipe.
Definition os_process.c:189
static struct GNUNET_Process current_process
Handle for 'this' process.
Definition os_process.c:153
static struct GNUNET_SCHEDULER_Task * pch
Handle for the parent_control_handler() Task.
Definition os_process.c:158
static int safe_dup2(int oldfd, int newfd)
Call dup2() in a way that races and interrupts are safely handled.
Definition os_process.c:404
#define LOG_STRERROR_FILE(kind, syscall, filename)
Definition os_process.c:36
static enum GNUNET_GenericReturnValue map_std(struct GNUNET_Process *proc, int fd, int flags)
Map the standard input/output/error file descriptor fd based on the mapping given in proc.
Definition os_process.c:522
static void shutdown_pch(void *cls)
This handler is called on shutdown to remove the pch.
Definition os_process.c:172
static struct GNUNET_SCHEDULER_Task * spch
Handle for the shutdown_pch() Task.
Definition os_process.c:163
#define LOG(kind,...)
Definition os_process.c:31
#define LOG_STRERROR(kind, syscall)
Definition os_process.c:33
static enum GNUNET_GenericReturnValue clear_cloexec(int fd)
Clear the close-on-exec flag of fd.
Definition os_process.c:483
static enum GNUNET_GenericReturnValue open_dev_null(int target_fd, int flags)
Open '/dev/null' and make the result the given file descriptor.
Definition os_process.c:435
static enum GNUNET_GenericReturnValue process_start(struct GNUNET_Process *proc)
Definition os_process.c:548
Key-value pairs for setenv().
Definition os_process.c:76
char * value
Value of the environment variable.
Definition os_process.c:85
char * key
Environment key to use.
Definition os_process.c:80
Handle used to access files (and pipes).
Handle used to manage a pipe.
Definition disk.c:69
Possible options we can set for a process.
struct GNUNET_ProcessOptionValue::@34::@35 set_environment
Value of if option is GNUNET_PROCESS_OPTION_SET_ENVIRONMENT.
int target_fd
File descriptor in the target process.
const char * key
Name of the environment variable to set.
struct GNUNET_ProcessOptionValue::@34::@36 inherit_fd
Value of if option is GNUNET_PROCESS_OPTION_INHERIT_FD.
int parent_fd
File descriptor in the parent process (must be open!).
enum GNUNET_ProcessOption option
Type of the option being set.
const char * value
Value to set, NULL to clear.
union GNUNET_ProcessOptionValue::@34 details
Specific option value.
int inherit_lsock
Value of if option is GNUNET_PROCESS_OPTION_INHERIT_LSOCK.
struct EnviEntry * envs
Environment variables to set in the target process.
Definition os_process.c:126
enum GNUNET_OS_ProcessStatusType exit_type
Runtime status of the process.
Definition os_process.c:141
unsigned int envs_size
Length of the envs.
Definition os_process.c:121
pid_t pid
PID of the process.
Definition os_process.c:94
enum GNUNET_OS_InheritStdioFlags std_inheritance
What to do with stdin/stdout/stderr unless already specified in the file map.
Definition os_process.c:106
struct ProcessFileMapEntry * map
Map of file descriptors to keep and dup2 for the new process.
Definition os_process.c:116
char * filename
Name of the binary to execute.
Definition os_process.c:131
char ** argv
Command-line arguments to pass, NULL-terminated.
Definition os_process.c:136
unsigned long exit_code
Exit status code of the process, interpretation depends on exit_type.
Definition os_process.c:146
unsigned int map_size
Length of the map.
Definition os_process.c:111
struct GNUNET_DISK_FileHandle * control_pipe
Pipe we use to signal the process.
Definition os_process.c:100
Entry in list of pending tasks.
Definition scheduler.c:141
Mapping of file descriptors of the current process to (desired) file descriptors of the child process...
Definition os_process.c:48
int target_fd
File descriptor to be used in the target process, -1 if it does not matter.
Definition os_process.c:52

◆ LOG_STRERROR_FILE

#define LOG_STRERROR_FILE (   kind,
  syscall,
  filename 
)
Value:
GNUNET_log_from_strerror_file (kind, "util-os-process", syscall, \
#define GNUNET_log_from_strerror_file(level, component, cmd, filename)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...

Definition at line 36 of file os_process.c.

◆ GNUNET_OS_CONTROL_PIPE

#define GNUNET_OS_CONTROL_PIPE   "GNUNET_OS_CONTROL_PIPE"

Definition at line 40 of file os_process.c.

Function Documentation

◆ shutdown_pch()

static void shutdown_pch ( void *  cls)
static

This handler is called on shutdown to remove the pch.

Parameters
clsthe struct GNUNET_DISK_FileHandle of the control pipe

Definition at line 172 of file os_process.c.

173{
174 struct GNUNET_DISK_FileHandle *control_pipe = cls;
175
177 pch = NULL;
178 GNUNET_DISK_file_close (control_pipe);
179 control_pipe = NULL;
180}

References GNUNET_DISK_file_close(), GNUNET_SCHEDULER_cancel(), and pch.

Referenced by GNUNET_process_install_parent_control_handler().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parent_control_handler()

static void parent_control_handler ( void *  cls)
static

This handler is called when there are control data to be read on the pipe.

Parameters
clsthe struct GNUNET_DISK_FileHandle of the control pipe

Definition at line 189 of file os_process.c.

190{
191 struct GNUNET_DISK_FileHandle *control_pipe = cls;
192 char sig;
193 ssize_t ret;
194
195 pch = NULL;
196 ret = GNUNET_DISK_file_read (control_pipe,
197 &sig,
198 sizeof(sig));
199 if (sizeof(sig) != ret)
200 {
201 if (-1 == ret)
203 "GNUNET_DISK_file_read");
205 "Closing control pipe\n");
206 GNUNET_DISK_file_close (control_pipe);
208 spch = NULL;
209 return;
210 }
212 "Got control code %d from parent via pipe\n",
213 sig);
215 control_pipe,
217 control_pipe);
218 GNUNET_SIGNAL_raise ((int) sig);
219}

References GNUNET_DISK_file_close(), GNUNET_DISK_file_read(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_SCHEDULER_add_read_file(), GNUNET_SCHEDULER_cancel(), GNUNET_SIGNAL_raise(), GNUNET_TIME_UNIT_FOREVER_REL, LOG, LOG_STRERROR, parent_control_handler(), pch, ret, and spch.

Referenced by GNUNET_process_install_parent_control_handler(), and parent_control_handler().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ safe_dup2()

static int safe_dup2 ( int  oldfd,
int  newfd 
)
static

Call dup2() in a way that races and interrupts are safely handled.

Parameters
oldfdold file descriptor
newfdnew file descriptor
Returns
result of dup2()

Definition at line 404 of file os_process.c.

406{
407 int ret;
408
409 while (1)
410 {
411 ret = dup2 (oldfd,
412 newfd);
413 if (-1 != ret)
414 break;
415 if ( (EBADF == errno) ||
416 (EINVAL == errno) ||
417 (EMFILE == errno) )
418 break;
419 /* should be EBUSY/EINTR, so try again */
421 "dup2");
422 }
423 return ret;
424}

References GNUNET_ERROR_TYPE_WARNING, GNUNET_log_strerror, and ret.

Referenced by map_std(), and process_start().

Here is the caller graph for this function:

◆ open_dev_null()

static enum GNUNET_GenericReturnValue open_dev_null ( int  target_fd,
int  flags 
)
static

Open '/dev/null' and make the result the given file descriptor.

Parameters
target_fddesired FD to point to /dev/null
flagsopen flags (O_RDONLY, O_WRONLY)
Returns
GNUNET_OK on success

Definition at line 435 of file os_process.c.

437{
438 int fd;
439
440 fd = open ("/dev/null",
441 flags);
442 if (-1 == fd)
443 {
445 "open",
446 "/dev/null");
447 return GNUNET_SYSERR;
448 }
449 if (fd == target_fd)
450 return GNUNET_OK;
451 if (-1 == dup2 (fd,
452 target_fd))
453 {
455 "dup2");
456 GNUNET_break (0 == close (fd));
457 return GNUNET_SYSERR;
458 }
459 GNUNET_break (0 == close (fd));
460 return GNUNET_OK;
461}

References GNUNET_break, GNUNET_ERROR_TYPE_ERROR, GNUNET_log_strerror, GNUNET_log_strerror_file, GNUNET_OK, GNUNET_SYSERR, and ProcessFileMapEntry::target_fd.

Referenced by map_std().

Here is the caller graph for this function:

◆ clear_cloexec()

static enum GNUNET_GenericReturnValue clear_cloexec ( int  fd)
static

Clear the close-on-exec flag of fd.

Parameters
fdfile descriptor to modify
Returns
GNUNET_OK on success

Definition at line 483 of file os_process.c.

484{
485 int flags;
486
487 flags = fcntl (fd,
488 F_GETFD);
489 if (flags == -1)
490 {
492 "fcntl(F_GETFD)");
493 return GNUNET_SYSERR;
494 }
495
496 flags &= ~FD_CLOEXEC;
497
498 if (-1 ==
499 fcntl (fd,
500 F_SETFD,
501 flags))
502 {
504 "fcntl(F_SETFD)");
505 return GNUNET_SYSERR;
506 }
507 return GNUNET_OK;
508}

References GNUNET_ERROR_TYPE_ERROR, GNUNET_log_strerror, GNUNET_OK, and GNUNET_SYSERR.

Referenced by process_start().

Here is the caller graph for this function:

◆ map_std()

static enum GNUNET_GenericReturnValue map_std ( struct GNUNET_Process proc,
int  fd,
int  flags 
)
static

Map the standard input/output/error file descriptor fd based on the mapping given in proc.

If fd is not explicitly mapped, use /dev/null with the given flags.

Parameters
procprocess with mapping information
fdfile descriptor to map
flagsflags to use with /dev/null
Returns
GNUNET_OK on success

Definition at line 522 of file os_process.c.

525{
526 for (unsigned int i=0; i<proc->map_size; i++)
527 {
528 struct ProcessFileMapEntry *me = &proc->map[i];
529
530 if (fd == me->target_fd)
531 {
532 if (fd !=
533 safe_dup2 (me->parent_fd,
534 fd))
535 return GNUNET_SYSERR;
536 if (me->owned)
537 GNUNET_break (0 == close (me->parent_fd));
538 me->parent_fd = -1;
539 return GNUNET_OK;
540 }
541 }
542 return open_dev_null (fd,
543 flags);
544}

References GNUNET_break, GNUNET_OK, GNUNET_SYSERR, GNUNET_Process::map, GNUNET_Process::map_size, me, open_dev_null(), and safe_dup2().

Referenced by process_start().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ process_start()

static enum GNUNET_GenericReturnValue process_start ( struct GNUNET_Process proc)
static

Definition at line 548 of file os_process.c.

549{
550 pid_t ret;
551 int childpipe_read_fd;
552
553 if (-1 != proc->pid)
554 {
555 GNUNET_break (0);
556 return GNUNET_SYSERR;
557 }
558 if (NULL == proc->filename)
559 {
560 GNUNET_break (0);
561 return GNUNET_SYSERR;
562 }
563
565 {
566 struct GNUNET_DISK_PipeHandle *childpipe;
567 struct GNUNET_DISK_FileHandle *childpipe_read;
568 int dup_childpipe_read_fd = -1;
569
571 if (NULL == childpipe)
572 {
573 GNUNET_break (0);
574 return GNUNET_SYSERR;
575 }
576 childpipe_read =
579 proc->control_pipe =
582 GNUNET_DISK_pipe_close (childpipe);
583 if ( (NULL == childpipe_read) ||
584 (NULL == proc->control_pipe) ||
585 (GNUNET_OK !=
586 GNUNET_DISK_internal_file_handle_ (childpipe_read,
587 &childpipe_read_fd)) ||
588 (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd))) )
589 {
590 GNUNET_break (0);
591 if (NULL != childpipe_read)
592 GNUNET_DISK_file_close (childpipe_read);
593 if (NULL != proc->control_pipe)
594 {
596 proc->control_pipe = NULL;
597 }
598 return GNUNET_SYSERR;
599 }
600 childpipe_read_fd = dup_childpipe_read_fd;
601 GNUNET_DISK_file_close (childpipe_read);
602 }
603 else
604 {
605 childpipe_read_fd = -1;
606 }
607
608 {
609 /* Make sure none of the parent_fds conflict with the target_fds in the client process */
610 unsigned int max_fd = 3; /* never use stdin/stdout/stderr */
611 unsigned int pos;
612
613 for (unsigned int i=0; i<proc->map_size; i++)
614 {
615 struct ProcessFileMapEntry *me = &proc->map[i];
616
617 max_fd = GNUNET_MAX (max_fd,
618 me->target_fd);
619 }
620 pos = max_fd + 1;
621 for (unsigned int i=0; i<proc->map_size; i++)
622 {
623 struct ProcessFileMapEntry *me = &proc->map[i];
624
625 if (me->parent_fd < pos)
626 {
627 if (me->target_fd == me->parent_fd)
628 {
629 /* same FD, so no conflict, but make sure FD is not closed
630 on exec() */
631 clear_cloexec (me->parent_fd);
632 }
633 else
634 {
635 int dst;
636
637 /* search for FD that is not yet open */
638 while (-1 !=
639 fcntl (pos,
640 F_GETFD))
641 pos++;
642 while (1)
643 {
644 dst = dup2 (me->parent_fd,
645 pos);
646 if (-1 != dst)
647 break;
648 if ( (EBADF == errno) ||
649 (EINVAL == errno) ||
650 (EMFILE == errno) )
651 {
652 GNUNET_break (0);
653 return GNUNET_SYSERR;
654 }
655 /* leaves interrupt/busy, try again */
656 }
657 if (me->owned)
658 GNUNET_break (0 == close (me->parent_fd));
659 me->parent_fd = dst;
660 me->owned = true;
661 }
662 }
663 }
664 }
665
666 ret = fork ();
667 if (-1 == ret)
668 {
669 int eno = errno;
670
672 "fork");
673 if (NULL != proc->control_pipe)
674 {
676 proc->control_pipe = NULL;
677 }
678 if (0 <= childpipe_read_fd)
679 GNUNET_break (0 == close (childpipe_read_fd));
680 errno = eno;
681 return GNUNET_SYSERR;
682 }
683 if (0 != ret)
684 {
685 proc->pid = ret;
686 if (0 <= childpipe_read_fd)
687 GNUNET_break (0 == close (childpipe_read_fd));
688 for (unsigned int i=0; i<proc->map_size; i++)
689 {
690 struct ProcessFileMapEntry *me = &proc->map[i];
691
692 if (! me->owned)
693 continue;
694 if (-1 != me->parent_fd)
695 {
696 GNUNET_assert (0 ==
697 close (me->parent_fd));
698 me->parent_fd = -1;
699 }
700 }
701 return GNUNET_OK;
702 }
703
704 /* in child process! */
705
706 /* deploy control pipe */
707 if (0 <= childpipe_read_fd)
708 {
709 char fdbuf[100];
710
712 snprintf (fdbuf,
713 sizeof (fdbuf),
714 "%x",
715 childpipe_read_fd);
716 GNUNET_assert (0 ==
718 fdbuf,
719 1));
720 }
721 else
722 {
723 GNUNET_assert (0 ==
724 unsetenv (GNUNET_OS_CONTROL_PIPE));
725 }
726
727 /* map stdin/stdout/stderr */
728 if (0 == (proc->std_inheritance & GNUNET_OS_INHERIT_STD_IN))
729 {
731 map_std (proc,
732 STDIN_FILENO,
733 O_RDONLY));
734 }
736 {
738 map_std (proc,
739 STDOUT_FILENO,
740 O_WRONLY));
741 }
743 {
745 map_std (proc,
746 STDERR_FILENO,
747 O_WRONLY));
748 }
749
750 /* map all other file descriptors */
751 {
752 char *fdnames = GNUNET_strdup ("");
753 unsigned int total_lfds = 0;
754
755 for (unsigned int i=0; i<proc->map_size; i++)
756 {
757 struct ProcessFileMapEntry *me = &proc->map[i];
758
759 if (-1 == me->parent_fd)
760 continue; /* already taken care of */
761 if (-1 == me->target_fd)
762 {
763 me->target_fd = dup (me->parent_fd);
764 GNUNET_assert (-1 != me->target_fd);
765 if (me->owned)
766 GNUNET_assert (0 == close (me->parent_fd));
767 }
768 else if (me->parent_fd != me->target_fd)
769 {
770 GNUNET_assert (me->target_fd ==
771 safe_dup2 (me->parent_fd,
772 me->target_fd));
773 if (me->owned)
774 GNUNET_assert (0 == close (me->parent_fd));
775 }
776 else
777 {
778 GNUNET_assert (! me->systemd_listen_socket);
779 GNUNET_assert (me->owned);
781 clear_cloexec (me->target_fd));
782 }
783 me->parent_fd = -1;
784 if (me->systemd_listen_socket)
785 {
786 char *tmp;
787
788 GNUNET_asprintf (&tmp,
789 "%s:%d",
790 fdnames,
791 me->target_fd);
792 GNUNET_free (fdnames);
793 fdnames = tmp;
794 total_lfds++;
795 }
796 }
797 if (0 != total_lfds)
798 {
799 char fds[16];
800
801 GNUNET_snprintf (fds,
802 sizeof(fds),
803 "%u",
804 total_lfds);
805 GNUNET_assert (0 ==
806 setenv ("LISTEN_FDS",
807 fds,
808 1));
809 GNUNET_assert (0 ==
810 setenv ("LISTEN_FDNAMES",
811 fdnames + 1, /* skip leading ':' */
812 1));
813 }
814 GNUNET_free (fdnames);
815 }
816
817 /* setup environment */
818 for (unsigned int i=0; i<proc->envs_size; i++)
819 {
820 struct EnviEntry *ee = &proc->envs[i];
821
822 if (NULL == ee->value)
823 GNUNET_assert (0 ==
824 unsetenv (ee->key));
825 else
826 GNUNET_assert (0 ==
827 setenv (ee->key,
828 ee->value,
829 1));
830 }
831
832 /* finally execute */
833 execvp (proc->filename,
834 proc->argv);
836 "execvp",
837 proc->filename);
838 _exit (1);
839}

References GNUNET_Process::argv, clear_cloexec(), GNUNET_Process::control_pipe, GNUNET_Process::envs, GNUNET_Process::envs_size, GNUNET_Process::filename, GNUNET_asprintf(), GNUNET_assert, GNUNET_break, GNUNET_DISK_file_close(), GNUNET_DISK_internal_file_handle_(), GNUNET_DISK_PF_NONE, GNUNET_DISK_pipe(), GNUNET_DISK_pipe_close(), GNUNET_DISK_pipe_detach_end(), GNUNET_DISK_PIPE_END_READ, GNUNET_DISK_PIPE_END_WRITE, GNUNET_ERROR_TYPE_ERROR, GNUNET_free, GNUNET_MAX, GNUNET_OK, GNUNET_OS_CONTROL_PIPE, GNUNET_OS_INHERIT_STD_ERR, GNUNET_OS_INHERIT_STD_IN, GNUNET_OS_INHERIT_STD_OUT, GNUNET_OS_USE_PIPE_CONTROL, GNUNET_snprintf(), GNUNET_strdup, GNUNET_SYSERR, EnviEntry::key, LOG_STRERROR, LOG_STRERROR_FILE, GNUNET_Process::map, GNUNET_Process::map_size, map_std(), me, GNUNET_Process::pid, ret, safe_dup2(), GNUNET_Process::std_inheritance, and EnviEntry::value.

Referenced by GNUNET_process_run_command(), GNUNET_process_run_command_ap(), and GNUNET_process_run_command_argv().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ current_process

struct GNUNET_Process current_process
static

Handle for 'this' process.

Definition at line 153 of file os_process.c.

Referenced by GNUNET_process_current().

◆ pch

struct GNUNET_SCHEDULER_Task* pch
static

◆ spch

struct GNUNET_SCHEDULER_Task* spch
static

Handle for the shutdown_pch() Task.

Definition at line 163 of file os_process.c.

Referenced by GNUNET_process_install_parent_control_handler(), and parent_control_handler().