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;
966 HANDLE stdih, stdoh, stdeh;
967 DWORD stdif, stdof, stdef;
969 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
970 return NULL; /* not executable */
972 /* Search in prefix dir (hopefully - the directory from which
973 * the current module was loaded), bindir and libdir, then in PATH
975 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
976 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
977 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
979 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
982 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
985 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
988 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
989 GNUNET_free (self_prefix);
990 GNUNET_free (bindir);
991 GNUNET_free (libdir);
993 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
994 if (alloc_len != pathbuf_len - 1)
996 GNUNET_free (pathbuf);
997 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1001 cmdlen = strlen (filename);
1002 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1003 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1005 GNUNET_asprintf (&non_const_filename, "%s", filename);
1007 /* It could be in POSIX form, convert it to a DOS path early on */
1008 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1010 SetErrnoFromWinError (lRet);
1011 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1012 non_const_filename);
1013 GNUNET_free (non_const_filename);
1014 GNUNET_free (pathbuf);
1017 GNUNET_free (non_const_filename);
1018 non_const_filename = GNUNET_strdup (win_path);
1019 /* Check that this is the full path. If it isn't, search. */
1020 /* FIXME: convert it to wchar_t and use SearchPathW?
1021 * Remember: arguments to _start_process() are technically in UTF-8...
1023 if (non_const_filename[1] == ':')
1024 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1025 else if (!SearchPathA
1026 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1029 SetErrnoFromWinError (GetLastError ());
1030 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1031 non_const_filename);
1032 GNUNET_free (non_const_filename);
1033 GNUNET_free (pathbuf);
1036 GNUNET_free (pathbuf);
1037 GNUNET_free (non_const_filename);
1039 /* Count the number of arguments */
1040 arg = (char **) argv;
1047 /* Allocate a copy argv */
1048 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1050 /* Copy all argv strings */
1052 arg = (char **) argv;
1056 non_const_argv[argcount] = GNUNET_strdup (path);
1058 non_const_argv[argcount] = GNUNET_strdup (*arg);
1062 non_const_argv[argcount] = NULL;
1066 arg = non_const_argv;
1069 cmdlen = cmdlen + strlen (*arg) + 4;
1073 /* Allocate and create cmd */
1074 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1075 arg = non_const_argv;
1078 char arg_last_char = (*arg)[strlen (*arg) - 1];
1079 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1080 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1084 while (argcount > 0)
1085 GNUNET_free (non_const_argv[--argcount]);
1086 GNUNET_free (non_const_argv);
1088 memset (&start, 0, sizeof (start));
1089 start.cb = sizeof (start);
1090 start.dwFlags |= STARTF_USESTDHANDLES;
1092 if (pipe_stdin != NULL)
1094 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1095 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1096 &stdin_handle, sizeof (HANDLE));
1097 start.hStdInput = stdin_handle;
1100 if (pipe_stdout != NULL)
1102 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1104 GNUNET_DISK_PIPE_END_WRITE),
1105 &stdout_handle, sizeof (HANDLE));
1106 start.hStdOutput = stdout_handle;
1109 if (GNUNET_YES == pipe_control)
1112 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1113 GNUNET_DISK_PERM_USER_READ |
1114 GNUNET_DISK_PERM_USER_WRITE);
1115 if (control_pipe == NULL)
1123 control_pipe = NULL;
1124 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1126 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1128 if (lsocks_pipe == NULL)
1132 GNUNET_DISK_pipe_close (lsocks_pipe);
1135 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1136 GNUNET_DISK_PIPE_END_WRITE);
1137 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1138 &lsocks_write, sizeof (HANDLE));
1139 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1140 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1141 &lsocks_read, sizeof (HANDLE));
1145 if (NULL != childpipename)
1147 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1149 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1150 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1151 GNUNET_free (childpipename);
1153 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1155 /*This will tell the child that we're going to send lsocks over the pipe*/
1156 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1157 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1159 our_env[env_off++] = NULL;
1160 env_block = CreateCustomEnvTable (our_env);
1162 GNUNET_free_non_null (our_env[--env_off]);
1165 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1167 LOG (GNUNET_ERROR_TYPE_DEBUG,
1168 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1169 GNUNET_free (env_block);
1175 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1177 LOG (GNUNET_ERROR_TYPE_DEBUG,
1178 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1179 GNUNET_free (env_block);
1185 /* Prevents our std handles from being inherited by the child */
1186 stdih = GetStdHandle (STD_INPUT_HANDLE);
1187 stdoh = GetStdHandle (STD_OUTPUT_HANDLE);
1188 stdeh = GetStdHandle (STD_ERROR_HANDLE);
1191 GetHandleInformation (stdih, &stdif);
1192 SetHandleInformation (stdih, HANDLE_FLAG_INHERIT, 0);
1196 GetHandleInformation (stdoh, &stdof);
1197 SetHandleInformation (stdoh, HANDLE_FLAG_INHERIT, 0);
1201 GetHandleInformation (stdeh, &stdef);
1202 SetHandleInformation (stdeh, HANDLE_FLAG_INHERIT, 0);
1205 bresult = CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1206 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc);
1207 error_code = GetLastError ();
1210 SetHandleInformation (stdih, stdif, stdif);
1212 SetHandleInformation (stdoh, stdof, stdof);
1214 SetHandleInformation (stdeh, stdef, stdef);
1216 GNUNET_free (env_block);
1223 SetErrnoFromWinError (error_code);
1224 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1226 if (NULL != control_pipe)
1227 GNUNET_DISK_file_close (control_pipe);
1229 GNUNET_DISK_pipe_close (lsocks_pipe);
1233 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1234 gnunet_proc->pid = proc.dwProcessId;
1235 gnunet_proc->handle = proc.hProcess;
1236 gnunet_proc->control_pipe = control_pipe;
1238 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1240 ResumeThread (proc.hThread);
1241 CloseHandle (proc.hThread);
1243 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1246 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1248 /* This is a replacement for "goto error" that doesn't use goto */
1253 uint64_t size, count, i;
1255 /* Tell the number of sockets */
1256 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1258 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1259 if (wrote != sizeof (count))
1261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1264 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1266 WSAPROTOCOL_INFOA pi;
1267 /* Get a socket duplication info */
1268 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1271 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1274 /* Synchronous I/O is not nice, but we can't schedule this:
1275 * lsocks will be closed/freed by the caller soon, and until
1276 * the child creates a duplicate, closing a socket here will
1277 * close it for good.
1279 /* Send the size of the structure
1280 * (the child might be built with different headers...)
1283 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1284 if (wrote != sizeof (size))
1286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1289 /* Finally! Send the data */
1290 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1291 if (wrote != sizeof (pi))
1293 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1297 /* This will block us until the child makes a final read or closes
1298 * the pipe (hence no 'wrote' check), since we have to wait for it
1299 * to duplicate the last socket, before we return and start closing
1302 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1307 GNUNET_DISK_file_sync (lsocks_write_fd);
1308 GNUNET_DISK_pipe_close (lsocks_pipe);
1312 /* If we can't pass on the socket(s), the child will block forever,
1313 * better put it out of its misery.
1315 TerminateProcess (gnunet_proc->handle, 0);
1316 CloseHandle (gnunet_proc->handle);
1317 if (NULL != gnunet_proc->control_pipe)
1318 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1319 GNUNET_free (gnunet_proc);
1332 * @param pipe_control should a pipe be used to send signals to the child?
1333 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1334 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1335 * @param filename name of the binary
1336 * @param argv NULL-terminated array of arguments to the process
1337 * @return pointer to process structure of the new process, NULL on error
1339 struct GNUNET_OS_Process *
1340 GNUNET_OS_start_process_vap (int pipe_control,
1341 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1342 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1343 const char *filename,
1346 return start_process (pipe_control,
1358 * @param pipe_control should a pipe be used to send signals to the child?
1359 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1360 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1361 * @param filename name of the binary
1362 * @param va NULL-terminated list of arguments to the process
1363 * @return pointer to process structure of the new process, NULL on error
1365 struct GNUNET_OS_Process *
1366 GNUNET_OS_start_process_va (int pipe_control,
1367 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1368 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1369 const char *filename, va_list va)
1371 struct GNUNET_OS_Process *ret;
1378 while (NULL != va_arg (ap, char *))
1381 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1384 while (NULL != (argv[argc] = va_arg (ap, char *)))
1387 ret = GNUNET_OS_start_process_vap (pipe_control,
1401 * @param pipe_control should a pipe be used to send signals to the child?
1402 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1403 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1404 * @param filename name of the binary
1405 * @param ... NULL-terminated list of arguments to the process
1407 * @return pointer to process structure of the new process, NULL on error
1410 struct GNUNET_OS_Process *
1411 GNUNET_OS_start_process (int pipe_control,
1412 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1413 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1414 const char *filename, ...)
1416 struct GNUNET_OS_Process *ret;
1419 va_start (ap, filename);
1420 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1429 * @param pipe_control should a pipe be used to send signals to the child?
1430 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1431 * must be NULL on platforms where dup is not supported
1432 * @param filename name of the binary
1433 * @param argv NULL-terminated list of arguments to the process
1434 * @return process ID of the new process, -1 on error
1436 struct GNUNET_OS_Process *
1437 GNUNET_OS_start_process_v (int pipe_control,
1438 const SOCKTYPE *lsocks,
1439 const char *filename,
1442 return start_process (pipe_control,
1452 * Retrieve the status of a process, waiting on him if dead.
1453 * Nonblocking version.
1455 * @param proc process ID
1456 * @param type status type
1457 * @param code return code/signal number
1458 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1461 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1462 enum GNUNET_OS_ProcessStatusType *type,
1463 unsigned long *code)
1469 GNUNET_assert (0 != proc);
1470 ret = waitpid (proc->pid, &status, WNOHANG);
1473 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1474 return GNUNET_SYSERR;
1478 *type = GNUNET_OS_PROCESS_RUNNING;
1482 if (proc->pid != ret)
1484 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1485 return GNUNET_SYSERR;
1487 if (WIFEXITED (status))
1489 *type = GNUNET_OS_PROCESS_EXITED;
1490 *code = WEXITSTATUS (status);
1492 else if (WIFSIGNALED (status))
1494 *type = GNUNET_OS_PROCESS_SIGNALED;
1495 *code = WTERMSIG (status);
1497 else if (WIFSTOPPED (status))
1499 *type = GNUNET_OS_PROCESS_SIGNALED;
1500 *code = WSTOPSIG (status);
1503 else if (WIFCONTINUED (status))
1505 *type = GNUNET_OS_PROCESS_RUNNING;
1511 *type = GNUNET_OS_PROCESS_UNKNOWN;
1516 DWORD c, error_code, ret;
1520 if (h == NULL || ret == 0)
1522 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1524 return GNUNET_SYSERR;
1527 h = GetCurrentProcess ();
1530 ret = GetExitCodeProcess (h, &c);
1531 error_code = GetLastError ();
1532 if (ret == 0 || error_code != NO_ERROR)
1534 SetErrnoFromWinError (error_code);
1535 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1536 return GNUNET_SYSERR;
1538 if (STILL_ACTIVE == c)
1540 *type = GNUNET_OS_PROCESS_RUNNING;
1544 *type = GNUNET_OS_PROCESS_EXITED;
1553 * Wait for a process
1554 * @param proc pointer to process structure
1555 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1558 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1562 pid_t pid = proc->pid;
1565 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1566 (EINTR == errno) ) ;
1569 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1570 return GNUNET_SYSERR;
1580 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1582 return GNUNET_SYSERR;
1585 h = GetCurrentProcess ();
1587 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1589 SetErrnoFromWinError (GetLastError ());
1590 ret = GNUNET_SYSERR;
1601 * Handle to a command.
1603 struct GNUNET_OS_CommandHandle
1609 struct GNUNET_OS_Process *eip;
1612 * Handle to the output pipe.
1614 struct GNUNET_DISK_PipeHandle *opipe;
1617 * Read-end of output pipe.
1619 const struct GNUNET_DISK_FileHandle *r;
1622 * Function to call on each line of output.
1624 GNUNET_OS_LineProcessor proc;
1627 * Closure for 'proc'.
1632 * Buffer for the output.
1637 * Task reading from pipe.
1639 GNUNET_SCHEDULER_TaskIdentifier rtask;
1644 struct GNUNET_TIME_Absolute timeout;
1647 * Current read offset in buf.
1654 * Stop/kill a command. Must ONLY be called either from
1655 * the callback after 'NULL' was passed for 'line' *OR*
1656 * from an independent task (not within the line processor).
1658 * @param cmd handle to the process
1661 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1664 if (cmd->proc != NULL)
1666 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1667 GNUNET_SCHEDULER_cancel (cmd->rtask);
1669 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1670 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1671 GNUNET_OS_process_destroy (cmd->eip);
1672 GNUNET_DISK_pipe_close (cmd->opipe);
1678 * Read from the process and call the line processor.
1680 * @param cls the 'struct GNUNET_OS_CommandHandle'
1681 * @param tc scheduler context
1684 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1686 struct GNUNET_OS_CommandHandle *cmd = cls;
1687 GNUNET_OS_LineProcessor proc;
1691 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1692 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1694 /* timeout, shutdown, etc. */
1697 proc (cmd->proc_cls, NULL);
1701 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1702 sizeof (cmd->buf) - cmd->off);
1705 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1707 cmd->buf[cmd->off] = '\0';
1708 cmd->proc (cmd->proc_cls, cmd->buf);
1712 proc (cmd->proc_cls, NULL);
1715 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1720 cmd->proc (cmd->proc_cls, cmd->buf);
1721 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1722 cmd->off -= (end + 1 - cmd->buf);
1723 end = memchr (cmd->buf, '\n', cmd->off);
1726 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1727 (cmd->timeout), cmd->r, &cmd_read, cmd);
1732 * Run the given command line and call the given function
1733 * for each line of the output.
1735 * @param proc function to call for each line of the output
1736 * @param proc_cls closure for proc
1737 * @param timeout when to time out
1738 * @param binary command to run
1739 * @param ... arguments to command
1740 * @return NULL on error
1742 struct GNUNET_OS_CommandHandle *
1743 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1744 struct GNUNET_TIME_Relative timeout, const char *binary,
1747 struct GNUNET_OS_CommandHandle *cmd;
1748 struct GNUNET_OS_Process *eip;
1749 struct GNUNET_DISK_PipeHandle *opipe;
1752 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1755 va_start (ap, binary);
1756 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1760 GNUNET_DISK_pipe_close (opipe);
1763 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1764 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1765 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1769 cmd->proc_cls = proc_cls;
1770 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1771 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1778 /* end of os_priority.c */