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 = NULL;
785 char *childpipename = NULL;
798 if ( (GNUNET_YES == pipe_control) &&
799 (GNUNET_OK != npipe_setup (&childpipename)) )
801 if (pipe_stdout != NULL)
803 GNUNET_assert (GNUNET_OK ==
804 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
806 GNUNET_DISK_PIPE_END_WRITE),
807 &fd_stdout_write, sizeof (int)));
808 GNUNET_assert (GNUNET_OK ==
809 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
810 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
811 &fd_stdout_read, sizeof (int)));
813 if (pipe_stdin != NULL)
815 GNUNET_assert (GNUNET_OK ==
816 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
817 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
818 &fd_stdin_read, sizeof (int)));
819 GNUNET_assert (GNUNET_OK ==
820 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
821 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
822 &fd_stdin_write, sizeof (int)));
829 while (-1 != (k = lsocks[i++]))
830 GNUNET_array_append (lscp, ls, k);
831 GNUNET_array_append (lscp, ls, -1);
838 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
839 GNUNET_free_non_null (childpipename);
840 GNUNET_array_grow (lscp, ls, 0);
846 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
847 gnunet_proc->pid = ret;
848 gnunet_proc->childpipename = childpipename;
849 GNUNET_array_grow (lscp, ls, 0);
852 if (NULL != childpipename)
854 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
855 GNUNET_free (childpipename);
857 if (pipe_stdout != NULL)
859 GNUNET_break (0 == close (fd_stdout_read));
860 if (-1 == dup2 (fd_stdout_write, 1))
861 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
862 GNUNET_break (0 == close (fd_stdout_write));
864 if (pipe_stdin != NULL)
867 GNUNET_break (0 == close (fd_stdin_write));
868 if (-1 == dup2 (fd_stdin_read, 0))
869 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
870 GNUNET_break (0 == close (fd_stdin_read));
874 /* read systemd documentation... */
875 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
876 setenv ("LISTEN_PID", lpid, 1);
879 while (-1 != lscp[i])
882 while (-1 != lscp[j])
888 GNUNET_assert (-1 != k);
889 GNUNET_assert (0 == close (lscp[j]));
897 /* Bury any existing FD, no matter what; they should all be closed
898 * on exec anyway and the important onces have been dup'ed away */
900 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
902 /* unset close-on-exec flag */
903 flags = fcntl (tgt, F_GETFD);
904 GNUNET_assert (flags >= 0);
905 flags &= ~FD_CLOEXEC;
907 (void) fcntl (tgt, F_SETFD, flags);
911 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
912 setenv ("LISTEN_FDS", fds, 1);
914 GNUNET_array_grow (lscp, ls, 0);
915 execvp (filename, argv);
916 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
919 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
920 char *childpipename = NULL;
922 char **non_const_argv;
927 PROCESS_INFORMATION proc;
929 struct GNUNET_OS_Process *gnunet_proc = NULL;
930 char path[MAX_PATH + 1];
931 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
932 char *env_block = NULL;
940 char *non_const_filename;
941 char win_path[MAX_PATH + 1];
942 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
943 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
954 HANDLE stdout_handle;
956 /* Search in prefix dir (hopefully - the directory from which
957 * the current module was loaded), bindir and libdir, then in PATH
959 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
960 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
961 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
963 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
966 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
969 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
972 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
973 GNUNET_free (self_prefix);
974 GNUNET_free (bindir);
975 GNUNET_free (libdir);
977 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
978 if (alloc_len != pathbuf_len - 1)
980 GNUNET_free (pathbuf);
981 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
985 cmdlen = strlen (filename);
986 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
987 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
989 GNUNET_asprintf (&non_const_filename, "%s", filename);
991 /* It could be in POSIX form, convert it to a DOS path early on */
992 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
994 SetErrnoFromWinError (lRet);
995 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
997 GNUNET_free (non_const_filename);
998 GNUNET_free (pathbuf);
1001 GNUNET_free (non_const_filename);
1002 non_const_filename = GNUNET_strdup (win_path);
1003 /* Check that this is the full path. If it isn't, search. */
1004 /* FIXME: convert it to wchar_t and use SearchPathW?
1005 * Remember: arguments to _start_process() are technically in UTF-8...
1007 if (non_const_filename[1] == ':')
1008 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1009 else if (!SearchPathA
1010 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1013 SetErrnoFromWinError (GetLastError ());
1014 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1015 non_const_filename);
1016 GNUNET_free (non_const_filename);
1017 GNUNET_free (pathbuf);
1020 GNUNET_free (pathbuf);
1021 GNUNET_free (non_const_filename);
1023 /* Count the number of arguments */
1024 arg = (char **) argv;
1031 /* Allocate a copy argv */
1032 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1034 /* Copy all argv strings */
1036 arg = (char **) argv;
1040 non_const_argv[argcount] = GNUNET_strdup (path);
1042 non_const_argv[argcount] = GNUNET_strdup (*arg);
1046 non_const_argv[argcount] = NULL;
1050 arg = non_const_argv;
1053 cmdlen = cmdlen + strlen (*arg) + 4;
1057 /* Allocate and create cmd */
1058 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1059 arg = non_const_argv;
1062 char arg_last_char = (*arg)[strlen (*arg) - 1];
1063 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1064 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1068 while (argcount > 0)
1069 GNUNET_free (non_const_argv[--argcount]);
1070 GNUNET_free (non_const_argv);
1072 memset (&start, 0, sizeof (start));
1073 start.cb = sizeof (start);
1074 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
1075 start.dwFlags |= STARTF_USESTDHANDLES;
1077 if (pipe_stdin != NULL)
1079 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1080 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
1081 &stdin_handle, sizeof (HANDLE));
1082 start.hStdInput = stdin_handle;
1085 if (pipe_stdout != NULL)
1087 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1089 GNUNET_DISK_PIPE_END_WRITE),
1090 &stdout_handle, sizeof (HANDLE));
1091 start.hStdOutput = stdout_handle;
1094 if (GNUNET_YES == pipe_control)
1097 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1098 GNUNET_DISK_PERM_USER_READ |
1099 GNUNET_DISK_PERM_USER_WRITE);
1100 if (control_pipe == NULL)
1108 control_pipe = NULL;
1109 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1111 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1113 if (lsocks_pipe == NULL)
1117 GNUNET_DISK_pipe_close (lsocks_pipe);
1120 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1121 GNUNET_DISK_PIPE_END_WRITE);
1122 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1123 &lsocks_write, sizeof (HANDLE));
1124 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1125 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1126 &lsocks_read, sizeof (HANDLE));
1130 if (NULL != childpipename)
1132 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1134 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1135 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1136 GNUNET_free (childpipename);
1138 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1140 /*This will tell the child that we're going to send lsocks over the pipe*/
1141 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1142 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1144 our_env[env_off++] = NULL;
1145 env_block = CreateCustomEnvTable (our_env);
1147 GNUNET_free_non_null (our_env[--env_off]);
1150 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1152 LOG (GNUNET_ERROR_TYPE_DEBUG,
1153 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1154 GNUNET_free (env_block);
1160 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1162 LOG (GNUNET_ERROR_TYPE_DEBUG,
1163 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1164 GNUNET_free (env_block);
1170 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1171 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1173 SetErrnoFromWinError (GetLastError ());
1174 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1175 if (NULL != control_pipe)
1176 GNUNET_DISK_file_close (control_pipe);
1178 GNUNET_DISK_pipe_close (lsocks_pipe);
1179 GNUNET_free (env_block);
1186 GNUNET_free (env_block);
1188 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1189 gnunet_proc->pid = proc.dwProcessId;
1190 gnunet_proc->handle = proc.hProcess;
1191 gnunet_proc->control_pipe = control_pipe;
1193 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1195 ResumeThread (proc.hThread);
1196 CloseHandle (proc.hThread);
1201 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1204 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1206 /* This is a replacement for "goto error" that doesn't use goto */
1211 uint64_t size, count, i;
1213 /* Tell the number of sockets */
1214 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1216 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1217 if (wrote != sizeof (count))
1219 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1222 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1224 WSAPROTOCOL_INFOA pi;
1225 /* Get a socket duplication info */
1226 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1229 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1232 /* Synchronous I/O is not nice, but we can't schedule this:
1233 * lsocks will be closed/freed by the caller soon, and until
1234 * the child creates a duplicate, closing a socket here will
1235 * close it for good.
1237 /* Send the size of the structure
1238 * (the child might be built with different headers...)
1241 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1242 if (wrote != sizeof (size))
1244 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1247 /* Finally! Send the data */
1248 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1249 if (wrote != sizeof (pi))
1251 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1255 /* This will block us until the child makes a final read or closes
1256 * the pipe (hence no 'wrote' check), since we have to wait for it
1257 * to duplicate the last socket, before we return and start closing
1260 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1265 GNUNET_DISK_file_sync (lsocks_write_fd);
1266 GNUNET_DISK_pipe_close (lsocks_pipe);
1270 /* If we can't pass on the socket(s), the child will block forever,
1271 * better put it out of its misery.
1273 TerminateProcess (gnunet_proc->handle, 0);
1274 CloseHandle (gnunet_proc->handle);
1275 if (NULL != gnunet_proc->control_pipe)
1276 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1277 GNUNET_free (gnunet_proc);
1290 * @param pipe_control should a pipe be used to send signals to the child?
1291 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1292 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1293 * @param filename name of the binary
1294 * @param argv NULL-terminated array of arguments to the process
1295 * @return pointer to process structure of the new process, NULL on error
1297 struct GNUNET_OS_Process *
1298 GNUNET_OS_start_process_vap (int pipe_control,
1299 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1300 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1301 const char *filename,
1304 return start_process (pipe_control,
1316 * @param pipe_control should a pipe be used to send signals to the child?
1317 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1318 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1319 * @param filename name of the binary
1320 * @param va NULL-terminated list of arguments to the process
1321 * @return pointer to process structure of the new process, NULL on error
1323 struct GNUNET_OS_Process *
1324 GNUNET_OS_start_process_va (int pipe_control,
1325 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1326 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1327 const char *filename, va_list va)
1329 struct GNUNET_OS_Process *ret;
1336 while (NULL != va_arg (ap, char *))
1339 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1342 while (NULL != (argv[argc] = va_arg (ap, char *)))
1345 ret = GNUNET_OS_start_process_vap (pipe_control,
1359 * @param pipe_control should a pipe be used to send signals to the child?
1360 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1361 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1362 * @param filename name of the binary
1363 * @param ... NULL-terminated list of arguments to the process
1365 * @return pointer to process structure of the new process, NULL on error
1368 struct GNUNET_OS_Process *
1369 GNUNET_OS_start_process (int pipe_control,
1370 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1371 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1372 const char *filename, ...)
1374 struct GNUNET_OS_Process *ret;
1377 va_start (ap, filename);
1378 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1387 * @param pipe_control should a pipe be used to send signals to the child?
1388 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1389 * must be NULL on platforms where dup is not supported
1390 * @param filename name of the binary
1391 * @param argv NULL-terminated list of arguments to the process
1392 * @return process ID of the new process, -1 on error
1394 struct GNUNET_OS_Process *
1395 GNUNET_OS_start_process_v (int pipe_control,
1396 const SOCKTYPE *lsocks,
1397 const char *filename,
1400 return start_process (pipe_control,
1410 * Retrieve the status of a process, waiting on him if dead.
1411 * Nonblocking version.
1413 * @param proc process ID
1414 * @param type status type
1415 * @param code return code/signal number
1416 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1419 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1420 enum GNUNET_OS_ProcessStatusType *type,
1421 unsigned long *code)
1427 GNUNET_assert (0 != proc);
1428 ret = waitpid (proc->pid, &status, WNOHANG);
1431 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1432 return GNUNET_SYSERR;
1436 *type = GNUNET_OS_PROCESS_RUNNING;
1440 if (proc->pid != ret)
1442 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1443 return GNUNET_SYSERR;
1445 if (WIFEXITED (status))
1447 *type = GNUNET_OS_PROCESS_EXITED;
1448 *code = WEXITSTATUS (status);
1450 else if (WIFSIGNALED (status))
1452 *type = GNUNET_OS_PROCESS_SIGNALED;
1453 *code = WTERMSIG (status);
1455 else if (WIFSTOPPED (status))
1457 *type = GNUNET_OS_PROCESS_SIGNALED;
1458 *code = WSTOPSIG (status);
1461 else if (WIFCONTINUED (status))
1463 *type = GNUNET_OS_PROCESS_RUNNING;
1469 *type = GNUNET_OS_PROCESS_UNKNOWN;
1474 DWORD c, error_code, ret;
1478 if (h == NULL || ret == 0)
1480 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1482 return GNUNET_SYSERR;
1485 h = GetCurrentProcess ();
1488 ret = GetExitCodeProcess (h, &c);
1489 error_code = GetLastError ();
1490 if (ret == 0 || error_code != NO_ERROR)
1492 SetErrnoFromWinError (error_code);
1493 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1494 return GNUNET_SYSERR;
1496 if (STILL_ACTIVE == c)
1498 *type = GNUNET_OS_PROCESS_RUNNING;
1502 *type = GNUNET_OS_PROCESS_EXITED;
1511 * Wait for a process
1512 * @param proc pointer to process structure
1513 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1516 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1520 pid_t pid = proc->pid;
1523 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1524 (EINTR == errno) ) ;
1527 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1528 return GNUNET_SYSERR;
1538 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1540 return GNUNET_SYSERR;
1543 h = GetCurrentProcess ();
1545 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1547 SetErrnoFromWinError (GetLastError ());
1548 ret = GNUNET_SYSERR;
1559 * Handle to a command.
1561 struct GNUNET_OS_CommandHandle
1567 struct GNUNET_OS_Process *eip;
1570 * Handle to the output pipe.
1572 struct GNUNET_DISK_PipeHandle *opipe;
1575 * Read-end of output pipe.
1577 const struct GNUNET_DISK_FileHandle *r;
1580 * Function to call on each line of output.
1582 GNUNET_OS_LineProcessor proc;
1585 * Closure for 'proc'.
1590 * Buffer for the output.
1595 * Task reading from pipe.
1597 GNUNET_SCHEDULER_TaskIdentifier rtask;
1602 struct GNUNET_TIME_Absolute timeout;
1605 * Current read offset in buf.
1612 * Stop/kill a command. Must ONLY be called either from
1613 * the callback after 'NULL' was passed for 'line' *OR*
1614 * from an independent task (not within the line processor).
1616 * @param cmd handle to the process
1619 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1622 if (cmd->proc != NULL)
1624 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1625 GNUNET_SCHEDULER_cancel (cmd->rtask);
1627 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1628 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1629 GNUNET_OS_process_destroy (cmd->eip);
1630 GNUNET_DISK_pipe_close (cmd->opipe);
1636 * Read from the process and call the line processor.
1638 * @param cls the 'struct GNUNET_OS_CommandHandle'
1639 * @param tc scheduler context
1642 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1644 struct GNUNET_OS_CommandHandle *cmd = cls;
1645 GNUNET_OS_LineProcessor proc;
1649 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1650 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1652 /* timeout, shutdown, etc. */
1655 proc (cmd->proc_cls, NULL);
1659 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1660 sizeof (cmd->buf) - cmd->off);
1663 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1665 cmd->buf[cmd->off] = '\0';
1666 cmd->proc (cmd->proc_cls, cmd->buf);
1670 proc (cmd->proc_cls, NULL);
1673 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1678 cmd->proc (cmd->proc_cls, cmd->buf);
1679 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1680 cmd->off -= (end + 1 - cmd->buf);
1681 end = memchr (cmd->buf, '\n', cmd->off);
1684 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1685 (cmd->timeout), cmd->r, &cmd_read, cmd);
1690 * Run the given command line and call the given function
1691 * for each line of the output.
1693 * @param proc function to call for each line of the output
1694 * @param proc_cls closure for proc
1695 * @param timeout when to time out
1696 * @param binary command to run
1697 * @param ... arguments to command
1698 * @return NULL on error
1700 struct GNUNET_OS_CommandHandle *
1701 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1702 struct GNUNET_TIME_Relative timeout, const char *binary,
1705 struct GNUNET_OS_CommandHandle *cmd;
1706 struct GNUNET_OS_Process *eip;
1707 struct GNUNET_DISK_PipeHandle *opipe;
1710 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1713 va_start (ap, binary);
1714 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1718 GNUNET_DISK_pipe_close (opipe);
1721 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1722 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1723 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1727 cmd->proc_cls = proc_cls;
1728 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1729 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1736 /* end of os_priority.c */