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"
44 struct GNUNET_OS_Process
59 * Pipe we use to signal the process (if used).
61 struct GNUNET_DISK_FileHandle *control_pipe;
64 * Name of the pipe, NULL for none.
71 * Handle for 'this' process.
73 static struct GNUNET_OS_Process current_process;
76 /* MinGW version of named pipe API */
79 * Creates a named pipe/FIFO and opens it
81 * @param fn pointer to the name of the named pipe or to NULL
82 * @param flags open flags
83 * @param perm access permissions
84 * @return pipe handle on success, NULL on error
86 static struct GNUNET_DISK_FileHandle *
87 npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
88 enum GNUNET_DISK_AccessPermissions perm)
90 struct GNUNET_DISK_FileHandle *ret;
96 if (flags & GNUNET_DISK_OPEN_READWRITE)
97 openMode = PIPE_ACCESS_DUPLEX;
98 else if (flags & GNUNET_DISK_OPEN_READ)
99 openMode = PIPE_ACCESS_INBOUND;
100 else if (flags & GNUNET_DISK_OPEN_WRITE)
101 openMode = PIPE_ACCESS_OUTBOUND;
102 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
103 openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
112 GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
113 LOG (GNUNET_ERROR_TYPE_DEBUG,
114 "Trying to create an instance of named pipe `%s'\n", name);
115 /* 1) This might work just fine with UTF-8 strings as it is.
116 * 2) This is only used by GNUnet itself, and only with latin names.
118 h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
119 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
124 GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
125 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
127 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
129 h = CreateNamedPipe (*fn,
130 openMode | FILE_FLAG_OVERLAPPED |
131 FILE_FLAG_FIRST_PIPE_INSTANCE,
132 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
135 error_code = GetLastError ();
138 /* don't re-set name to NULL yet */
139 if (h == INVALID_HANDLE_VALUE)
141 SetErrnoFromWinError (error_code);
142 LOG (GNUNET_ERROR_TYPE_DEBUG,
143 "Pipe creation have failed because of %d, errno is %d\n", error_code,
147 LOG (GNUNET_ERROR_TYPE_DEBUG,
148 "Pipe was to be unique, considering re-creation\n");
151 if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
155 LOG (GNUNET_ERROR_TYPE_DEBUG,
156 "Pipe name was not unique, trying again\n");
165 ret = GNUNET_malloc (sizeof (*ret));
167 ret->type = GNUNET_PIPE;
168 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
169 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
170 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
171 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
177 * Opens already existing named pipe/FIFO
179 * @param fn name of an existing named pipe
180 * @param flags open flags
181 * @return pipe handle on success, NULL on error
183 static struct GNUNET_DISK_FileHandle *
184 npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
186 struct GNUNET_DISK_FileHandle *ret;
191 if (flags & GNUNET_DISK_OPEN_READWRITE)
192 openMode = GENERIC_WRITE | GENERIC_READ;
193 else if (flags & GNUNET_DISK_OPEN_READ)
194 openMode = GENERIC_READ;
195 else if (flags & GNUNET_DISK_OPEN_WRITE)
196 openMode = GENERIC_WRITE;
198 h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
199 FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
200 if (h == INVALID_HANDLE_VALUE)
202 SetErrnoFromWinError (GetLastError ());
206 ret = GNUNET_malloc (sizeof (*ret));
208 ret->type = GNUNET_PIPE;
209 ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
210 ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
211 ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
212 ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
218 /* UNIX version of named-pipe API */
221 * Clean up a named pipe and the directory it was placed in.
223 * @param fn name of the pipe
226 cleanup_npipe (const char *fn)
231 if (0 != unlink (fn))
232 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
233 dn = GNUNET_strdup (fn);
236 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
242 * Setup a named pipe.
244 * @param fn where to store the name of the new pipe,
245 * if *fn is non-null, the name of the pipe to setup
246 * @return GNUNET_OK on success
249 npipe_setup (char **fn)
253 /* FIXME: hardwired '/tmp' path... is bad */
254 char dir[] = "/tmp/gnunet-pipe-XXXXXX";
256 if (NULL == mkdtemp (dir))
258 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
259 return GNUNET_SYSERR;
261 GNUNET_asprintf (fn, "%s/child-control", dir);
263 if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
264 return GNUNET_SYSERR;
270 * Open an existing named pipe.
272 * @param fn name of the file
273 * @param flags flags to use
274 * @return NULL on error
276 static struct GNUNET_DISK_FileHandle *
277 npipe_open (const char *fn,
278 enum GNUNET_DISK_OpenFlags flags)
280 struct GNUNET_DISK_FileHandle *ret;
285 /* 200 * 5ms = 1s at most */
288 fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
289 if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
291 /* as this is for killing a child process via pipe and it is conceivable that
292 the child process simply didn't finish starting yet, we do some sleeping
293 (which is obviously usually not allowed). We can't select on the FD as
294 'open' fails, and we probably shouldn't just "ignore" the error, so wait
295 and retry a few times is likely the best method; our process API doesn't
296 support continuations, so we need to sleep directly... */
298 req.tv_nsec = 5000000; /* 5ms */
299 (void) nanosleep (&req, NULL);
303 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
304 (flags == GNUNET_DISK_OPEN_READ)
305 ? _("Failed to open named pipe `%s' for reading: %s\n")
306 : _("Failed to open named pipe `%s' for writing: %s\n"),
311 ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
319 * This handler is called when there are control data to be read on the pipe
321 * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
322 * @param tc scheduler context
325 parent_control_handler (void *cls,
326 const struct GNUNET_SCHEDULER_TaskContext *tc)
328 struct GNUNET_DISK_FileHandle *control_pipe = cls;
332 LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
334 if (0 != (tc->reason &
335 (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT)))
337 GNUNET_DISK_file_close (control_pipe);
341 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig));
342 if (sizeof (sig) != ret)
345 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
346 GNUNET_DISK_file_close (control_pipe);
350 LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
351 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
352 control_pipe, &parent_control_handler,
359 * Task that connects this process to its parent via pipe;
360 * essentially, the parent control handler will read signal numbers
361 * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
362 * variable) and raise those signals.
364 * @param cls closure (unused)
365 * @param tc scheduler context (unused)
368 GNUNET_OS_install_parent_control_handler (void *cls,
370 GNUNET_SCHEDULER_TaskContext *tc)
373 struct GNUNET_DISK_FileHandle *control_pipe;
375 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
376 if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
378 LOG (GNUNET_ERROR_TYPE_DEBUG,
379 "Not installing a handler because $%s is empty\n",
380 GNUNET_OS_CONTROL_PIPE);
381 putenv ("GNUNET_OS_CONTROL_PIPE=");
385 npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
386 if (NULL == control_pipe)
388 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
389 putenv ("GNUNET_OS_CONTROL_PIPE=");
392 LOG (GNUNET_ERROR_TYPE_DEBUG,
393 "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
394 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
395 &parent_control_handler, control_pipe);
396 putenv ("GNUNET_OS_CONTROL_PIPE=");
401 * Get process structure for current process
403 * The pointer it returns points to static memory location and must not be
406 * @return pointer to the process sturcutre for this process
408 struct GNUNET_OS_Process *
409 GNUNET_OS_process_current ()
412 current_process.pid = GetCurrentProcessId ();
413 current_process.handle = GetCurrentProcess ();
415 current_process.pid = 0;
417 return ¤t_process;
422 * Sends a signal to the process
424 * @param proc pointer to process structure
426 * @return 0 on success, -1 on error
429 GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
436 if ( (NULL == proc->control_pipe) &&
437 (NULL != proc->childpipename) )
438 proc->control_pipe = npipe_open (proc->childpipename,
439 GNUNET_DISK_OPEN_WRITE);
441 if (NULL != proc->control_pipe)
443 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof (csig));
444 if (ret == sizeof (csig))
447 /* pipe failed or non-existent, try other methods */
456 #if WINDOWS && !defined(__CYGWIN__)
457 if (0 == TerminateProcess (proc->handle, 0))
459 /* FIXME: set 'errno' */
464 return PLIBC_KILL (proc->pid, sig);
471 return PLIBC_KILL (proc->pid, sig);
477 * Get the pid of the process in question
479 * @param proc the process to get the pid of
481 * @return the current process id
484 GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
491 * Cleans up process structure contents (OS-dependent) and deallocates it
493 * @param proc pointer to process structure
496 GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
498 if (NULL != proc->control_pipe)
499 GNUNET_DISK_file_close (proc->control_pipe);
502 if (proc->handle != NULL)
503 CloseHandle (proc->handle);
505 if (NULL != proc->childpipename)
508 cleanup_npipe (proc->childpipename);
510 GNUNET_free (proc->childpipename);
517 #include "gnunet_signal_lib.h"
519 extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
522 * Make seaspider happy.
524 #define DWORD_WINAPI DWORD WINAPI
527 * @brief Waits for a process to terminate and invokes the SIGCHLD handler
528 * @param proc pointer to process structure
531 child_wait_thread (void *arg)
533 struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
535 WaitForSingleObject (proc->handle, INFINITE);
537 if (w32_sigchld_handler)
538 w32_sigchld_handler ();
545 * Set process priority
547 * @param proc pointer to process structure
548 * @param prio priority value
549 * @return GNUNET_OK on success, GNUNET_SYSERR on error
552 GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
553 enum GNUNET_SCHEDULER_Priority prio)
557 GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
558 if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
561 /* convert to MINGW/Unix values */
564 case GNUNET_SCHEDULER_PRIORITY_UI:
565 case GNUNET_SCHEDULER_PRIORITY_URGENT:
567 rprio = HIGH_PRIORITY_CLASS;
573 case GNUNET_SCHEDULER_PRIORITY_HIGH:
575 rprio = ABOVE_NORMAL_PRIORITY_CLASS;
581 case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
583 rprio = NORMAL_PRIORITY_CLASS;
589 case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
591 rprio = BELOW_NORMAL_PRIORITY_CLASS;
597 case GNUNET_SCHEDULER_PRIORITY_IDLE:
599 rprio = IDLE_PRIORITY_CLASS;
606 return GNUNET_SYSERR;
609 /* Set process priority */
612 HANDLE h = proc->handle;
614 GNUNET_assert (h != NULL);
615 SetPriorityClass (h, rprio);
621 if ((0 == pid) || (pid == getpid ()))
624 int delta = rprio - have;
627 if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
629 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
630 return GNUNET_SYSERR;
635 if (0 != setpriority (PRIO_PROCESS, pid, rprio))
637 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
639 return GNUNET_SYSERR;
643 LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
644 "Priority management not availabe for this platform\n");
651 CreateCustomEnvTable (char **vars)
653 char *win32_env_table;
658 size_t tablesize = 0;
659 size_t items_count = 0;
668 win32_env_table = GetEnvironmentStringsA ();
669 if (win32_env_table == NULL)
671 for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
673 index = GNUNET_malloc (sizeof (char *) * n_var);
674 for (c = 0; c < n_var; c++)
676 for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
678 size_t len = strlen (ptr);
681 for (var_ptr = vars; *var_ptr; var_ptr++)
685 var_len = strlen (var);
686 if (strncmp (var, ptr, var_len) == 0)
690 tablesize += var_len + strlen (val) + 1;
695 tablesize += len + 1;
698 for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
703 n_found += strlen (var) + strlen (val) + 1;
705 result = GNUNET_malloc (tablesize + n_found + 1);
706 for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
708 size_t len = strlen (ptr);
711 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
715 var_len = strlen (var);
716 if (strncmp (var, ptr, var_len) == 0)
724 strcpy (result_ptr, ptr);
725 result_ptr += len + 1;
729 strcpy (result_ptr, var);
730 result_ptr += var_len;
731 strcpy (result_ptr, val);
732 result_ptr += strlen (val) + 1;
736 for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
740 var_len = strlen (var);
743 strcpy (result_ptr, var);
744 result_ptr += var_len;
745 strcpy (result_ptr, val);
746 result_ptr += strlen (val) + 1;
749 FreeEnvironmentStrings (win32_env_table);
760 * @param pipe_control should a pipe be used to send signals to the child?
761 * @param pipe_stdin pipe to use to send input to child process (or NULL)
762 * @param pipe_stdout pipe to use to get output from child process (or NULL)
763 * @param filename name of the binary
764 * @param argv NULL-terminated array of arguments to the process
765 * @return pointer to process structure of the new process, NULL on error
767 struct GNUNET_OS_Process *
768 GNUNET_OS_start_process_vap (int pipe_control,
769 struct GNUNET_DISK_PipeHandle *pipe_stdin,
770 struct GNUNET_DISK_PipeHandle *pipe_stdout,
771 const char *filename,
775 char *childpipename = NULL;
776 struct GNUNET_OS_Process *gnunet_proc = NULL;
783 if ( (GNUNET_YES == pipe_control) &&
785 npipe_setup (&childpipename)) )
787 if (pipe_stdout != NULL)
789 GNUNET_assert (GNUNET_OK ==
790 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
792 GNUNET_DISK_PIPE_END_WRITE),
793 &fd_stdout_write, sizeof (int)));
794 GNUNET_assert (GNUNET_OK ==
795 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
796 (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
797 &fd_stdout_read, sizeof (int)));
799 if (pipe_stdin != NULL)
801 GNUNET_assert (GNUNET_OK ==
802 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
803 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
804 &fd_stdin_read, sizeof (int)));
805 GNUNET_assert (GNUNET_OK ==
806 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
807 (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
808 &fd_stdin_write, sizeof (int)));
816 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
817 GNUNET_free_non_null (childpipename);
823 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
824 gnunet_proc->pid = ret;
825 gnunet_proc->childpipename = childpipename;
828 if (NULL != childpipename)
830 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
831 GNUNET_free (childpipename);
833 if (pipe_stdout != NULL)
835 GNUNET_break (0 == close (fd_stdout_read));
836 if (-1 == dup2 (fd_stdout_write, 1))
837 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
838 GNUNET_break (0 == close (fd_stdout_write));
841 if (pipe_stdin != NULL)
844 GNUNET_break (0 == close (fd_stdin_write));
845 if (-1 == dup2 (fd_stdin_read, 0))
846 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
847 GNUNET_break (0 == close (fd_stdin_read));
849 execvp (filename, argv);
850 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
853 char *childpipename = NULL;
854 struct GNUNET_OS_Process *gnunet_proc = NULL;
859 PROCESS_INFORMATION proc;
862 HANDLE stdout_handle;
863 struct GNUNET_DISK_FileHandle *control_pipe;
865 char path[MAX_PATH + 1];
867 char *our_env[3] = { NULL, NULL, NULL };
868 char *env_block = NULL;
870 DWORD pathbuf_len, alloc_len;
875 char *non_const_filename;
876 char win_path[MAX_PATH + 1];
877 wchar_t *wpath, *wcmd;
878 size_t wpath_len, wcmd_len;
881 /* Search in prefix dir (hopefully - the directory from which
882 * the current module was loaded), bindir and libdir, then in PATH
884 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
885 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
886 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
888 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
891 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
894 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
897 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
898 GNUNET_free (self_prefix);
899 GNUNET_free (bindir);
900 GNUNET_free (libdir);
902 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
903 GNUNET_assert (alloc_len == (pathbuf_len - 1));
905 cmdlen = strlen (filename);
906 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
907 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
909 GNUNET_asprintf (&non_const_filename, "%s", filename);
911 /* It could be in POSIX form, convert it to a DOS path early on */
912 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
914 SetErrnoFromWinError (lRet);
915 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
917 GNUNET_free (non_const_filename);
918 GNUNET_free (pathbuf);
921 GNUNET_free (non_const_filename);
922 non_const_filename = GNUNET_strdup (win_path);
923 /* Check that this is the full path. If it isn't, search. */
924 /* FIXME: convert it to wchar_t and use SearchPathW?
925 * Remember: arguments to _start_process() are technically in UTF-8...
927 if (non_const_filename[1] == ':')
928 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
929 else if (!SearchPathA
930 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
933 SetErrnoFromWinError (GetLastError ());
934 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
936 GNUNET_free (non_const_filename);
937 GNUNET_free (pathbuf);
940 GNUNET_free (pathbuf);
941 GNUNET_free (non_const_filename);
945 while (NULL != (arg = argv[argc++]))
948 cmdlen = cmdlen + strlen (path) + 4;
950 cmdlen = cmdlen + strlen (arg) + 4;
954 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
956 while (NULL != (arg = argv[argc++]))
958 /* This is to escape trailing slash */
959 char arg_lastchar = arg[strlen (arg) - 1];
961 idx += sprintf (idx, "\"%s%s\"%s", path,
962 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
964 idx += sprintf (idx, "\"%s%s\"%s", arg,
965 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
968 memset (&start, 0, sizeof (start));
969 start.cb = sizeof (start);
971 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
972 start.dwFlags |= STARTF_USESTDHANDLES;
974 if (pipe_stdin != NULL)
976 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
977 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
978 &stdin_handle, sizeof (HANDLE));
979 start.hStdInput = stdin_handle;
982 if (pipe_stdout != NULL)
984 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
986 GNUNET_DISK_PIPE_END_WRITE),
987 &stdout_handle, sizeof (HANDLE));
988 start.hStdOutput = stdout_handle;
990 if (GNUNET_YES == pipe_control)
993 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
994 GNUNET_DISK_PERM_USER_READ |
995 GNUNET_DISK_PERM_USER_WRITE);
996 if (control_pipe == NULL)
1004 control_pipe = NULL;
1005 if (NULL != childpipename)
1007 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1009 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1010 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1017 env_block = CreateCustomEnvTable (our_env);
1018 GNUNET_free_non_null (our_env[0]);
1019 GNUNET_free_non_null (our_env[1]);
1022 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1024 LOG (GNUNET_ERROR_TYPE_DEBUG,
1025 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1026 GNUNET_free (env_block);
1032 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1034 LOG (GNUNET_ERROR_TYPE_DEBUG,
1035 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1036 GNUNET_free (env_block);
1042 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1043 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1045 SetErrnoFromWinError (GetLastError ());
1046 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1047 GNUNET_free (env_block);
1054 GNUNET_free (env_block);
1056 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1057 gnunet_proc->pid = proc.dwProcessId;
1058 gnunet_proc->handle = proc.hProcess;
1059 gnunet_proc->control_pipe = control_pipe;
1061 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1063 ResumeThread (proc.hThread);
1064 CloseHandle (proc.hThread);
1077 * @param pipe_control should a pipe be used to send signals to the child?
1078 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1079 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1080 * @param filename name of the binary
1081 * @param va NULL-terminated list of arguments to the process
1082 * @return pointer to process structure of the new process, NULL on error
1084 struct GNUNET_OS_Process *
1085 GNUNET_OS_start_process_va (int pipe_control,
1086 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1087 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1088 const char *filename, va_list va)
1090 struct GNUNET_OS_Process *ret;
1097 while (NULL != va_arg (ap, char *))
1100 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1103 while (NULL != (argv[argc] = va_arg (ap, char *)))
1106 ret = GNUNET_OS_start_process_vap (pipe_control,
1120 * @param pipe_control should a pipe be used to send signals to the child?
1121 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1122 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1123 * @param filename name of the binary
1124 * @param ... NULL-terminated list of arguments to the process
1126 * @return pointer to process structure of the new process, NULL on error
1129 struct GNUNET_OS_Process *
1130 GNUNET_OS_start_process (int pipe_control,
1131 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1132 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1133 const char *filename, ...)
1135 struct GNUNET_OS_Process *ret;
1138 va_start (ap, filename);
1139 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1148 * @param pipe_control should a pipe be used to send signals to the child?
1149 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1150 * must be NULL on platforms where dup is not supported
1151 * @param filename name of the binary
1152 * @param argv NULL-terminated list of arguments to the process
1153 * @return process ID of the new process, -1 on error
1155 struct GNUNET_OS_Process *
1156 GNUNET_OS_start_process_v (int pipe_control,
1157 const SOCKTYPE *lsocks,
1158 const char *filename,
1165 struct GNUNET_OS_Process *gnunet_proc = NULL;
1166 char *childpipename = NULL;
1175 if ( (GNUNET_YES == pipe_control) &&
1176 (GNUNET_OK != npipe_setup (&childpipename)) )
1183 while (-1 != (k = lsocks[i++]))
1184 GNUNET_array_append (lscp, ls, k);
1185 GNUNET_array_append (lscp, ls, -1);
1192 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1193 GNUNET_free_non_null (childpipename);
1194 GNUNET_array_grow (lscp, ls, 0);
1200 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1201 gnunet_proc->pid = ret;
1202 gnunet_proc->childpipename = childpipename;
1203 GNUNET_array_grow (lscp, ls, 0);
1206 if (NULL != childpipename)
1208 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1209 GNUNET_free (childpipename);
1213 /* read systemd documentation... */
1214 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1215 setenv ("LISTEN_PID", lpid, 1);
1218 while (-1 != lscp[i])
1221 while (-1 != lscp[j])
1227 GNUNET_assert (-1 != k);
1228 GNUNET_assert (0 == close (lscp[j]));
1236 /* Bury any existing FD, no matter what; they should all be closed
1237 * on exec anyway and the important onces have been dup'ed away */
1239 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1241 /* unset close-on-exec flag */
1242 flags = fcntl (tgt, F_GETFD);
1243 GNUNET_assert (flags >= 0);
1244 flags &= ~FD_CLOEXEC;
1246 (void) fcntl (tgt, F_SETFD, flags);
1250 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1251 setenv ("LISTEN_FDS", fds, 1);
1253 GNUNET_array_grow (lscp, ls, 0);
1254 execvp (filename, argv);
1255 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1258 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1259 char *childpipename = NULL;
1260 char **arg, **non_const_argv;
1261 unsigned int cmdlen;
1264 PROCESS_INFORMATION proc;
1266 struct GNUNET_OS_Process *gnunet_proc = NULL;
1267 char path[MAX_PATH + 1];
1268 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1269 char *env_block = NULL;
1271 DWORD pathbuf_len, alloc_len;
1276 char *non_const_filename;
1277 char win_path[MAX_PATH + 1];
1278 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1279 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1281 HANDLE lsocks_write;
1282 wchar_t *wpath, *wcmd;
1283 size_t wpath_len, wcmd_len;
1288 /* Search in prefix dir (hopefully - the directory from which
1289 * the current module was loaded), bindir and libdir, then in PATH
1291 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1292 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1293 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1295 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1298 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1301 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1304 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1305 GNUNET_free (self_prefix);
1306 GNUNET_free (bindir);
1307 GNUNET_free (libdir);
1309 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1310 if (alloc_len != pathbuf_len - 1)
1312 GNUNET_free (pathbuf);
1313 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1317 cmdlen = strlen (filename);
1318 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1319 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1321 GNUNET_asprintf (&non_const_filename, "%s", filename);
1323 /* It could be in POSIX form, convert it to a DOS path early on */
1324 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1326 SetErrnoFromWinError (lRet);
1327 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1328 non_const_filename);
1329 GNUNET_free (non_const_filename);
1330 GNUNET_free (pathbuf);
1333 GNUNET_free (non_const_filename);
1334 non_const_filename = GNUNET_strdup (win_path);
1335 /* Check that this is the full path. If it isn't, search. */
1336 /* FIXME: convert it to wchar_t and use SearchPathW?
1337 * Remember: arguments to _start_process() are technically in UTF-8...
1339 if (non_const_filename[1] == ':')
1340 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1341 else if (!SearchPathA
1342 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1345 SetErrnoFromWinError (GetLastError ());
1346 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1347 non_const_filename);
1348 GNUNET_free (non_const_filename);
1349 GNUNET_free (pathbuf);
1352 GNUNET_free (pathbuf);
1353 GNUNET_free (non_const_filename);
1355 /* Count the number of arguments */
1356 arg = (char **) argv;
1363 /* Allocate a copy argv */
1364 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1366 /* Copy all argv strings */
1368 arg = (char **) argv;
1372 non_const_argv[argcount] = GNUNET_strdup (path);
1374 non_const_argv[argcount] = GNUNET_strdup (*arg);
1378 non_const_argv[argcount] = NULL;
1382 arg = non_const_argv;
1385 cmdlen = cmdlen + strlen (*arg) + 4;
1389 /* Allocate and create cmd */
1390 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1391 arg = non_const_argv;
1394 char arg_last_char = (*arg)[strlen (*arg) - 1];
1395 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1396 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1400 while (argcount > 0)
1401 GNUNET_free (non_const_argv[--argcount]);
1402 GNUNET_free (non_const_argv);
1404 memset (&start, 0, sizeof (start));
1405 start.cb = sizeof (start);
1407 if (GNUNET_YES == pipe_control)
1410 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1411 GNUNET_DISK_PERM_USER_READ |
1412 GNUNET_DISK_PERM_USER_WRITE);
1413 if (control_pipe == NULL)
1421 control_pipe = NULL;
1422 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1424 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1426 if (lsocks_pipe == NULL)
1430 GNUNET_DISK_pipe_close (lsocks_pipe);
1433 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1434 GNUNET_DISK_PIPE_END_WRITE);
1435 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1436 &lsocks_write, sizeof (HANDLE));
1437 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1438 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1439 &lsocks_read, sizeof (HANDLE));
1443 if (NULL != childpipename)
1445 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1447 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1448 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1449 GNUNET_free (childpipename);
1451 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1453 /*This will tell the child that we're going to send lsocks over the pipe*/
1454 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1455 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1457 our_env[env_off++] = NULL;
1458 env_block = CreateCustomEnvTable (our_env);
1460 GNUNET_free_non_null (our_env[--env_off]);
1463 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1465 LOG (GNUNET_ERROR_TYPE_DEBUG,
1466 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1467 GNUNET_free (env_block);
1473 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1475 LOG (GNUNET_ERROR_TYPE_DEBUG,
1476 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1477 GNUNET_free (env_block);
1483 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1484 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1486 SetErrnoFromWinError (GetLastError ());
1487 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1488 if (NULL != control_pipe)
1489 GNUNET_DISK_file_close (control_pipe);
1491 GNUNET_DISK_pipe_close (lsocks_pipe);
1492 GNUNET_free (env_block);
1499 GNUNET_free (env_block);
1501 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1502 gnunet_proc->pid = proc.dwProcessId;
1503 gnunet_proc->handle = proc.hProcess;
1504 gnunet_proc->control_pipe = control_pipe;
1506 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1508 ResumeThread (proc.hThread);
1509 CloseHandle (proc.hThread);
1514 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1517 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1519 /* This is a replacement for "goto error" that doesn't use goto */
1524 uint64_t size, count, i;
1526 /* Tell the number of sockets */
1527 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1529 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1530 if (wrote != sizeof (count))
1532 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1535 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1537 WSAPROTOCOL_INFOA pi;
1538 /* Get a socket duplication info */
1539 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1542 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1545 /* Synchronous I/O is not nice, but we can't schedule this:
1546 * lsocks will be closed/freed by the caller soon, and until
1547 * the child creates a duplicate, closing a socket here will
1548 * close it for good.
1550 /* Send the size of the structure
1551 * (the child might be built with different headers...)
1554 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1555 if (wrote != sizeof (size))
1557 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1560 /* Finally! Send the data */
1561 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1562 if (wrote != sizeof (pi))
1564 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1568 /* This will block us until the child makes a final read or closes
1569 * the pipe (hence no 'wrote' check), since we have to wait for it
1570 * to duplicate the last socket, before we return and start closing
1573 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1578 GNUNET_DISK_file_sync (lsocks_write_fd);
1579 GNUNET_DISK_pipe_close (lsocks_pipe);
1583 /* If we can't pass on the socket(s), the child will block forever,
1584 * better put it out of its misery.
1586 TerminateProcess (gnunet_proc->handle, 0);
1587 CloseHandle (gnunet_proc->handle);
1588 if (NULL != gnunet_proc->control_pipe)
1589 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1590 GNUNET_free (gnunet_proc);
1599 * Retrieve the status of a process, waiting on him if dead.
1600 * Nonblocking version.
1602 * @param proc process ID
1603 * @param type status type
1604 * @param code return code/signal number
1605 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1608 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1609 enum GNUNET_OS_ProcessStatusType *type,
1610 unsigned long *code)
1616 GNUNET_assert (0 != proc);
1617 ret = waitpid (proc->pid, &status, WNOHANG);
1620 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1621 return GNUNET_SYSERR;
1625 *type = GNUNET_OS_PROCESS_RUNNING;
1629 if (proc->pid != ret)
1631 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1632 return GNUNET_SYSERR;
1634 if (WIFEXITED (status))
1636 *type = GNUNET_OS_PROCESS_EXITED;
1637 *code = WEXITSTATUS (status);
1639 else if (WIFSIGNALED (status))
1641 *type = GNUNET_OS_PROCESS_SIGNALED;
1642 *code = WTERMSIG (status);
1644 else if (WIFSTOPPED (status))
1646 *type = GNUNET_OS_PROCESS_SIGNALED;
1647 *code = WSTOPSIG (status);
1650 else if (WIFCONTINUED (status))
1652 *type = GNUNET_OS_PROCESS_RUNNING;
1658 *type = GNUNET_OS_PROCESS_UNKNOWN;
1663 DWORD c, error_code, ret;
1667 if (h == NULL || ret == 0)
1669 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1671 return GNUNET_SYSERR;
1674 h = GetCurrentProcess ();
1677 ret = GetExitCodeProcess (h, &c);
1678 error_code = GetLastError ();
1679 if (ret == 0 || error_code != NO_ERROR)
1681 SetErrnoFromWinError (error_code);
1682 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1683 return GNUNET_SYSERR;
1685 if (STILL_ACTIVE == c)
1687 *type = GNUNET_OS_PROCESS_RUNNING;
1691 *type = GNUNET_OS_PROCESS_EXITED;
1700 * Wait for a process
1701 * @param proc pointer to process structure
1702 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1705 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1709 pid_t pid = proc->pid;
1712 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1713 (EINTR == errno) ) ;
1716 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1717 return GNUNET_SYSERR;
1727 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1729 return GNUNET_SYSERR;
1732 h = GetCurrentProcess ();
1734 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1736 SetErrnoFromWinError (GetLastError ());
1737 ret = GNUNET_SYSERR;
1748 * Handle to a command.
1750 struct GNUNET_OS_CommandHandle
1756 struct GNUNET_OS_Process *eip;
1759 * Handle to the output pipe.
1761 struct GNUNET_DISK_PipeHandle *opipe;
1764 * Read-end of output pipe.
1766 const struct GNUNET_DISK_FileHandle *r;
1769 * Function to call on each line of output.
1771 GNUNET_OS_LineProcessor proc;
1774 * Closure for 'proc'.
1779 * Buffer for the output.
1784 * Task reading from pipe.
1786 GNUNET_SCHEDULER_TaskIdentifier rtask;
1791 struct GNUNET_TIME_Absolute timeout;
1794 * Current read offset in buf.
1801 * Stop/kill a command. Must ONLY be called either from
1802 * the callback after 'NULL' was passed for 'line' *OR*
1803 * from an independent task (not within the line processor).
1805 * @param cmd handle to the process
1808 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1811 if (cmd->proc != NULL)
1813 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1814 GNUNET_SCHEDULER_cancel (cmd->rtask);
1816 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1817 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1818 GNUNET_OS_process_destroy (cmd->eip);
1819 GNUNET_DISK_pipe_close (cmd->opipe);
1825 * Read from the process and call the line processor.
1827 * @param cls the 'struct GNUNET_OS_CommandHandle'
1828 * @param tc scheduler context
1831 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1833 struct GNUNET_OS_CommandHandle *cmd = cls;
1834 GNUNET_OS_LineProcessor proc;
1838 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1839 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1841 /* timeout, shutdown, etc. */
1844 proc (cmd->proc_cls, NULL);
1848 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1849 sizeof (cmd->buf) - cmd->off);
1852 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1854 cmd->buf[cmd->off] = '\0';
1855 cmd->proc (cmd->proc_cls, cmd->buf);
1859 proc (cmd->proc_cls, NULL);
1862 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1867 cmd->proc (cmd->proc_cls, cmd->buf);
1868 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1869 cmd->off -= (end + 1 - cmd->buf);
1870 end = memchr (cmd->buf, '\n', cmd->off);
1873 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1874 (cmd->timeout), cmd->r, &cmd_read, cmd);
1879 * Run the given command line and call the given function
1880 * for each line of the output.
1882 * @param proc function to call for each line of the output
1883 * @param proc_cls closure for proc
1884 * @param timeout when to time out
1885 * @param binary command to run
1886 * @param ... arguments to command
1887 * @return NULL on error
1889 struct GNUNET_OS_CommandHandle *
1890 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1891 struct GNUNET_TIME_Relative timeout, const char *binary,
1894 struct GNUNET_OS_CommandHandle *cmd;
1895 struct GNUNET_OS_Process *eip;
1896 struct GNUNET_DISK_PipeHandle *opipe;
1899 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1902 va_start (ap, binary);
1903 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1907 GNUNET_DISK_pipe_close (opipe);
1910 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1911 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1912 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1916 cmd->proc_cls = proc_cls;
1917 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1918 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1925 /* end of os_priority.c */