2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2011 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file util/os_priority.c
23 * @brief Methods to set process priority
28 #include "gnunet_common.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet_scheduler_lib.h"
31 #include "gnunet_strings_lib.h"
32 #include "gnunet_crypto_lib.h"
36 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
38 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
40 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
42 #define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
45 struct GNUNET_OS_Process
60 * Pipe we use to signal the process (if used).
62 struct GNUNET_DISK_FileHandle *control_pipe;
65 * Name of the pipe, NULL for none.
72 * Handle for 'this' process.
74 static struct GNUNET_OS_Process current_process;
77 /* MinGW version of named pipe API */
80 * Creates a named pipe/FIFO and opens it
82 * @param fn pointer to the name of the named pipe or to NULL
83 * @param flags open flags
84 * @param perm access permissions
85 * @return pipe handle on success, NULL on error
87 static struct GNUNET_DISK_FileHandle *
88 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
89 enum GNUNET_DISK_AccessPermissions perm)
91 struct GNUNET_DISK_FileHandle *ret;
97 if (flags & GNUNET_DISK_OPEN_READWRITE)
98 openMode = PIPE_ACCESS_DUPLEX;
99 else if (flags & GNUNET_DISK_OPEN_READ)
100 openMode = PIPE_ACCESS_INBOUND;
101 else if (flags & GNUNET_DISK_OPEN_WRITE)
102 openMode = PIPE_ACCESS_OUTBOUND;
103 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
104 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
113 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
114 LOG (GNUNET_ERROR_TYPE_DEBUG,
115 "Trying to create an instance of named pipe `%s'\n", name);
116 /* 1) This might work just fine with UTF-8 strings as it is.
117 * 2) This is only used by GNUnet itself, and only with latin names.
119 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
120 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
125 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
126 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
128 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
130 h = CreateNamedPipe (*fn,
131 openMode | FILE_FLAG_OVERLAPPED |
132 FILE_FLAG_FIRST_PIPE_INSTANCE,
133 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
136 error_code = GetLastError ();
139 /* don't re-set name to NULL yet */
140 if (h == INVALID_HANDLE_VALUE)
142 SetErrnoFromWinError (error_code);
143 LOG (GNUNET_ERROR_TYPE_DEBUG,
144 "Pipe creation have failed because of %d, errno is %d\n", error_code,
148 LOG (GNUNET_ERROR_TYPE_DEBUG,
149 "Pipe was to be unique, considering re-creation\n");
152 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
156 LOG (GNUNET_ERROR_TYPE_DEBUG,
157 "Pipe name was not unique, trying again\n");
166 ret = GNUNET_malloc (sizeof (*ret));
168 ret->type = GNUNET_PIPE;
169 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
170 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
171 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
172 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
178 * Opens already existing named pipe/FIFO
180 * @param fn name of an existing named pipe
181 * @param flags open flags
182 * @return pipe handle on success, NULL on error
184 static struct GNUNET_DISK_FileHandle *
185 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
187 struct GNUNET_DISK_FileHandle *ret;
192 if (flags & GNUNET_DISK_OPEN_READWRITE)
193 openMode = GENERIC_WRITE | GENERIC_READ;
194 else if (flags & GNUNET_DISK_OPEN_READ)
195 openMode = GENERIC_READ;
196 else if (flags & GNUNET_DISK_OPEN_WRITE)
197 openMode = GENERIC_WRITE;
199 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
200 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
201 if (h == INVALID_HANDLE_VALUE)
203 SetErrnoFromWinError (GetLastError ());
207 ret = GNUNET_malloc (sizeof (*ret));
209 ret->type = GNUNET_PIPE;
210 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
211 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
212 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
213 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
219 /* UNIX version of named-pipe API */
222 * Clean up a named pipe and the directory it was placed in.
224 * @param fn name of the pipe
227 cleanup_npipe (const char *fn)
232 if (0 != unlink (fn))
233 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
234 dn = GNUNET_strdup (fn);
237 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
243 * Setup a named pipe.
245 * @param fn where to store the name of the new pipe,
246 * if *fn is non-null, the name of the pipe to setup
247 * @return GNUNET_OK on success
250 npipe_setup (char **fn)
254 /* FIXME: hardwired '/tmp' path... is bad */
255 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
257 if (NULL == mkdtemp (dir))
259 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
260 return GNUNET_SYSERR;
262 GNUNET_asprintf (fn, "%s/child-control", dir);
264 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
265 return GNUNET_SYSERR;
271 * Open an existing named pipe.
273 * @param fn name of the file
274 * @param flags flags to use
275 * @return NULL on error
277 static struct GNUNET_DISK_FileHandle *
278 npipe_open (const char *fn,
279 enum GNUNET_DISK_OpenFlags flags)
281 struct GNUNET_DISK_FileHandle *ret;
286 /* 200 * 5ms = 1s at most */
289 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
290 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
292 /* as this is for killing a child process via pipe and it is conceivable that
293 the child process simply didn't finish starting yet, we do some sleeping
294 (which is obviously usually not allowed). We can't select on the FD as
295 'open' fails, and we probably shouldn't just "ignore" the error, so wait
296 and retry a few times is likely the best method; our process API doesn't
297 support continuations, so we need to sleep directly... */
299 req.tv_nsec = 5000000; /* 5ms */
300 (void) nanosleep (&req, NULL);
304 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
305 (flags == GNUNET_DISK_OPEN_READ)
306 ? _("Failed to open named pipe `%s' for reading: %s\n")
307 : _("Failed to open named pipe `%s' for writing: %s\n"),
312 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
320 * This handler is called when there are control data to be read on the pipe
322 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
323 * @param tc scheduler context
326 parent_control_handler (void *cls,
327 const struct GNUNET_SCHEDULER_TaskContext *tc)
329 struct GNUNET_DISK_FileHandle *control_pipe = cls;
333 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
335 if (0 != (tc->reason &
336 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
338 GNUNET_DISK_file_close (control_pipe);
342 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
343 if (sizeof (sig) != ret)
346 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
347 GNUNET_DISK_file_close (control_pipe);
351 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
352 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
353 control_pipe, &parent_control_handler,
360 * Task that connects this process to its parent via pipe;
361 * essentially, the parent control handler will read signal numbers
362 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
363 * variable) and raise those signals.
365 * @param cls closure (unused)
366 * @param tc scheduler context (unused)
369 GNUNET_OS_install_parent_control_handler (void *cls,
371 GNUNET_SCHEDULER_TaskContext *tc)
374 struct GNUNET_DISK_FileHandle *control_pipe;
376 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
377 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
379 LOG (GNUNET_ERROR_TYPE_DEBUG,
380 "Not installing a handler because $%s is empty\n",
381 GNUNET_OS_CONTROL_PIPE);
382 putenv ("GNUNET_OS_CONTROL_PIPE=");
386 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
387 if (NULL == control_pipe)
389 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
390 putenv ("GNUNET_OS_CONTROL_PIPE=");
393 LOG (GNUNET_ERROR_TYPE_DEBUG,
394 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
395 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
396 &parent_control_handler, control_pipe);
397 putenv ("GNUNET_OS_CONTROL_PIPE=");
402 * Get process structure for current process
404 * The pointer it returns points to static memory location and must not be
407 * @return pointer to the process sturcutre for this process
409 struct GNUNET_OS_Process *
410 GNUNET_OS_process_current ()
413 current_process.pid = GetCurrentProcessId ();
414 current_process.handle = GetCurrentProcess ();
416 current_process.pid = 0;
418 return ¤t_process;
423 * Sends a signal to the process
425 * @param proc pointer to process structure
427 * @return 0 on success, -1 on error
430 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
437 if ( (NULL == proc->control_pipe) &&
438 (NULL != proc->childpipename) )
439 proc->control_pipe = npipe_open (proc->childpipename,
440 GNUNET_DISK_OPEN_WRITE);
442 if (NULL != proc->control_pipe)
444 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
445 if (ret == sizeof (csig))
448 /* pipe failed or non-existent, try other methods */
457 #if WINDOWS && !defined(__CYGWIN__)
460 int must_kill = GNUNET_YES;
461 if (0 != GetExitCodeProcess (proc->handle, &exitcode))
462 must_kill = (exitcode == STILL_ACTIVE) ? GNUNET_YES : GNUNET_NO;
463 if (GNUNET_YES == must_kill)
464 if (0 == TerminateProcess (proc->handle, 0))
466 SetErrnoFromWinError (GetLastError ());
472 return PLIBC_KILL (proc->pid, sig);
479 return PLIBC_KILL (proc->pid, sig);
485 * Get the pid of the process in question
487 * @param proc the process to get the pid of
489 * @return the current process id
492 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
499 * Cleans up process structure contents (OS-dependent) and deallocates it
501 * @param proc pointer to process structure
504 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
506 if (NULL != proc->control_pipe)
507 GNUNET_DISK_file_close (proc->control_pipe);
510 if (proc->handle != NULL)
511 CloseHandle (proc->handle);
513 if (NULL != proc->childpipename)
516 cleanup_npipe (proc->childpipename);
518 GNUNET_free (proc->childpipename);
525 #include "gnunet_signal_lib.h"
527 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
530 * Make seaspider happy.
532 #define DWORD_WINAPI DWORD WINAPI
535 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
536 * @param proc pointer to process structure
539 child_wait_thread (void *arg)
541 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
543 WaitForSingleObject (proc->handle, INFINITE);
545 if (w32_sigchld_handler)
546 w32_sigchld_handler ();
554 * Set process priority
556 * @param proc pointer to process structure
557 * @param prio priority value
558 * @return GNUNET_OK on success, GNUNET_SYSERR on error
561 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
562 enum GNUNET_SCHEDULER_Priority prio)
566 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
567 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
570 /* convert to MINGW/Unix values */
573 case GNUNET_SCHEDULER_PRIORITY_UI:
574 case GNUNET_SCHEDULER_PRIORITY_URGENT:
576 rprio = HIGH_PRIORITY_CLASS;
582 case GNUNET_SCHEDULER_PRIORITY_HIGH:
584 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
590 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
592 rprio = NORMAL_PRIORITY_CLASS;
598 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
600 rprio = BELOW_NORMAL_PRIORITY_CLASS;
606 case GNUNET_SCHEDULER_PRIORITY_IDLE:
608 rprio = IDLE_PRIORITY_CLASS;
615 return GNUNET_SYSERR;
618 /* Set process priority */
621 HANDLE h = proc->handle;
623 GNUNET_assert (h != NULL);
624 SetPriorityClass (h, rprio);
630 if ((0 == pid) || (pid == getpid ()))
633 int delta = rprio - have;
636 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
638 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
639 return GNUNET_SYSERR;
644 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
646 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
648 return GNUNET_SYSERR;
652 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
653 "Priority management not availabe for this platform\n");
661 CreateCustomEnvTable (char **vars)
663 char *win32_env_table;
668 size_t tablesize = 0;
669 size_t items_count = 0;
678 win32_env_table = GetEnvironmentStringsA ();
679 if (win32_env_table == NULL)
681 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
683 index = GNUNET_malloc (sizeof (char *) * n_var);
684 for (c = 0; c < n_var; c++)
686 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
688 size_t len = strlen (ptr);
691 for (var_ptr = vars; *var_ptr; var_ptr++)
695 var_len = strlen (var);
696 if (strncmp (var, ptr, var_len) == 0)
700 tablesize += var_len + strlen (val) + 1;
705 tablesize += len + 1;
708 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
713 n_found += strlen (var) + strlen (val) + 1;
715 result = GNUNET_malloc (tablesize + n_found + 1);
716 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
718 size_t len = strlen (ptr);
721 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
725 var_len = strlen (var);
726 if (strncmp (var, ptr, var_len) == 0)
734 strcpy (result_ptr, ptr);
735 result_ptr += len + 1;
739 strcpy (result_ptr, var);
740 result_ptr += var_len;
741 strcpy (result_ptr, val);
742 result_ptr += strlen (val) + 1;
746 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
750 var_len = strlen (var);
753 strcpy (result_ptr, var);
754 result_ptr += var_len;
755 strcpy (result_ptr, val);
756 result_ptr += strlen (val) + 1;
759 FreeEnvironmentStrings (win32_env_table);
770 * @param pipe_control should a pipe be used to send signals to the child?
771 * @param pipe_stdin pipe to use to send input to child process (or NULL)
772 * @param pipe_stdout pipe to use to get output from child process (or NULL)
773 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
774 * must be NULL on platforms where dup is not supported
775 * @param filename name of the binary
776 * @param argv NULL-terminated list of arguments to the process
777 * @return process ID of the new process, -1 on error
779 static struct GNUNET_OS_Process *
780 start_process (int pipe_control,
781 struct GNUNET_DISK_PipeHandle *pipe_stdin,
782 struct GNUNET_DISK_PipeHandle *pipe_stdout,
783 const SOCKTYPE *lsocks,
784 const char *filename,
791 struct GNUNET_OS_Process *gnunet_proc;
792 char *childpipename = NULL;
805 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
806 return NULL; /* not executable */
807 if ( (GNUNET_YES == pipe_control) &&
808 (GNUNET_OK != npipe_setup (&childpipename)) )
810 if (pipe_stdout != NULL)
812 GNUNET_assert (GNUNET_OK ==
813 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
815 GNUNET_DISK_PIPE_END_WRITE),
816 &fd_stdout_write, sizeof (int)));
817 GNUNET_assert (GNUNET_OK ==
818 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
819 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
820 &fd_stdout_read, sizeof (int)));
822 if (pipe_stdin != NULL)
824 GNUNET_assert (GNUNET_OK ==
825 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
826 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
827 &fd_stdin_read, sizeof (int)));
828 GNUNET_assert (GNUNET_OK ==
829 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
830 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
831 &fd_stdin_write, sizeof (int)));
838 while (-1 != (k = lsocks[i++]))
839 GNUNET_array_append (lscp, ls, k);
840 GNUNET_array_append (lscp, ls, -1);
847 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
848 GNUNET_free_non_null (childpipename);
849 GNUNET_array_grow (lscp, ls, 0);
855 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
856 gnunet_proc->pid = ret;
857 gnunet_proc->childpipename = childpipename;
858 GNUNET_array_grow (lscp, ls, 0);
861 if (NULL != childpipename)
863 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
864 GNUNET_free (childpipename);
866 if (pipe_stdout != NULL)
868 GNUNET_break (0 == close (fd_stdout_read));
869 if (-1 == dup2 (fd_stdout_write, 1))
870 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
871 GNUNET_break (0 == close (fd_stdout_write));
873 if (pipe_stdin != NULL)
876 GNUNET_break (0 == close (fd_stdin_write));
877 if (-1 == dup2 (fd_stdin_read, 0))
878 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
879 GNUNET_break (0 == close (fd_stdin_read));
883 /* read systemd documentation... */
884 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
885 setenv ("LISTEN_PID", lpid, 1);
888 while (-1 != lscp[i])
891 while (-1 != lscp[j])
897 GNUNET_assert (-1 != k);
898 GNUNET_assert (0 == close (lscp[j]));
906 /* Bury any existing FD, no matter what; they should all be closed
907 * on exec anyway and the important onces have been dup'ed away */
909 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
911 /* unset close-on-exec flag */
912 flags = fcntl (tgt, F_GETFD);
913 GNUNET_assert (flags >= 0);
914 flags &= ~FD_CLOEXEC;
916 (void) fcntl (tgt, F_SETFD, flags);
920 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
921 setenv ("LISTEN_FDS", fds, 1);
923 GNUNET_array_grow (lscp, ls, 0);
924 execvp (filename, argv);
925 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
928 struct GNUNET_DISK_FileHandle *control_pipe;
929 char *childpipename = NULL;
931 char **non_const_argv;
936 PROCESS_INFORMATION proc;
938 struct GNUNET_OS_Process *gnunet_proc;
939 char path[MAX_PATH + 1];
940 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
941 char *env_block = NULL;
949 char *non_const_filename;
950 char win_path[MAX_PATH + 1];
951 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
952 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
963 HANDLE stdout_handle;
965 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
966 return NULL; /* not executable */
968 /* Search in prefix dir (hopefully - the directory from which
969 * the current module was loaded), bindir and libdir, then in PATH
971 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
972 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
973 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
975 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
978 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
981 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
984 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
985 GNUNET_free (self_prefix);
986 GNUNET_free (bindir);
987 GNUNET_free (libdir);
989 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
990 if (alloc_len != pathbuf_len - 1)
992 GNUNET_free (pathbuf);
993 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
997 cmdlen = strlen (filename);
998 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
999 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1001 GNUNET_asprintf (&non_const_filename, "%s", filename);
1003 /* It could be in POSIX form, convert it to a DOS path early on */
1004 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1006 SetErrnoFromWinError (lRet);
1007 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1008 non_const_filename);
1009 GNUNET_free (non_const_filename);
1010 GNUNET_free (pathbuf);
1013 GNUNET_free (non_const_filename);
1014 non_const_filename = GNUNET_strdup (win_path);
1015 /* Check that this is the full path. If it isn't, search. */
1016 /* FIXME: convert it to wchar_t and use SearchPathW?
1017 * Remember: arguments to _start_process() are technically in UTF-8...
1019 if (non_const_filename[1] == ':')
1020 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1021 else if (!SearchPathA
1022 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1025 SetErrnoFromWinError (GetLastError ());
1026 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1027 non_const_filename);
1028 GNUNET_free (non_const_filename);
1029 GNUNET_free (pathbuf);
1032 GNUNET_free (pathbuf);
1033 GNUNET_free (non_const_filename);
1035 /* Count the number of arguments */
1036 arg = (char **) argv;
1043 /* Allocate a copy argv */
1044 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1046 /* Copy all argv strings */
1048 arg = (char **) argv;
1052 non_const_argv[argcount] = GNUNET_strdup (path);
1054 non_const_argv[argcount] = GNUNET_strdup (*arg);
1058 non_const_argv[argcount] = NULL;
1062 arg = non_const_argv;
1065 cmdlen = cmdlen + strlen (*arg) + 4;
1069 /* Allocate and create cmd */
1070 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1071 arg = non_const_argv;
1074 char arg_last_char = (*arg)[strlen (*arg) - 1];
1075 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1076 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1080 while (argcount > 0)
1081 GNUNET_free (non_const_argv[--argcount]);
1082 GNUNET_free (non_const_argv);
1084 memset (&start, 0, sizeof (start));
1085 start.cb = sizeof (start);
1086 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
1087 start.dwFlags |= STARTF_USESTDHANDLES;
1089 if (pipe_stdin != NULL)
1091 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1092 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1093 &stdin_handle, sizeof (HANDLE));
1094 start.hStdInput = stdin_handle;
1097 if (pipe_stdout != NULL)
1099 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1101 GNUNET_DISK_PIPE_END_WRITE),
1102 &stdout_handle, sizeof (HANDLE));
1103 start.hStdOutput = stdout_handle;
1106 if (GNUNET_YES == pipe_control)
1109 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1110 GNUNET_DISK_PERM_USER_READ |
1111 GNUNET_DISK_PERM_USER_WRITE);
1112 if (control_pipe == NULL)
1120 control_pipe = NULL;
1121 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1123 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1125 if (lsocks_pipe == NULL)
1129 GNUNET_DISK_pipe_close (lsocks_pipe);
1132 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1133 GNUNET_DISK_PIPE_END_WRITE);
1134 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1135 &lsocks_write, sizeof (HANDLE));
1136 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1137 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1138 &lsocks_read, sizeof (HANDLE));
1142 if (NULL != childpipename)
1144 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1146 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1147 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1148 GNUNET_free (childpipename);
1150 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1152 /*This will tell the child that we're going to send lsocks over the pipe*/
1153 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1154 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1156 our_env[env_off++] = NULL;
1157 env_block = CreateCustomEnvTable (our_env);
1159 GNUNET_free_non_null (our_env[--env_off]);
1162 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1164 LOG (GNUNET_ERROR_TYPE_DEBUG,
1165 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1166 GNUNET_free (env_block);
1172 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1174 LOG (GNUNET_ERROR_TYPE_DEBUG,
1175 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1176 GNUNET_free (env_block);
1182 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1183 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1185 SetErrnoFromWinError (GetLastError ());
1186 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1187 if (NULL != control_pipe)
1188 GNUNET_DISK_file_close (control_pipe);
1190 GNUNET_DISK_pipe_close (lsocks_pipe);
1191 GNUNET_free (env_block);
1198 GNUNET_free (env_block);
1200 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1201 gnunet_proc->pid = proc.dwProcessId;
1202 gnunet_proc->handle = proc.hProcess;
1203 gnunet_proc->control_pipe = control_pipe;
1205 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1207 ResumeThread (proc.hThread);
1208 CloseHandle (proc.hThread);
1213 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1216 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1218 /* This is a replacement for "goto error" that doesn't use goto */
1223 uint64_t size, count, i;
1225 /* Tell the number of sockets */
1226 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1228 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1229 if (wrote != sizeof (count))
1231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1234 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1236 WSAPROTOCOL_INFOA pi;
1237 /* Get a socket duplication info */
1238 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1240 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1241 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1244 /* Synchronous I/O is not nice, but we can't schedule this:
1245 * lsocks will be closed/freed by the caller soon, and until
1246 * the child creates a duplicate, closing a socket here will
1247 * close it for good.
1249 /* Send the size of the structure
1250 * (the child might be built with different headers...)
1253 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1254 if (wrote != sizeof (size))
1256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1259 /* Finally! Send the data */
1260 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1261 if (wrote != sizeof (pi))
1263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1267 /* This will block us until the child makes a final read or closes
1268 * the pipe (hence no 'wrote' check), since we have to wait for it
1269 * to duplicate the last socket, before we return and start closing
1272 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1277 GNUNET_DISK_file_sync (lsocks_write_fd);
1278 GNUNET_DISK_pipe_close (lsocks_pipe);
1282 /* If we can't pass on the socket(s), the child will block forever,
1283 * better put it out of its misery.
1285 TerminateProcess (gnunet_proc->handle, 0);
1286 CloseHandle (gnunet_proc->handle);
1287 if (NULL != gnunet_proc->control_pipe)
1288 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1289 GNUNET_free (gnunet_proc);
1302 * @param pipe_control should a pipe be used to send signals to the child?
1303 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1304 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1305 * @param filename name of the binary
1306 * @param argv NULL-terminated array of arguments to the process
1307 * @return pointer to process structure of the new process, NULL on error
1309 struct GNUNET_OS_Process *
1310 GNUNET_OS_start_process_vap (int pipe_control,
1311 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1312 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1313 const char *filename,
1316 return start_process (pipe_control,
1328 * @param pipe_control should a pipe be used to send signals to the child?
1329 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1330 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1331 * @param filename name of the binary
1332 * @param va NULL-terminated list of arguments to the process
1333 * @return pointer to process structure of the new process, NULL on error
1335 struct GNUNET_OS_Process *
1336 GNUNET_OS_start_process_va (int pipe_control,
1337 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1338 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1339 const char *filename, va_list va)
1341 struct GNUNET_OS_Process *ret;
1348 while (NULL != va_arg (ap, char *))
1351 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1354 while (NULL != (argv[argc] = va_arg (ap, char *)))
1357 ret = GNUNET_OS_start_process_vap (pipe_control,
1371 * @param pipe_control should a pipe be used to send signals to the child?
1372 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1373 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1374 * @param filename name of the binary
1375 * @param ... NULL-terminated list of arguments to the process
1377 * @return pointer to process structure of the new process, NULL on error
1380 struct GNUNET_OS_Process *
1381 GNUNET_OS_start_process (int pipe_control,
1382 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1383 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1384 const char *filename, ...)
1386 struct GNUNET_OS_Process *ret;
1389 va_start (ap, filename);
1390 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1399 * @param pipe_control should a pipe be used to send signals to the child?
1400 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1401 * must be NULL on platforms where dup is not supported
1402 * @param filename name of the binary
1403 * @param argv NULL-terminated list of arguments to the process
1404 * @return process ID of the new process, -1 on error
1406 struct GNUNET_OS_Process *
1407 GNUNET_OS_start_process_v (int pipe_control,
1408 const SOCKTYPE *lsocks,
1409 const char *filename,
1412 return start_process (pipe_control,
1422 * Retrieve the status of a process, waiting on him if dead.
1423 * Nonblocking version.
1425 * @param proc process ID
1426 * @param type status type
1427 * @param code return code/signal number
1428 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1431 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1432 enum GNUNET_OS_ProcessStatusType *type,
1433 unsigned long *code)
1439 GNUNET_assert (0 != proc);
1440 ret = waitpid (proc->pid, &status, WNOHANG);
1443 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1444 return GNUNET_SYSERR;
1448 *type = GNUNET_OS_PROCESS_RUNNING;
1452 if (proc->pid != ret)
1454 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1455 return GNUNET_SYSERR;
1457 if (WIFEXITED (status))
1459 *type = GNUNET_OS_PROCESS_EXITED;
1460 *code = WEXITSTATUS (status);
1462 else if (WIFSIGNALED (status))
1464 *type = GNUNET_OS_PROCESS_SIGNALED;
1465 *code = WTERMSIG (status);
1467 else if (WIFSTOPPED (status))
1469 *type = GNUNET_OS_PROCESS_SIGNALED;
1470 *code = WSTOPSIG (status);
1473 else if (WIFCONTINUED (status))
1475 *type = GNUNET_OS_PROCESS_RUNNING;
1481 *type = GNUNET_OS_PROCESS_UNKNOWN;
1486 DWORD c, error_code, ret;
1490 if (h == NULL || ret == 0)
1492 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1494 return GNUNET_SYSERR;
1497 h = GetCurrentProcess ();
1500 ret = GetExitCodeProcess (h, &c);
1501 error_code = GetLastError ();
1502 if (ret == 0 || error_code != NO_ERROR)
1504 SetErrnoFromWinError (error_code);
1505 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1506 return GNUNET_SYSERR;
1508 if (STILL_ACTIVE == c)
1510 *type = GNUNET_OS_PROCESS_RUNNING;
1514 *type = GNUNET_OS_PROCESS_EXITED;
1523 * Wait for a process
1524 * @param proc pointer to process structure
1525 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1528 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1532 pid_t pid = proc->pid;
1535 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1536 (EINTR == errno) ) ;
1539 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1540 return GNUNET_SYSERR;
1550 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1552 return GNUNET_SYSERR;
1555 h = GetCurrentProcess ();
1557 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1559 SetErrnoFromWinError (GetLastError ());
1560 ret = GNUNET_SYSERR;
1571 * Handle to a command.
1573 struct GNUNET_OS_CommandHandle
1579 struct GNUNET_OS_Process *eip;
1582 * Handle to the output pipe.
1584 struct GNUNET_DISK_PipeHandle *opipe;
1587 * Read-end of output pipe.
1589 const struct GNUNET_DISK_FileHandle *r;
1592 * Function to call on each line of output.
1594 GNUNET_OS_LineProcessor proc;
1597 * Closure for 'proc'.
1602 * Buffer for the output.
1607 * Task reading from pipe.
1609 GNUNET_SCHEDULER_TaskIdentifier rtask;
1614 struct GNUNET_TIME_Absolute timeout;
1617 * Current read offset in buf.
1624 * Stop/kill a command. Must ONLY be called either from
1625 * the callback after 'NULL' was passed for 'line' *OR*
1626 * from an independent task (not within the line processor).
1628 * @param cmd handle to the process
1631 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1634 if (cmd->proc != NULL)
1636 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1637 GNUNET_SCHEDULER_cancel (cmd->rtask);
1639 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1640 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1641 GNUNET_OS_process_destroy (cmd->eip);
1642 GNUNET_DISK_pipe_close (cmd->opipe);
1648 * Read from the process and call the line processor.
1650 * @param cls the 'struct GNUNET_OS_CommandHandle'
1651 * @param tc scheduler context
1654 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1656 struct GNUNET_OS_CommandHandle *cmd = cls;
1657 GNUNET_OS_LineProcessor proc;
1661 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1662 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1664 /* timeout, shutdown, etc. */
1667 proc (cmd->proc_cls, NULL);
1671 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1672 sizeof (cmd->buf) - cmd->off);
1675 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1677 cmd->buf[cmd->off] = '\0';
1678 cmd->proc (cmd->proc_cls, cmd->buf);
1682 proc (cmd->proc_cls, NULL);
1685 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1690 cmd->proc (cmd->proc_cls, cmd->buf);
1691 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1692 cmd->off -= (end + 1 - cmd->buf);
1693 end = memchr (cmd->buf, '\n', cmd->off);
1696 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1697 (cmd->timeout), cmd->r, &cmd_read, cmd);
1702 * Run the given command line and call the given function
1703 * for each line of the output.
1705 * @param proc function to call for each line of the output
1706 * @param proc_cls closure for proc
1707 * @param timeout when to time out
1708 * @param binary command to run
1709 * @param ... arguments to command
1710 * @return NULL on error
1712 struct GNUNET_OS_CommandHandle *
1713 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1714 struct GNUNET_TIME_Relative timeout, const char *binary,
1717 struct GNUNET_OS_CommandHandle *cmd;
1718 struct GNUNET_OS_Process *eip;
1719 struct GNUNET_DISK_PipeHandle *opipe;
1722 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1725 va_start (ap, binary);
1726 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1730 GNUNET_DISK_pipe_close (opipe);
1733 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1734 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1735 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1739 cmd->proc_cls = proc_cls;
1740 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1741 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1748 /* end of os_priority.c */