From 22e893cbfab6d19abb860a13778850e9df4e82d7 Mon Sep 17 00:00:00 2001 From: Nils Durner Date: Mon, 27 Dec 2010 09:53:48 +0000 Subject: [PATCH] shutdown via control pipe (#0001616) --- configure.ac | 17 ++ src/include/gnunet_disk_lib.h | 15 +- src/include/gnunet_os_lib.h | 8 + src/util/disk.c | 133 ++++++++- src/util/network.c | 21 +- src/util/os_priority.c | 506 ++++++++++++++++++++++++++++++---- src/util/scheduler.c | 5 + 7 files changed, 635 insertions(+), 70 deletions(-) diff --git a/configure.ac b/configure.ac index 8c747d283..6f6e47ea4 100644 --- a/configure.ac +++ b/configure.ac @@ -755,6 +755,23 @@ AC_MSG_RESULT($enable_malicious) AM_CONDITIONAL([HAVE_MALICIOUS], [test "x$enable_malicious" = "x1"]) AC_DEFINE_UNQUOTED([HAVE_MALICIOUS], $enable_malicious, [Compile malicious code]) +# should code be enabled that works around missing OS functionality on Windows? +# used for test cases +AC_ARG_ENABLE(windows_workarounds, [AS_HELP_STRING([--enable-windows_workarounds], + [enable workarounds used on Windows (only useful for test cases)])]) +if test $build_target = "mingw" +then + workarounds=1 +else + if test x$enable_windows_workarounds = "xyes" + then + workarounds=1 + else + workarounds=0 + fi +fi +AC_DEFINE_UNQUOTED([ENABLE_WINDOWS_WORKAROUNDS], $workarounds, [enable workarounds used on Windows (only useful for test cases)]) + # gcov compilation use_gcov=no AC_ARG_ENABLE([coverage], AS_HELP_STRING([--enable-coverage], diff --git a/src/include/gnunet_disk_lib.h b/src/include/gnunet_disk_lib.h index a59d10ed0..383a9daa2 100644 --- a/src/include/gnunet_disk_lib.h +++ b/src/include/gnunet_disk_lib.h @@ -643,8 +643,19 @@ int GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h); int GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h); /** - * Creates a named pipe/FIFO - * @param fn name of the named pipe + * Creates a named pipe/FIFO and opens it + * @param fn pointer to the name of the named pipe or to NULL + * @param flags open flags + * @param perm access permissions + * @return pipe handle on success, NULL on error + */ +struct GNUNET_DISK_FileHandle *GNUNET_DISK_npipe_create (char **fn, + enum GNUNET_DISK_OpenFlags flags, enum GNUNET_DISK_AccessPermissions perm); + +/** + * Opens already existing named pipe/FIFO + * + * @param fn name of an existing named pipe * @param flags open flags * @param perm access permissions * @return pipe handle on success, NULL on error diff --git a/src/include/gnunet_os_lib.h b/src/include/gnunet_os_lib.h index 5326e20cf..7bd65adcb 100644 --- a/src/include/gnunet_os_lib.h +++ b/src/include/gnunet_os_lib.h @@ -274,6 +274,14 @@ int GNUNET_OS_process_status (struct GNUNET_OS_Process *proc, int GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc); +/** + * Connects this process to its parent via pipe + */ +void +GNUNET_OS_install_parent_control_handler (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc); + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/util/disk.c b/src/util/disk.c index bb7929551..3c9469ca7 100644 --- a/src/util/disk.c +++ b/src/util/disk.c @@ -31,8 +31,10 @@ #include "gnunet_disk_lib.h" #include "gnunet_scheduler_lib.h" #include "gnunet_strings_lib.h" +#include "gnunet_crypto_lib.h" #include "disk.h" +#define DEBUG_NPIPE GNUNET_YES /** * Block size for IO for copying files. @@ -1876,20 +1878,20 @@ GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p) /** - * Creates a named pipe/FIFO - * @param fn name of the named pipe + * Creates a named pipe/FIFO and opens it + * @param fn pointer to the name of the named pipe or to NULL * @param flags open flags * @param perm access permissions * @return pipe handle on success, NULL on error */ struct GNUNET_DISK_FileHandle * -GNUNET_DISK_npipe_open (const char *fn, - enum GNUNET_DISK_OpenFlags flags, - enum GNUNET_DISK_AccessPermissions perm) +GNUNET_DISK_npipe_create (char **fn, + enum GNUNET_DISK_OpenFlags flags, + enum GNUNET_DISK_AccessPermissions perm) { #ifdef MINGW struct GNUNET_DISK_FileHandle *ret; - HANDLE h; + HANDLE h = NULL; DWORD openMode; char *name; @@ -1904,22 +1906,80 @@ GNUNET_DISK_npipe_open (const char *fn, if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS) openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; - GNUNET_asprintf(&name, "\\\\.\\pipe\\pipename\\%s", fn); - h = CreateNamedPipe (fn, openMode | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL); - GNUNET_free(name); - if (h == NULL) + while (h == NULL) { - SetErrnoFromWinError(GetLastError()); - return NULL; + DWORD error_code; + name = NULL; + if (*fn != NULL) + { + GNUNET_asprintf(&name, "\\\\.\\pipe\\%.246s", fn); +#if DEBUG_NPIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create an instance of named pipe `%s'\n", name); +#endif + h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL); + } + else + { + GNUNET_asprintf(fn, "\\\\.\\pipe\\gnunet-%llu", + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX)); +#if DEBUG_NPIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n", *fn); +#endif + h = CreateNamedPipe (*fn, openMode | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL); + } + error_code = GetLastError (); + if (name) + GNUNET_free(name); + /* don't re-set name to NULL yet */ + if (h == INVALID_HANDLE_VALUE) + { + SetErrnoFromWinError(error_code); +#if DEBUG_NPIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe creation have failed because of %d, errno is %d\n", error_code, errno); +#endif + if (name == NULL) + { +#if DEBUG_NPIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe was to be unique, considering re-creation\n"); +#endif + GNUNET_free (*fn); + *fn = NULL; + if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY) + { + return NULL; + } +#if DEBUG_NPIPE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe name was not unique, trying again\n"); +#endif + h = NULL; + } + else + return NULL; + } } + errno = 0; ret = GNUNET_malloc(sizeof(*ret)); ret->h = h; + ret->type = GNUNET_PIPE; return ret; #else - if (mkfifo(fn, translate_unix_perms(perm)) == -1) + if (*fn == NULL) + { + char dir[] = "/tmp/gnunet-pipe-XXXXXX"; + + if (mkdtemp(dir) == NULL) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp"); + return NULL; + } + GNUNET_asprintf(fn, "%s/child-control", dir); + } + + if (mkfifo(*fn, translate_unix_perms(perm)) == -1) { if ( (errno != EEXIST) || (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) ) @@ -1931,6 +1991,51 @@ GNUNET_DISK_npipe_open (const char *fn, #endif } +/** + * Opens already existing named pipe/FIFO + * + * @param fn name of an existing named pipe + * @param flags open flags + * @param perm access permissions + * @return pipe handle on success, NULL on error + */ +struct GNUNET_DISK_FileHandle * +GNUNET_DISK_npipe_open (const char *fn, + enum GNUNET_DISK_OpenFlags flags, + enum GNUNET_DISK_AccessPermissions perm) +{ +#ifdef MINGW + struct GNUNET_DISK_FileHandle *ret; + HANDLE h; + DWORD openMode; + + openMode = 0; + if (flags & GNUNET_DISK_OPEN_READWRITE) + openMode = GENERIC_WRITE | GENERIC_READ; + else if (flags & GNUNET_DISK_OPEN_READ) + openMode = GENERIC_READ; + else if (flags & GNUNET_DISK_OPEN_WRITE) + openMode = GENERIC_WRITE; + + h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL); + if (h == INVALID_HANDLE_VALUE) + { + SetErrnoFromWinError(GetLastError()); + return NULL; + } + + ret = GNUNET_malloc(sizeof(*ret)); + ret->h = h; + ret->type = GNUNET_PIPE; + + return ret; +#else + flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS); + return GNUNET_DISK_file_open(fn, flags, perm); +#endif +} + /** * Closes a named pipe/FIFO * @param pipe named pipe diff --git a/src/util/network.c b/src/util/network.c index 08121928d..9e85a3be9 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -1127,15 +1127,26 @@ GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds, { if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL)) { - retcode = -1; - SetErrnoFromWinError (GetLastError ()); + DWORD error_code = GetLastError (); + switch (error_code) + { + case ERROR_BROKEN_PIPE: + GNUNET_CONTAINER_slist_add (handles_read, + GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, + fh, sizeof (struct GNUNET_DISK_FileHandle)); + retcode++; + break; + default: + retcode = -1; + SetErrnoFromWinError (error_code); #if DEBUG_NETWORK - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "PeekNamedPipe"); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "PeekNamedPipe"); #endif - goto select_loop_end; + goto select_loop_end; + } } else if (dwBytes) diff --git a/src/util/os_priority.c b/src/util/os_priority.c index fb6c2922c..e9ca9deea 100644 --- a/src/util/os_priority.c +++ b/src/util/os_priority.c @@ -27,29 +27,87 @@ #include "platform.h" #include "gnunet_common.h" #include "gnunet_os_lib.h" +#include "gnunet_scheduler_lib.h" #include "disk.h" +#define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE" + struct GNUNET_OS_Process { pid_t pid; #if WINDOWS HANDLE handle; #endif + int sig; + struct GNUNET_DISK_FileHandle *control_pipe; }; static struct GNUNET_OS_Process current_process; -#if WINDOWS +/** + * This handler is called when there are control data to be read on the pipe + */ void -GNUNET_OS_process_set_handle(struct GNUNET_OS_Process *proc, HANDLE handle) +GNUNET_OS_parent_control_handler (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc) { - if (proc->handle != NULL) - CloseHandle (proc->handle); - proc->handle = handle; + struct GNUNET_DISK_FileHandle *control_pipe = (struct GNUNET_DISK_FileHandle *) cls; + int sig; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__, tc->reason); + + if (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE)) + { + GNUNET_DISK_npipe_close (control_pipe); + } + else + { + if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) != sizeof (sig)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read"); + GNUNET_DISK_npipe_close (control_pipe); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig); + raise (sig); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Re-scheduling the parent control handler pipe\n"); + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe, GNUNET_OS_parent_control_handler, control_pipe); + } + } } -#endif +/** + * Connects this process to its parent via pipe + */ +void +GNUNET_OS_install_parent_control_handler (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc) +{ + char *env_buf; + struct GNUNET_DISK_FileHandle *control_pipe = NULL; + + env_buf = getenv (GNUNET_OS_CONTROL_PIPE); + if (env_buf == NULL || strlen (env_buf) <= 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not installing a handler because %s=%s\n", GNUNET_OS_CONTROL_PIPE, env_buf); + return; + } + + control_pipe = GNUNET_DISK_npipe_open (env_buf, GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + if (control_pipe == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to open the pipe `%s'\n", env_buf); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding parent control handler pipe `%s' to the scheduler\n", env_buf); + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe, GNUNET_OS_parent_control_handler, control_pipe); +} /** * Get process structure for current process @@ -74,28 +132,60 @@ GNUNET_OS_process_current () int GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig) { -#if WINDOWS - if (sig == SIGKILL || sig == SIGTERM) +#if ENABLE_WINDOWS_WORKAROUNDS + int res; + int ret; + + ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof(sig)); + if (ret != sizeof(sig)) { - HANDLE h = proc->handle; - if (NULL == h) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Invalid process information {%d, %08X}\n"), - proc->pid, - h); - return -1; - } - if (!TerminateProcess (h, 0)) - { - SetErrnoFromWinError (GetLastError ()); - return -1; - } + if (errno == ECOMM) + /* Child process is not controllable via pipe */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Child process is not controllable, will kill it directly\n"); else - return 0; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to write into control pipe , errno is %d\n", errno); + res = PLIBC_KILL (proc->pid, sig); } - errno = EINVAL; - return -1; + else + { + struct GNUNET_NETWORK_FDSet *rfds; + struct GNUNET_NETWORK_FDSet *efds; + + rfds = GNUNET_NETWORK_fdset_create (); + efds = GNUNET_NETWORK_fdset_create (); + + GNUNET_NETWORK_fdset_handle_set (rfds, proc->control_pipe); + GNUNET_NETWORK_fdset_handle_set (efds, proc->control_pipe); + + read_next: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Wrote control code into control pipe, now waiting\n"); + + ret = GNUNET_NETWORK_socket_select (rfds, NULL, efds, + GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_unit (), + 5000)); + + if (ret < 1 || GNUNET_NETWORK_fdset_handle_isset (efds, + proc->control_pipe)) + { + /* Just to be sure */ + PLIBC_KILL (proc->pid, sig); + res = 0; + } + else + { + if (GNUNET_DISK_file_read (proc->control_pipe, &ret, + sizeof(ret)) != GNUNET_OK) + res = PLIBC_KILL (proc->pid, sig); + + /* Child signaled shutdown is in progress */ + goto read_next; + } + } + + return res; #else return kill (proc->pid, sig); #endif @@ -117,13 +207,19 @@ GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc) void GNUNET_OS_process_close (struct GNUNET_OS_Process *proc) { -#if WINDOWS +#if ENABLE_WINDOWS_WORKAROUNDS + if (proc->control_pipe) + GNUNET_DISK_npipe_close (proc->control_pipe); +#endif +// FIXME NILS +#ifdef WINDOWS if (proc->handle != NULL) CloseHandle (proc->handle); -#endif +#endif GNUNET_free (proc); } +// FIXME NILS #if WINDOWS #include "gnunet_signal_lib.h" @@ -258,6 +354,105 @@ GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc, return GNUNET_OK; } +#if MINGW +static char * +CreateCustomEnvTable (char **vars) +{ + char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr; + size_t tablesize = 0; + size_t items_count = 0; + size_t n_found = 0, n_var; + char *index = NULL; + size_t c; + size_t var_len; + char *var; + char *val; + win32_env_table = GetEnvironmentStringsA (); + if (win32_env_table == NULL) + return NULL; + for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++); + n_var = c; + index = GNUNET_malloc (n_var); + for (c = 0; c < n_var; c++) + index[c] = 0; + for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++) + { + size_t len = strlen (ptr); + int found = 0; + for (var_ptr = vars; *var_ptr; var_ptr++) + { + var = *var_ptr++; + val = *var_ptr; + var_len = strlen (var); + if (strncmp (var, ptr, var_len) == 0) + { + found = 1; + index[c] = 1; + tablesize += var_len + strlen (val) + 1; + break; + } + } + if (!found) + tablesize += len + 1; + ptr += len + 1; + } + for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++) + { + var = *var_ptr++; + val = *var_ptr; + if (index[c] != 1) + n_found += strlen (var) + strlen (val) + 1; + } + result = GNUNET_malloc (tablesize + n_found + 1); + for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;) + { + size_t len = strlen (ptr); + int found = 0; + for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++) + { + var = *var_ptr++; + val = *var_ptr; + var_len = strlen (var); + if (strncmp (var, ptr, var_len) == 0) + { + found = 1; + break; + } + } + if (!found) + { + strcpy (result_ptr, ptr); + result_ptr += len + 1; + } + else + { + strcpy (result_ptr, var); + result_ptr += var_len; + strcpy (result_ptr, val); + result_ptr += strlen (val) + 1; + } + ptr += len + 1; + } + for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++) + { + var = *var_ptr++; + val = *var_ptr; + var_len = strlen (var); + if (index[c] != 1) + { + strcpy (result_ptr, var); + result_ptr += var_len; + strcpy (result_ptr, val); + result_ptr += strlen (val) + 1; + } + } + FreeEnvironmentStrings (win32_env_table); + GNUNET_free (index); + *result_ptr = 0; + return result; +} +#endif + /** * Start a process. * @@ -273,10 +468,14 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, const char *filename, ...) { va_list ap; +#if ENABLE_WINDOWS_WORKAROUNDS + char *childpipename = NULL; + struct GNUNET_DISK_FileHandle *control_pipe = NULL; +#endif + struct GNUNET_OS_Process *gnunet_proc = NULL; #ifndef MINGW pid_t ret; - struct GNUNET_OS_Process *gnunet_proc = NULL; char **argv; int argc; int fd_stdout_write; @@ -284,6 +483,14 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, int fd_stdin_read; int fd_stdin_write; +#if ENABLE_WINDOWS_WORKAROUNDS + control_pipe = GNUNET_DISK_npipe_create (&childpipename, + GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (control_pipe == NULL) + return NULL; +#endif + argc = 0; va_start (ap, filename); while (NULL != va_arg (ap, char *)) @@ -316,6 +523,9 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, if (ret == -1) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); +#if ENABLE_WINDOWS_WORKAROUNDS + GNUNET_DISK_npipe_close (control_pipe); +#endif } else { @@ -337,11 +547,22 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, #endif gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process)); gnunet_proc->pid = ret; +#if ENABLE_WINDOWS_WORKAROUNDS + gnunet_proc->control_pipe = control_pipe; +#endif } GNUNET_free (argv); +#if ENABLE_WINDOWS_WORKAROUNDS + GNUNET_free (childpipename); +#endif return gnunet_proc; } +#if ENABLE_WINDOWS_WORKAROUNDS + setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1); + GNUNET_free (childpipename); +#endif + if (pipe_stdout != NULL) { GNUNET_break (0 == close (fd_stdout_read)); @@ -367,23 +588,84 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, char *cmd, *idx; STARTUPINFO start; PROCESS_INFORMATION proc; - struct GNUNET_OS_Process *gnunet_proc = NULL; HANDLE stdin_handle; HANDLE stdout_handle; char path[MAX_PATH + 1]; + char *our_env[3] = { NULL, NULL, NULL }; + char *env_block = NULL; + char *pathbuf; + DWORD pathbuf_len, alloc_len; + char *self_prefix; + char *bindir; + char *libdir; + char *ptr; + char *non_const_filename; + + /* Search in prefix dir (hopefully - the directory from which + * the current module was loaded), bindir and libdir, then in PATH + */ + self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX); + bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); + libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); + + pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0); + + alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir); + + pathbuf = GNUNET_malloc (alloc_len * sizeof (char)); + + ptr = pathbuf; + ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir); + GNUNET_free (self_prefix); + GNUNET_free (bindir); + GNUNET_free (libdir); + + alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len); + GNUNET_assert (alloc_len == (pathbuf_len - 1)); + + cmdlen = strlen (filename); + if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0) + GNUNET_asprintf (&non_const_filename, "%s.exe", filename); + else + GNUNET_asprintf (&non_const_filename, "%s", filename); + + /* Check that this is the full path. If it isn't, search. */ + if (non_const_filename[1] == ':') + snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename); + else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL)) + { + SetErrnoFromWinError (GetLastError ()); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename); + GNUNET_free (non_const_filename); + GNUNET_free (pathbuf); + return NULL; + } + GNUNET_free (pathbuf); + GNUNET_free (non_const_filename); + cmdlen = 0; va_start (ap, filename); while (NULL != (arg = va_arg (ap, char *))) - cmdlen = cmdlen + strlen (arg) + 3; + { + if (cmdlen == 0) + cmdlen = cmdlen + strlen (path) + 3; + else + cmdlen = cmdlen + strlen (arg) + 3; + } va_end (ap); cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1)); va_start (ap, filename); while (NULL != (arg = va_arg (ap, char *))) - idx += sprintf (idx, "\"%s\" ", arg); + { + if (idx == cmd) + idx += sprintf (idx, "\"%s\" ", path); + else + idx += sprintf (idx, "\"%s\" ", arg); + } va_end (ap); memset (&start, 0, sizeof (start)); @@ -404,28 +686,46 @@ GNUNET_OS_start_process (struct GNUNET_DISK_PipeHandle *pipe_stdin, start.hStdOutput = stdout_handle; } - if (32 >= (int) FindExecutableA (filename, NULL, path)) - { - SetErrnoFromWinError (GetLastError ()); - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename); - return NULL; - } + control_pipe = GNUNET_DISK_npipe_create (&childpipename, + GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (control_pipe == NULL) + { + GNUNET_free (cmd); + GNUNET_free (path); + return NULL; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename); + + GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE); + GNUNET_asprintf (&our_env[1], "%s", childpipename); + our_env[2] = NULL; + env_block = CreateCustomEnvTable (our_env); + GNUNET_free (our_env[0]); + GNUNET_free (our_env[1]); if (!CreateProcessA - (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start, - &proc)) + (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED, + env_block, NULL, &start, &proc)) { SetErrnoFromWinError (GetLastError ()); GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path); + GNUNET_free (env_block); + GNUNET_free (cmd); return NULL; } + GNUNET_free (env_block); + gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process)); gnunet_proc->pid = proc.dwProcessId; gnunet_proc->handle = proc.hProcess; + gnunet_proc->control_pipe = control_pipe; CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL); + ResumeThread (proc.hThread); CloseHandle (proc.hThread); GNUNET_free (cmd); @@ -450,6 +750,11 @@ struct GNUNET_OS_Process * GNUNET_OS_start_process_v (const int *lsocks, const char *filename, char *const argv[]) { +#if ENABLE_WINDOWS_WORKAROUNDS + struct GNUNET_DISK_FileHandle *control_pipe = NULL; + char *childpipename = NULL; +#endif + #ifndef MINGW pid_t ret; char lpid[16]; @@ -463,6 +768,14 @@ GNUNET_OS_start_process_v (const int *lsocks, int *lscp; unsigned int ls; +#if ENABLE_WINDOWS_WORKAROUNDS + control_pipe = GNUNET_DISK_npipe_create (&childpipename, + GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (control_pipe == NULL) + return NULL; +#endif + lscp = NULL; ls = 0; if (lsocks != NULL) @@ -482,6 +795,9 @@ GNUNET_OS_start_process_v (const int *lsocks, if (ret == -1) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); +#if ENABLE_WINDOWS_WORKAROUNDS + GNUNET_DISK_npipe_close (control_pipe); +#endif } else { @@ -498,10 +814,23 @@ GNUNET_OS_start_process_v (const int *lsocks, #endif gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process)); gnunet_proc->pid = ret; +#if ENABLE_WINDOWS_WORKAROUNDS + gnunet_proc->control_pipe = control_pipe; + +#endif } GNUNET_array_grow (lscp, ls, 0); +#if ENABLE_WINDOWS_WORKAROUNDS + GNUNET_free (childpipename); +#endif return gnunet_proc; } + +#if ENABLE_WINDOWS_WORKAROUNDS + setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1); + GNUNET_free (childpipename); +#endif + if (lscp != NULL) { /* read systemd documentation... */ @@ -555,17 +884,68 @@ GNUNET_OS_start_process_v (const int *lsocks, STARTUPINFO start; PROCESS_INFORMATION proc; int argcount = 0; - char non_const_filename[MAX_PATH +1]; struct GNUNET_OS_Process *gnunet_proc = NULL; + char path[MAX_PATH + 1]; + + char *our_env[3] = { NULL, NULL, NULL }; + char *env_block = NULL; + char *pathbuf; + DWORD pathbuf_len, alloc_len; + char *self_prefix; + char *bindir; + char *libdir; + char *ptr; + char *non_const_filename; + GNUNET_assert (lsocks == NULL); - if (32 >= (int) FindExecutableA (filename, NULL, non_const_filename)) + /* Search in prefix dir (hopefully - the directory from which + * the current module was loaded), bindir and libdir, then in PATH + */ + self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX); + bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); + libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); + + pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0); + + alloc_len = pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 + strlen (libdir); + + pathbuf = GNUNET_malloc (alloc_len * sizeof (char)); + + ptr = pathbuf; + ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir); + GNUNET_free (self_prefix); + GNUNET_free (bindir); + GNUNET_free (libdir); + + alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len); + if (alloc_len != pathbuf_len - 1) + { + GNUNET_free (pathbuf); + errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */ + return NULL; + } + + cmdlen = strlen (filename); + if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0) + GNUNET_asprintf (&non_const_filename, "%s.exe", filename); + else + GNUNET_asprintf (&non_const_filename, "%s", filename); + + /* Check that this is the full path. If it isn't, search. */ + if (non_const_filename[1] == ':') + snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename); + else if (!SearchPathA (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char), path, NULL)) { SetErrnoFromWinError (GetLastError ()); - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "SearchPath", non_const_filename); + GNUNET_free (non_const_filename); + GNUNET_free (pathbuf); return NULL; } + GNUNET_free (pathbuf); + GNUNET_free (non_const_filename); /* Count the number of arguments */ arg = (char **) argv; @@ -583,7 +963,10 @@ GNUNET_OS_start_process_v (const int *lsocks, arg = (char **) argv; while (*arg) { - non_const_argv[argcount] = GNUNET_strdup (*arg); + if (arg == argv) + non_const_argv[argcount] = GNUNET_strdup (path); + else + non_const_argv[argcount] = GNUNET_strdup (*arg); arg++; argcount++; } @@ -607,31 +990,56 @@ GNUNET_OS_start_process_v (const int *lsocks, arg++; } + while (argcount > 0) + GNUNET_free (non_const_argv[--argcount]); + GNUNET_free (non_const_argv); + memset (&start, 0, sizeof (start)); start.cb = sizeof (start); + control_pipe = GNUNET_DISK_npipe_create (&childpipename, + GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (control_pipe == NULL) + { + GNUNET_free (cmd); + GNUNET_free (path); + return NULL; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n", childpipename); + + GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE); + GNUNET_asprintf (&our_env[1], "%s", childpipename); + our_env[2] = NULL; + env_block = CreateCustomEnvTable (our_env); + GNUNET_free (our_env[0]); + GNUNET_free (our_env[1]); + if (!CreateProcess - (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start, - &proc)) + (path, cmd, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_SUSPENDED, + env_block, NULL, &start, &proc)) { SetErrnoFromWinError (GetLastError ()); GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess"); + GNUNET_free (env_block); + GNUNET_free (cmd); return NULL; } + GNUNET_free (env_block); + gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process)); gnunet_proc->pid = proc.dwProcessId; gnunet_proc->handle = proc.hProcess; + gnunet_proc->control_pipe = control_pipe; CreateThread (NULL, 64000, ChildWaitThread, (void *) gnunet_proc, 0, NULL); + ResumeThread (proc.hThread); CloseHandle (proc.hThread); GNUNET_free (cmd); - while (argcount > 0) - GNUNET_free (non_const_argv[--argcount]); - GNUNET_free (non_const_argv); - return gnunet_proc; #endif } diff --git a/src/util/scheduler.c b/src/util/scheduler.c index 8a6479929..2d7907395 100644 --- a/src/util/scheduler.c +++ b/src/util/scheduler.c @@ -739,6 +739,11 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls) GNUNET_SCHEDULER_add_continuation (task, task_cls, GNUNET_SCHEDULER_REASON_STARTUP); +#if ENABLE_WINDOWS_WORKAROUNDS + GNUNET_SCHEDULER_add_continuation (GNUNET_OS_install_parent_control_handler, + NULL, GNUNET_SCHEDULER_REASON_STARTUP); +#endif + last_tr = 0; busy_wait_warning = 0; while ((pending != NULL) || -- 2.25.1