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__)
458 if (0 == TerminateProcess (proc->handle, 0))
460 /* FIXME: set 'errno' */
465 return PLIBC_KILL (proc->pid, sig);
472 return PLIBC_KILL (proc->pid, sig);
478 * Get the pid of the process in question
480 * @param proc the process to get the pid of
482 * @return the current process id
485 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
492 * Cleans up process structure contents (OS-dependent) and deallocates it
494 * @param proc pointer to process structure
497 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
499 if (NULL != proc->control_pipe)
500 GNUNET_DISK_file_close (proc->control_pipe);
503 if (proc->handle != NULL)
504 CloseHandle (proc->handle);
506 if (NULL != proc->childpipename)
509 cleanup_npipe (proc->childpipename);
511 GNUNET_free (proc->childpipename);
518 #include "gnunet_signal_lib.h"
520 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
523 * Make seaspider happy.
525 #define DWORD_WINAPI DWORD WINAPI
528 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
529 * @param proc pointer to process structure
532 child_wait_thread (void *arg)
534 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
536 WaitForSingleObject (proc->handle, INFINITE);
538 if (w32_sigchld_handler)
539 w32_sigchld_handler ();
547 * Set process priority
549 * @param proc pointer to process structure
550 * @param prio priority value
551 * @return GNUNET_OK on success, GNUNET_SYSERR on error
554 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
555 enum GNUNET_SCHEDULER_Priority prio)
559 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
560 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
563 /* convert to MINGW/Unix values */
566 case GNUNET_SCHEDULER_PRIORITY_UI:
567 case GNUNET_SCHEDULER_PRIORITY_URGENT:
569 rprio = HIGH_PRIORITY_CLASS;
575 case GNUNET_SCHEDULER_PRIORITY_HIGH:
577 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
583 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
585 rprio = NORMAL_PRIORITY_CLASS;
591 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
593 rprio = BELOW_NORMAL_PRIORITY_CLASS;
599 case GNUNET_SCHEDULER_PRIORITY_IDLE:
601 rprio = IDLE_PRIORITY_CLASS;
608 return GNUNET_SYSERR;
611 /* Set process priority */
614 HANDLE h = proc->handle;
616 GNUNET_assert (h != NULL);
617 SetPriorityClass (h, rprio);
623 if ((0 == pid) || (pid == getpid ()))
626 int delta = rprio - have;
629 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
631 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
632 return GNUNET_SYSERR;
637 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
639 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
641 return GNUNET_SYSERR;
645 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
646 "Priority management not availabe for this platform\n");
654 CreateCustomEnvTable (char **vars)
656 char *win32_env_table;
661 size_t tablesize = 0;
662 size_t items_count = 0;
671 win32_env_table = GetEnvironmentStringsA ();
672 if (win32_env_table == NULL)
674 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
676 index = GNUNET_malloc (sizeof (char *) * n_var);
677 for (c = 0; c < n_var; c++)
679 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
681 size_t len = strlen (ptr);
684 for (var_ptr = vars; *var_ptr; var_ptr++)
688 var_len = strlen (var);
689 if (strncmp (var, ptr, var_len) == 0)
693 tablesize += var_len + strlen (val) + 1;
698 tablesize += len + 1;
701 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
706 n_found += strlen (var) + strlen (val) + 1;
708 result = GNUNET_malloc (tablesize + n_found + 1);
709 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
711 size_t len = strlen (ptr);
714 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
718 var_len = strlen (var);
719 if (strncmp (var, ptr, var_len) == 0)
727 strcpy (result_ptr, ptr);
728 result_ptr += len + 1;
732 strcpy (result_ptr, var);
733 result_ptr += var_len;
734 strcpy (result_ptr, val);
735 result_ptr += strlen (val) + 1;
739 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
743 var_len = strlen (var);
746 strcpy (result_ptr, var);
747 result_ptr += var_len;
748 strcpy (result_ptr, val);
749 result_ptr += strlen (val) + 1;
752 FreeEnvironmentStrings (win32_env_table);
763 * @param pipe_control should a pipe be used to send signals to the child?
764 * @param pipe_stdin pipe to use to send input to child process (or NULL)
765 * @param pipe_stdout pipe to use to get output from child process (or NULL)
766 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
767 * must be NULL on platforms where dup is not supported
768 * @param filename name of the binary
769 * @param argv NULL-terminated list of arguments to the process
770 * @return process ID of the new process, -1 on error
772 static struct GNUNET_OS_Process *
773 start_process (int pipe_control,
774 struct GNUNET_DISK_PipeHandle *pipe_stdin,
775 struct GNUNET_DISK_PipeHandle *pipe_stdout,
776 const SOCKTYPE *lsocks,
777 const char *filename,
784 struct GNUNET_OS_Process *gnunet_proc;
785 char *childpipename = NULL;
798 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
799 return NULL; /* not executable */
800 if ( (GNUNET_YES == pipe_control) &&
801 (GNUNET_OK != npipe_setup (&childpipename)) )
803 if (pipe_stdout != NULL)
805 GNUNET_assert (GNUNET_OK ==
806 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
808 GNUNET_DISK_PIPE_END_WRITE),
809 &fd_stdout_write, sizeof (int)));
810 GNUNET_assert (GNUNET_OK ==
811 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
812 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
813 &fd_stdout_read, sizeof (int)));
815 if (pipe_stdin != NULL)
817 GNUNET_assert (GNUNET_OK ==
818 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
819 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
820 &fd_stdin_read, sizeof (int)));
821 GNUNET_assert (GNUNET_OK ==
822 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
823 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
824 &fd_stdin_write, sizeof (int)));
831 while (-1 != (k = lsocks[i++]))
832 GNUNET_array_append (lscp, ls, k);
833 GNUNET_array_append (lscp, ls, -1);
840 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
841 GNUNET_free_non_null (childpipename);
842 GNUNET_array_grow (lscp, ls, 0);
848 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
849 gnunet_proc->pid = ret;
850 gnunet_proc->childpipename = childpipename;
851 GNUNET_array_grow (lscp, ls, 0);
854 if (NULL != childpipename)
856 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
857 GNUNET_free (childpipename);
859 if (pipe_stdout != NULL)
861 GNUNET_break (0 == close (fd_stdout_read));
862 if (-1 == dup2 (fd_stdout_write, 1))
863 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
864 GNUNET_break (0 == close (fd_stdout_write));
866 if (pipe_stdin != NULL)
869 GNUNET_break (0 == close (fd_stdin_write));
870 if (-1 == dup2 (fd_stdin_read, 0))
871 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
872 GNUNET_break (0 == close (fd_stdin_read));
876 /* read systemd documentation... */
877 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
878 setenv ("LISTEN_PID", lpid, 1);
881 while (-1 != lscp[i])
884 while (-1 != lscp[j])
890 GNUNET_assert (-1 != k);
891 GNUNET_assert (0 == close (lscp[j]));
899 /* Bury any existing FD, no matter what; they should all be closed
900 * on exec anyway and the important onces have been dup'ed away */
902 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
904 /* unset close-on-exec flag */
905 flags = fcntl (tgt, F_GETFD);
906 GNUNET_assert (flags >= 0);
907 flags &= ~FD_CLOEXEC;
909 (void) fcntl (tgt, F_SETFD, flags);
913 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
914 setenv ("LISTEN_FDS", fds, 1);
916 GNUNET_array_grow (lscp, ls, 0);
917 execvp (filename, argv);
918 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
921 struct GNUNET_DISK_FileHandle *control_pipe;
922 char *childpipename = NULL;
924 char **non_const_argv;
929 PROCESS_INFORMATION proc;
931 struct GNUNET_OS_Process *gnunet_proc;
932 char path[MAX_PATH + 1];
933 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
934 char *env_block = NULL;
942 char *non_const_filename;
943 char win_path[MAX_PATH + 1];
944 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
945 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
956 HANDLE stdout_handle;
958 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary (filename))
959 return NULL; /* not executable */
961 /* Search in prefix dir (hopefully - the directory from which
962 * the current module was loaded), bindir and libdir, then in PATH
964 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
965 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
966 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
968 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
971 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
974 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
977 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
978 GNUNET_free (self_prefix);
979 GNUNET_free (bindir);
980 GNUNET_free (libdir);
982 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
983 if (alloc_len != pathbuf_len - 1)
985 GNUNET_free (pathbuf);
986 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
990 cmdlen = strlen (filename);
991 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
992 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
994 GNUNET_asprintf (&non_const_filename, "%s", filename);
996 /* It could be in POSIX form, convert it to a DOS path early on */
997 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
999 SetErrnoFromWinError (lRet);
1000 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1001 non_const_filename);
1002 GNUNET_free (non_const_filename);
1003 GNUNET_free (pathbuf);
1006 GNUNET_free (non_const_filename);
1007 non_const_filename = GNUNET_strdup (win_path);
1008 /* Check that this is the full path. If it isn't, search. */
1009 /* FIXME: convert it to wchar_t and use SearchPathW?
1010 * Remember: arguments to _start_process() are technically in UTF-8...
1012 if (non_const_filename[1] == ':')
1013 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1014 else if (!SearchPathA
1015 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1018 SetErrnoFromWinError (GetLastError ());
1019 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1020 non_const_filename);
1021 GNUNET_free (non_const_filename);
1022 GNUNET_free (pathbuf);
1025 GNUNET_free (pathbuf);
1026 GNUNET_free (non_const_filename);
1028 /* Count the number of arguments */
1029 arg = (char **) argv;
1036 /* Allocate a copy argv */
1037 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1039 /* Copy all argv strings */
1041 arg = (char **) argv;
1045 non_const_argv[argcount] = GNUNET_strdup (path);
1047 non_const_argv[argcount] = GNUNET_strdup (*arg);
1051 non_const_argv[argcount] = NULL;
1055 arg = non_const_argv;
1058 cmdlen = cmdlen + strlen (*arg) + 4;
1062 /* Allocate and create cmd */
1063 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1064 arg = non_const_argv;
1067 char arg_last_char = (*arg)[strlen (*arg) - 1];
1068 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1069 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1073 while (argcount > 0)
1074 GNUNET_free (non_const_argv[--argcount]);
1075 GNUNET_free (non_const_argv);
1077 memset (&start, 0, sizeof (start));
1078 start.cb = sizeof (start);
1079 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
1080 start.dwFlags |= STARTF_USESTDHANDLES;
1082 if (pipe_stdin != NULL)
1084 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1085 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1086 &stdin_handle, sizeof (HANDLE));
1087 start.hStdInput = stdin_handle;
1090 if (pipe_stdout != NULL)
1092 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1094 GNUNET_DISK_PIPE_END_WRITE),
1095 &stdout_handle, sizeof (HANDLE));
1096 start.hStdOutput = stdout_handle;
1099 if (GNUNET_YES == pipe_control)
1102 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1103 GNUNET_DISK_PERM_USER_READ |
1104 GNUNET_DISK_PERM_USER_WRITE);
1105 if (control_pipe == NULL)
1113 control_pipe = NULL;
1114 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1116 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1118 if (lsocks_pipe == NULL)
1122 GNUNET_DISK_pipe_close (lsocks_pipe);
1125 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1126 GNUNET_DISK_PIPE_END_WRITE);
1127 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1128 &lsocks_write, sizeof (HANDLE));
1129 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1130 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1131 &lsocks_read, sizeof (HANDLE));
1135 if (NULL != childpipename)
1137 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1139 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1140 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1141 GNUNET_free (childpipename);
1143 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1145 /*This will tell the child that we're going to send lsocks over the pipe*/
1146 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1147 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1149 our_env[env_off++] = NULL;
1150 env_block = CreateCustomEnvTable (our_env);
1152 GNUNET_free_non_null (our_env[--env_off]);
1155 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1157 LOG (GNUNET_ERROR_TYPE_DEBUG,
1158 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1159 GNUNET_free (env_block);
1165 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1167 LOG (GNUNET_ERROR_TYPE_DEBUG,
1168 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1169 GNUNET_free (env_block);
1175 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1176 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1178 SetErrnoFromWinError (GetLastError ());
1179 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1180 if (NULL != control_pipe)
1181 GNUNET_DISK_file_close (control_pipe);
1183 GNUNET_DISK_pipe_close (lsocks_pipe);
1184 GNUNET_free (env_block);
1191 GNUNET_free (env_block);
1193 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1194 gnunet_proc->pid = proc.dwProcessId;
1195 gnunet_proc->handle = proc.hProcess;
1196 gnunet_proc->control_pipe = control_pipe;
1198 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1200 ResumeThread (proc.hThread);
1201 CloseHandle (proc.hThread);
1206 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1209 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1211 /* This is a replacement for "goto error" that doesn't use goto */
1216 uint64_t size, count, i;
1218 /* Tell the number of sockets */
1219 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1221 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1222 if (wrote != sizeof (count))
1224 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1227 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1229 WSAPROTOCOL_INFOA pi;
1230 /* Get a socket duplication info */
1231 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1233 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1234 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1237 /* Synchronous I/O is not nice, but we can't schedule this:
1238 * lsocks will be closed/freed by the caller soon, and until
1239 * the child creates a duplicate, closing a socket here will
1240 * close it for good.
1242 /* Send the size of the structure
1243 * (the child might be built with different headers...)
1246 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1247 if (wrote != sizeof (size))
1249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1252 /* Finally! Send the data */
1253 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1254 if (wrote != sizeof (pi))
1256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1260 /* This will block us until the child makes a final read or closes
1261 * the pipe (hence no 'wrote' check), since we have to wait for it
1262 * to duplicate the last socket, before we return and start closing
1265 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1270 GNUNET_DISK_file_sync (lsocks_write_fd);
1271 GNUNET_DISK_pipe_close (lsocks_pipe);
1275 /* If we can't pass on the socket(s), the child will block forever,
1276 * better put it out of its misery.
1278 TerminateProcess (gnunet_proc->handle, 0);
1279 CloseHandle (gnunet_proc->handle);
1280 if (NULL != gnunet_proc->control_pipe)
1281 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1282 GNUNET_free (gnunet_proc);
1295 * @param pipe_control should a pipe be used to send signals to the child?
1296 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1297 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1298 * @param filename name of the binary
1299 * @param argv NULL-terminated array of arguments to the process
1300 * @return pointer to process structure of the new process, NULL on error
1302 struct GNUNET_OS_Process *
1303 GNUNET_OS_start_process_vap (int pipe_control,
1304 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1305 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1306 const char *filename,
1309 return start_process (pipe_control,
1321 * @param pipe_control should a pipe be used to send signals to the child?
1322 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1323 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1324 * @param filename name of the binary
1325 * @param va NULL-terminated list of arguments to the process
1326 * @return pointer to process structure of the new process, NULL on error
1328 struct GNUNET_OS_Process *
1329 GNUNET_OS_start_process_va (int pipe_control,
1330 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1331 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1332 const char *filename, va_list va)
1334 struct GNUNET_OS_Process *ret;
1341 while (NULL != va_arg (ap, char *))
1344 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1347 while (NULL != (argv[argc] = va_arg (ap, char *)))
1350 ret = GNUNET_OS_start_process_vap (pipe_control,
1364 * @param pipe_control should a pipe be used to send signals to the child?
1365 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1366 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1367 * @param filename name of the binary
1368 * @param ... NULL-terminated list of arguments to the process
1370 * @return pointer to process structure of the new process, NULL on error
1373 struct GNUNET_OS_Process *
1374 GNUNET_OS_start_process (int pipe_control,
1375 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1376 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1377 const char *filename, ...)
1379 struct GNUNET_OS_Process *ret;
1382 va_start (ap, filename);
1383 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1392 * @param pipe_control should a pipe be used to send signals to the child?
1393 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1394 * must be NULL on platforms where dup is not supported
1395 * @param filename name of the binary
1396 * @param argv NULL-terminated list of arguments to the process
1397 * @return process ID of the new process, -1 on error
1399 struct GNUNET_OS_Process *
1400 GNUNET_OS_start_process_v (int pipe_control,
1401 const SOCKTYPE *lsocks,
1402 const char *filename,
1405 return start_process (pipe_control,
1415 * Retrieve the status of a process, waiting on him if dead.
1416 * Nonblocking version.
1418 * @param proc process ID
1419 * @param type status type
1420 * @param code return code/signal number
1421 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1424 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1425 enum GNUNET_OS_ProcessStatusType *type,
1426 unsigned long *code)
1432 GNUNET_assert (0 != proc);
1433 ret = waitpid (proc->pid, &status, WNOHANG);
1436 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1437 return GNUNET_SYSERR;
1441 *type = GNUNET_OS_PROCESS_RUNNING;
1445 if (proc->pid != ret)
1447 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1448 return GNUNET_SYSERR;
1450 if (WIFEXITED (status))
1452 *type = GNUNET_OS_PROCESS_EXITED;
1453 *code = WEXITSTATUS (status);
1455 else if (WIFSIGNALED (status))
1457 *type = GNUNET_OS_PROCESS_SIGNALED;
1458 *code = WTERMSIG (status);
1460 else if (WIFSTOPPED (status))
1462 *type = GNUNET_OS_PROCESS_SIGNALED;
1463 *code = WSTOPSIG (status);
1466 else if (WIFCONTINUED (status))
1468 *type = GNUNET_OS_PROCESS_RUNNING;
1474 *type = GNUNET_OS_PROCESS_UNKNOWN;
1479 DWORD c, error_code, ret;
1483 if (h == NULL || ret == 0)
1485 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1487 return GNUNET_SYSERR;
1490 h = GetCurrentProcess ();
1493 ret = GetExitCodeProcess (h, &c);
1494 error_code = GetLastError ();
1495 if (ret == 0 || error_code != NO_ERROR)
1497 SetErrnoFromWinError (error_code);
1498 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1499 return GNUNET_SYSERR;
1501 if (STILL_ACTIVE == c)
1503 *type = GNUNET_OS_PROCESS_RUNNING;
1507 *type = GNUNET_OS_PROCESS_EXITED;
1516 * Wait for a process
1517 * @param proc pointer to process structure
1518 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1521 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1525 pid_t pid = proc->pid;
1528 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1529 (EINTR == errno) ) ;
1532 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1533 return GNUNET_SYSERR;
1543 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1545 return GNUNET_SYSERR;
1548 h = GetCurrentProcess ();
1550 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1552 SetErrnoFromWinError (GetLastError ());
1553 ret = GNUNET_SYSERR;
1564 * Handle to a command.
1566 struct GNUNET_OS_CommandHandle
1572 struct GNUNET_OS_Process *eip;
1575 * Handle to the output pipe.
1577 struct GNUNET_DISK_PipeHandle *opipe;
1580 * Read-end of output pipe.
1582 const struct GNUNET_DISK_FileHandle *r;
1585 * Function to call on each line of output.
1587 GNUNET_OS_LineProcessor proc;
1590 * Closure for 'proc'.
1595 * Buffer for the output.
1600 * Task reading from pipe.
1602 GNUNET_SCHEDULER_TaskIdentifier rtask;
1607 struct GNUNET_TIME_Absolute timeout;
1610 * Current read offset in buf.
1617 * Stop/kill a command. Must ONLY be called either from
1618 * the callback after 'NULL' was passed for 'line' *OR*
1619 * from an independent task (not within the line processor).
1621 * @param cmd handle to the process
1624 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1627 if (cmd->proc != NULL)
1629 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1630 GNUNET_SCHEDULER_cancel (cmd->rtask);
1632 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1633 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1634 GNUNET_OS_process_destroy (cmd->eip);
1635 GNUNET_DISK_pipe_close (cmd->opipe);
1641 * Read from the process and call the line processor.
1643 * @param cls the 'struct GNUNET_OS_CommandHandle'
1644 * @param tc scheduler context
1647 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1649 struct GNUNET_OS_CommandHandle *cmd = cls;
1650 GNUNET_OS_LineProcessor proc;
1654 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1655 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1657 /* timeout, shutdown, etc. */
1660 proc (cmd->proc_cls, NULL);
1664 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1665 sizeof (cmd->buf) - cmd->off);
1668 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1670 cmd->buf[cmd->off] = '\0';
1671 cmd->proc (cmd->proc_cls, cmd->buf);
1675 proc (cmd->proc_cls, NULL);
1678 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1683 cmd->proc (cmd->proc_cls, cmd->buf);
1684 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1685 cmd->off -= (end + 1 - cmd->buf);
1686 end = memchr (cmd->buf, '\n', cmd->off);
1689 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1690 (cmd->timeout), cmd->r, &cmd_read, cmd);
1695 * Run the given command line and call the given function
1696 * for each line of the output.
1698 * @param proc function to call for each line of the output
1699 * @param proc_cls closure for proc
1700 * @param timeout when to time out
1701 * @param binary command to run
1702 * @param ... arguments to command
1703 * @return NULL on error
1705 struct GNUNET_OS_CommandHandle *
1706 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1707 struct GNUNET_TIME_Relative timeout, const char *binary,
1710 struct GNUNET_OS_CommandHandle *cmd;
1711 struct GNUNET_OS_Process *eip;
1712 struct GNUNET_DISK_PipeHandle *opipe;
1715 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1718 va_start (ap, binary);
1719 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1723 GNUNET_DISK_pipe_close (opipe);
1726 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1727 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1728 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1732 cmd->proc_cls = proc_cls;
1733 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1734 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1741 /* end of os_priority.c */