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)));
814 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
815 GNUNET_free_non_null (childpipename);
820 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
821 gnunet_proc->pid = ret;
822 gnunet_proc->childpipename = childpipename;
825 if (NULL != childpipename)
827 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
828 GNUNET_free (childpipename);
830 if (pipe_stdout != NULL)
832 GNUNET_break (0 == close (fd_stdout_read));
833 if (-1 == dup2 (fd_stdout_write, 1))
834 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
835 GNUNET_break (0 == close (fd_stdout_write));
838 if (pipe_stdin != NULL)
841 GNUNET_break (0 == close (fd_stdin_write));
842 if (-1 == dup2 (fd_stdin_read, 0))
843 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
844 GNUNET_break (0 == close (fd_stdin_read));
846 execvp (filename, argv);
847 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
850 char *childpipename = NULL;
851 struct GNUNET_OS_Process *gnunet_proc = NULL;
856 PROCESS_INFORMATION proc;
859 HANDLE stdout_handle;
860 struct GNUNET_DISK_FileHandle *control_pipe;
862 char path[MAX_PATH + 1];
864 char *our_env[3] = { NULL, NULL, NULL };
865 char *env_block = NULL;
867 DWORD pathbuf_len, alloc_len;
872 char *non_const_filename;
873 char win_path[MAX_PATH + 1];
874 wchar_t *wpath, *wcmd;
875 size_t wpath_len, wcmd_len;
878 /* Search in prefix dir (hopefully - the directory from which
879 * the current module was loaded), bindir and libdir, then in PATH
881 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
882 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
883 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
885 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
888 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
891 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
894 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
895 GNUNET_free (self_prefix);
896 GNUNET_free (bindir);
897 GNUNET_free (libdir);
899 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
900 GNUNET_assert (alloc_len == (pathbuf_len - 1));
902 cmdlen = strlen (filename);
903 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
904 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
906 GNUNET_asprintf (&non_const_filename, "%s", filename);
908 /* It could be in POSIX form, convert it to a DOS path early on */
909 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
911 SetErrnoFromWinError (lRet);
912 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
914 GNUNET_free (non_const_filename);
915 GNUNET_free (pathbuf);
918 GNUNET_free (non_const_filename);
919 non_const_filename = GNUNET_strdup (win_path);
920 /* Check that this is the full path. If it isn't, search. */
921 /* FIXME: convert it to wchar_t and use SearchPathW?
922 * Remember: arguments to _start_process() are technically in UTF-8...
924 if (non_const_filename[1] == ':')
925 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
926 else if (!SearchPathA
927 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
930 SetErrnoFromWinError (GetLastError ());
931 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
933 GNUNET_free (non_const_filename);
934 GNUNET_free (pathbuf);
937 GNUNET_free (pathbuf);
938 GNUNET_free (non_const_filename);
942 while (NULL != (arg = argv[argc++]))
945 cmdlen = cmdlen + strlen (path) + 4;
947 cmdlen = cmdlen + strlen (arg) + 4;
951 cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
953 while (NULL != (arg = argv[argc++]))
955 /* This is to escape trailing slash */
956 char arg_lastchar = arg[strlen (arg) - 1];
958 idx += sprintf (idx, "\"%s%s\"%s", path,
959 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
961 idx += sprintf (idx, "\"%s%s\"%s", arg,
962 arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
965 memset (&start, 0, sizeof (start));
966 start.cb = sizeof (start);
968 if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
969 start.dwFlags |= STARTF_USESTDHANDLES;
971 if (pipe_stdin != NULL)
973 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
974 (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
975 &stdin_handle, sizeof (HANDLE));
976 start.hStdInput = stdin_handle;
979 if (pipe_stdout != NULL)
981 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
983 GNUNET_DISK_PIPE_END_WRITE),
984 &stdout_handle, sizeof (HANDLE));
985 start.hStdOutput = stdout_handle;
987 if (GNUNET_YES == pipe_control)
990 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
991 GNUNET_DISK_PERM_USER_READ |
992 GNUNET_DISK_PERM_USER_WRITE);
993 if (control_pipe == NULL)
1001 control_pipe = NULL;
1002 if (NULL != childpipename)
1004 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1006 GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
1007 GNUNET_asprintf (&our_env[1], "%s", childpipename);
1014 env_block = CreateCustomEnvTable (our_env);
1015 GNUNET_free_non_null (our_env[0]);
1016 GNUNET_free_non_null (our_env[1]);
1019 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1021 LOG (GNUNET_ERROR_TYPE_DEBUG,
1022 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1023 GNUNET_free (env_block);
1029 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1031 LOG (GNUNET_ERROR_TYPE_DEBUG,
1032 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1033 GNUNET_free (env_block);
1039 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1040 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1042 SetErrnoFromWinError (GetLastError ());
1043 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
1044 GNUNET_free (env_block);
1051 GNUNET_free (env_block);
1053 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1054 gnunet_proc->pid = proc.dwProcessId;
1055 gnunet_proc->handle = proc.hProcess;
1056 gnunet_proc->control_pipe = control_pipe;
1058 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1060 ResumeThread (proc.hThread);
1061 CloseHandle (proc.hThread);
1074 * @param pipe_control should a pipe be used to send signals to the child?
1075 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1076 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1077 * @param filename name of the binary
1078 * @param va NULL-terminated list of arguments to the process
1079 * @return pointer to process structure of the new process, NULL on error
1081 struct GNUNET_OS_Process *
1082 GNUNET_OS_start_process_va (int pipe_control,
1083 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1084 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1085 const char *filename, va_list va)
1087 struct GNUNET_OS_Process *ret;
1094 while (NULL != va_arg (ap, char *))
1097 argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
1100 while (NULL != (argv[argc] = va_arg (ap, char *)))
1103 ret = GNUNET_OS_start_process_vap (pipe_control,
1117 * @param pipe_control should a pipe be used to send signals to the child?
1118 * @param pipe_stdin pipe to use to send input to child process (or NULL)
1119 * @param pipe_stdout pipe to use to get output from child process (or NULL)
1120 * @param filename name of the binary
1121 * @param ... NULL-terminated list of arguments to the process
1123 * @return pointer to process structure of the new process, NULL on error
1126 struct GNUNET_OS_Process *
1127 GNUNET_OS_start_process (int pipe_control,
1128 struct GNUNET_DISK_PipeHandle *pipe_stdin,
1129 struct GNUNET_DISK_PipeHandle *pipe_stdout,
1130 const char *filename, ...)
1132 struct GNUNET_OS_Process *ret;
1135 va_start (ap, filename);
1136 ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
1145 * @param pipe_control should a pipe be used to send signals to the child?
1146 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
1147 * must be NULL on platforms where dup is not supported
1148 * @param filename name of the binary
1149 * @param argv NULL-terminated list of arguments to the process
1150 * @return process ID of the new process, -1 on error
1152 struct GNUNET_OS_Process *
1153 GNUNET_OS_start_process_v (int pipe_control,
1154 const SOCKTYPE *lsocks,
1155 const char *filename,
1162 struct GNUNET_OS_Process *gnunet_proc = NULL;
1163 char *childpipename = NULL;
1172 if ( (GNUNET_YES == pipe_control) &&
1173 (GNUNET_OK != npipe_setup (&childpipename)) )
1180 while (-1 != (k = lsocks[i++]))
1181 GNUNET_array_append (lscp, ls, k);
1182 GNUNET_array_append (lscp, ls, -1);
1187 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
1188 GNUNET_free_non_null (childpipename);
1189 GNUNET_array_grow (lscp, ls, 0);
1194 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1195 gnunet_proc->pid = ret;
1196 gnunet_proc->childpipename = childpipename;
1197 GNUNET_array_grow (lscp, ls, 0);
1200 if (NULL != childpipename)
1202 setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
1203 GNUNET_free (childpipename);
1207 /* read systemd documentation... */
1208 GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
1209 setenv ("LISTEN_PID", lpid, 1);
1212 while (-1 != lscp[i])
1215 while (-1 != lscp[j])
1221 GNUNET_assert (-1 != k);
1222 GNUNET_assert (0 == close (lscp[j]));
1230 /* Bury any existing FD, no matter what; they should all be closed
1231 * on exec anyway and the important onces have been dup'ed away */
1233 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
1235 /* unset close-on-exec flag */
1236 flags = fcntl (tgt, F_GETFD);
1237 GNUNET_assert (flags >= 0);
1238 flags &= ~FD_CLOEXEC;
1240 (void) fcntl (tgt, F_SETFD, flags);
1244 GNUNET_snprintf (fds, sizeof (fds), "%u", i);
1245 setenv ("LISTEN_FDS", fds, 1);
1247 GNUNET_array_grow (lscp, ls, 0);
1248 execvp (filename, argv);
1249 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
1252 struct GNUNET_DISK_FileHandle *control_pipe = NULL;
1253 char *childpipename = NULL;
1254 char **arg, **non_const_argv;
1255 unsigned int cmdlen;
1258 PROCESS_INFORMATION proc;
1260 struct GNUNET_OS_Process *gnunet_proc = NULL;
1261 char path[MAX_PATH + 1];
1262 char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
1263 char *env_block = NULL;
1265 DWORD pathbuf_len, alloc_len;
1270 char *non_const_filename;
1271 char win_path[MAX_PATH + 1];
1272 struct GNUNET_DISK_PipeHandle *lsocks_pipe;
1273 const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
1275 HANDLE lsocks_write;
1276 wchar_t *wpath, *wcmd;
1277 size_t wpath_len, wcmd_len;
1282 /* Search in prefix dir (hopefully - the directory from which
1283 * the current module was loaded), bindir and libdir, then in PATH
1285 self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
1286 bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
1287 libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
1289 pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
1292 pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
1295 pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
1298 ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
1299 GNUNET_free (self_prefix);
1300 GNUNET_free (bindir);
1301 GNUNET_free (libdir);
1303 alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
1304 if (alloc_len != pathbuf_len - 1)
1306 GNUNET_free (pathbuf);
1307 errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
1311 cmdlen = strlen (filename);
1312 if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
1313 GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
1315 GNUNET_asprintf (&non_const_filename, "%s", filename);
1317 /* It could be in POSIX form, convert it to a DOS path early on */
1318 if (ERROR_SUCCESS != (lRet = plibc_conv_to_win_path (non_const_filename, win_path)))
1320 SetErrnoFromWinError (lRet);
1321 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "plibc_conv_to_win_path",
1322 non_const_filename);
1323 GNUNET_free (non_const_filename);
1324 GNUNET_free (pathbuf);
1327 GNUNET_free (non_const_filename);
1328 non_const_filename = GNUNET_strdup (win_path);
1329 /* Check that this is the full path. If it isn't, search. */
1330 /* FIXME: convert it to wchar_t and use SearchPathW?
1331 * Remember: arguments to _start_process() are technically in UTF-8...
1333 if (non_const_filename[1] == ':')
1334 snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
1335 else if (!SearchPathA
1336 (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
1339 SetErrnoFromWinError (GetLastError ());
1340 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
1341 non_const_filename);
1342 GNUNET_free (non_const_filename);
1343 GNUNET_free (pathbuf);
1346 GNUNET_free (pathbuf);
1347 GNUNET_free (non_const_filename);
1349 /* Count the number of arguments */
1350 arg = (char **) argv;
1357 /* Allocate a copy argv */
1358 non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
1360 /* Copy all argv strings */
1362 arg = (char **) argv;
1366 non_const_argv[argcount] = GNUNET_strdup (path);
1368 non_const_argv[argcount] = GNUNET_strdup (*arg);
1372 non_const_argv[argcount] = NULL;
1376 arg = non_const_argv;
1379 cmdlen = cmdlen + strlen (*arg) + 4;
1383 /* Allocate and create cmd */
1384 cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
1385 arg = non_const_argv;
1388 char arg_last_char = (*arg)[strlen (*arg) - 1];
1389 idx += sprintf (idx, "\"%s%s\"%s", *arg,
1390 arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
1394 while (argcount > 0)
1395 GNUNET_free (non_const_argv[--argcount]);
1396 GNUNET_free (non_const_argv);
1398 memset (&start, 0, sizeof (start));
1399 start.cb = sizeof (start);
1401 if (GNUNET_YES == pipe_control)
1404 npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
1405 GNUNET_DISK_PERM_USER_READ |
1406 GNUNET_DISK_PERM_USER_WRITE);
1407 if (control_pipe == NULL)
1415 control_pipe = NULL;
1416 if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
1418 lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
1420 if (lsocks_pipe == NULL)
1424 GNUNET_DISK_pipe_close (lsocks_pipe);
1427 lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
1428 GNUNET_DISK_PIPE_END_WRITE);
1429 GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
1430 &lsocks_write, sizeof (HANDLE));
1431 GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
1432 (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
1433 &lsocks_read, sizeof (HANDLE));
1437 if (NULL != childpipename)
1439 LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
1441 GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
1442 GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
1443 GNUNET_free (childpipename);
1445 if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
1447 /*This will tell the child that we're going to send lsocks over the pipe*/
1448 GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
1449 GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
1451 our_env[env_off++] = NULL;
1452 env_block = CreateCustomEnvTable (our_env);
1454 GNUNET_free_non_null (our_env[--env_off]);
1457 if (NULL == (wpath = u8_to_u16 ((uint8_t *) path, 1 + strlen (path), NULL, &wpath_len)))
1459 LOG (GNUNET_ERROR_TYPE_DEBUG,
1460 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", path, errno);
1461 GNUNET_free (env_block);
1467 if (NULL == (wcmd = u8_to_u16 ((uint8_t *) cmd, 1 + strlen (cmd), NULL, &wcmd_len)))
1469 LOG (GNUNET_ERROR_TYPE_DEBUG,
1470 "Failed to convert `%s' from UTF-8 to UTF-16: %d\n", cmd, errno);
1471 GNUNET_free (env_block);
1477 if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE,
1478 DETACHED_PROCESS | CREATE_SUSPENDED, env_block, NULL, &start, &proc))
1480 SetErrnoFromWinError (GetLastError ());
1481 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1482 if (NULL != control_pipe)
1483 GNUNET_DISK_file_close (control_pipe);
1485 GNUNET_DISK_pipe_close (lsocks_pipe);
1486 GNUNET_free (env_block);
1493 GNUNET_free (env_block);
1495 gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
1496 gnunet_proc->pid = proc.dwProcessId;
1497 gnunet_proc->handle = proc.hProcess;
1498 gnunet_proc->control_pipe = control_pipe;
1500 CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
1502 ResumeThread (proc.hThread);
1503 CloseHandle (proc.hThread);
1508 if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
1511 GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
1513 /* This is a replacement for "goto error" that doesn't use goto */
1518 uint64_t size, count, i;
1520 /* Tell the number of sockets */
1521 for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
1523 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1524 if (wrote != sizeof (count))
1526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
1529 for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
1531 WSAPROTOCOL_INFOA pi;
1532 /* Get a socket duplication info */
1533 if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
1535 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
1536 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
1539 /* Synchronous I/O is not nice, but we can't schedule this:
1540 * lsocks will be closed/freed by the caller soon, and until
1541 * the child creates a duplicate, closing a socket here will
1542 * close it for good.
1544 /* Send the size of the structure
1545 * (the child might be built with different headers...)
1548 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
1549 if (wrote != sizeof (size))
1551 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
1554 /* Finally! Send the data */
1555 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
1556 if (wrote != sizeof (pi))
1558 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
1562 /* This will block us until the child makes a final read or closes
1563 * the pipe (hence no 'wrote' check), since we have to wait for it
1564 * to duplicate the last socket, before we return and start closing
1567 wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
1572 GNUNET_DISK_file_sync (lsocks_write_fd);
1573 GNUNET_DISK_pipe_close (lsocks_pipe);
1577 /* If we can't pass on the socket(s), the child will block forever,
1578 * better put it out of its misery.
1580 TerminateProcess (gnunet_proc->handle, 0);
1581 CloseHandle (gnunet_proc->handle);
1582 if (NULL != gnunet_proc->control_pipe)
1583 GNUNET_DISK_file_close (gnunet_proc->control_pipe);
1584 GNUNET_free (gnunet_proc);
1593 * Retrieve the status of a process, waiting on him if dead.
1594 * Nonblocking version.
1596 * @param proc process ID
1597 * @param type status type
1598 * @param code return code/signal number
1599 * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
1602 GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
1603 enum GNUNET_OS_ProcessStatusType *type,
1604 unsigned long *code)
1610 GNUNET_assert (0 != proc);
1611 ret = waitpid (proc->pid, &status, WNOHANG);
1614 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1615 return GNUNET_SYSERR;
1619 *type = GNUNET_OS_PROCESS_RUNNING;
1623 if (proc->pid != ret)
1625 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1626 return GNUNET_SYSERR;
1628 if (WIFEXITED (status))
1630 *type = GNUNET_OS_PROCESS_EXITED;
1631 *code = WEXITSTATUS (status);
1633 else if (WIFSIGNALED (status))
1635 *type = GNUNET_OS_PROCESS_SIGNALED;
1636 *code = WTERMSIG (status);
1638 else if (WIFSTOPPED (status))
1640 *type = GNUNET_OS_PROCESS_SIGNALED;
1641 *code = WSTOPSIG (status);
1644 else if (WIFCONTINUED (status))
1646 *type = GNUNET_OS_PROCESS_RUNNING;
1652 *type = GNUNET_OS_PROCESS_UNKNOWN;
1657 DWORD c, error_code, ret;
1661 if (h == NULL || ret == 0)
1663 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1665 return GNUNET_SYSERR;
1668 h = GetCurrentProcess ();
1671 ret = GetExitCodeProcess (h, &c);
1672 error_code = GetLastError ();
1673 if (ret == 0 || error_code != NO_ERROR)
1675 SetErrnoFromWinError (error_code);
1676 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
1677 return GNUNET_SYSERR;
1679 if (STILL_ACTIVE == c)
1681 *type = GNUNET_OS_PROCESS_RUNNING;
1685 *type = GNUNET_OS_PROCESS_EXITED;
1694 * Wait for a process
1695 * @param proc pointer to process structure
1696 * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
1699 GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
1703 pid_t pid = proc->pid;
1706 while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
1707 (EINTR == errno) ) ;
1710 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
1711 return GNUNET_SYSERR;
1721 LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
1723 return GNUNET_SYSERR;
1726 h = GetCurrentProcess ();
1728 if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
1730 SetErrnoFromWinError (GetLastError ());
1731 ret = GNUNET_SYSERR;
1742 * Handle to a command.
1744 struct GNUNET_OS_CommandHandle
1750 struct GNUNET_OS_Process *eip;
1753 * Handle to the output pipe.
1755 struct GNUNET_DISK_PipeHandle *opipe;
1758 * Read-end of output pipe.
1760 const struct GNUNET_DISK_FileHandle *r;
1763 * Function to call on each line of output.
1765 GNUNET_OS_LineProcessor proc;
1768 * Closure for 'proc'.
1773 * Buffer for the output.
1778 * Task reading from pipe.
1780 GNUNET_SCHEDULER_TaskIdentifier rtask;
1785 struct GNUNET_TIME_Absolute timeout;
1788 * Current read offset in buf.
1795 * Stop/kill a command. Must ONLY be called either from
1796 * the callback after 'NULL' was passed for 'line' *OR*
1797 * from an independent task (not within the line processor).
1799 * @param cmd handle to the process
1802 GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
1805 if (cmd->proc != NULL)
1807 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
1808 GNUNET_SCHEDULER_cancel (cmd->rtask);
1810 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
1811 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
1812 GNUNET_OS_process_destroy (cmd->eip);
1813 GNUNET_DISK_pipe_close (cmd->opipe);
1819 * Read from the process and call the line processor.
1821 * @param cls the 'struct GNUNET_OS_CommandHandle'
1822 * @param tc scheduler context
1825 cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1827 struct GNUNET_OS_CommandHandle *cmd = cls;
1828 GNUNET_OS_LineProcessor proc;
1832 cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
1833 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
1835 /* timeout, shutdown, etc. */
1838 proc (cmd->proc_cls, NULL);
1842 GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
1843 sizeof (cmd->buf) - cmd->off);
1846 if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
1848 cmd->buf[cmd->off] = '\0';
1849 cmd->proc (cmd->proc_cls, cmd->buf);
1853 proc (cmd->proc_cls, NULL);
1856 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1861 cmd->proc (cmd->proc_cls, cmd->buf);
1862 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1863 cmd->off -= (end + 1 - cmd->buf);
1864 end = memchr (cmd->buf, '\n', cmd->off);
1867 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
1868 (cmd->timeout), cmd->r, &cmd_read, cmd);
1873 * Run the given command line and call the given function
1874 * for each line of the output.
1876 * @param proc function to call for each line of the output
1877 * @param proc_cls closure for proc
1878 * @param timeout when to time out
1879 * @param binary command to run
1880 * @param ... arguments to command
1881 * @return NULL on error
1883 struct GNUNET_OS_CommandHandle *
1884 GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
1885 struct GNUNET_TIME_Relative timeout, const char *binary,
1888 struct GNUNET_OS_CommandHandle *cmd;
1889 struct GNUNET_OS_Process *eip;
1890 struct GNUNET_DISK_PipeHandle *opipe;
1893 opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
1896 va_start (ap, binary);
1897 eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
1901 GNUNET_DISK_pipe_close (opipe);
1904 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1905 cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
1906 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1910 cmd->proc_cls = proc_cls;
1911 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1912 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1919 /* end of os_priority.c */