Index: src/include/gnunet_os_lib.h =================================================================== --- src/include/gnunet_os_lib.h (revision 13625) +++ src/include/gnunet_os_lib.h (working copy) @@ -273,6 +273,18 @@ */ int GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc); +/** + * Read env variable and set up the read end of the control + * pipe that parent uses to send signals to this process. + * + * Only does something on Windows + * + * @param cls user-specified parameter (NULL) + * @param tc task context + */ +void GNUNET_OS_install_parent_control_handler (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc); #if 0 /* keep Emacsens' auto-indent happy */ { Index: src/util/scheduler.c =================================================================== --- src/util/scheduler.c (revision 13625) +++ src/util/scheduler.c (working copy) @@ -744,6 +744,7 @@ shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown); #endif current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT; + GNUNET_SCHEDULER_add_continuation (GNUNET_OS_install_parent_control_handler, NULL, GNUNET_SCHEDULER_REASON_STARTUP); GNUNET_SCHEDULER_add_continuation (task, task_cls, GNUNET_SCHEDULER_REASON_STARTUP); @@ -1351,8 +1352,7 @@ GNUNET_assert (rfd != NULL); rs = GNUNET_NETWORK_fdset_create (); GNUNET_NETWORK_fdset_handle_set (rs, rfd); - ret = GNUNET_SCHEDULER_add_select (sched, - GNUNET_SCHEDULER_PRIORITY_KEEP, + ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP, GNUNET_SCHEDULER_NO_TASK, delay, rs, NULL, task, task_cls); GNUNET_NETWORK_fdset_destroy (rs); @@ -1400,8 +1400,7 @@ GNUNET_assert (wfd != NULL); ws = GNUNET_NETWORK_fdset_create (); GNUNET_NETWORK_fdset_handle_set (ws, wfd); - ret = GNUNET_SCHEDULER_add_select (sched, - GNUNET_SCHEDULER_PRIORITY_KEEP, + ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP, GNUNET_SCHEDULER_NO_TASK, delay, NULL, ws, task, task_cls); GNUNET_NETWORK_fdset_destroy (ws); Index: src/util/os_priority.c =================================================================== --- src/util/os_priority.c (revision 13625) +++ src/util/os_priority.c (working copy) @@ -29,28 +29,95 @@ #include "gnunet_os_lib.h" #include "disk.h" +#define GNUNET_OS_NT_CONTROL_PIPE "GNUNET_OS_NT_CONTROL_PIPE" + struct GNUNET_OS_Process { pid_t pid; #if WINDOWS HANDLE handle; + struct GNUNET_DISK_PipeHandle *control_pipe; #endif }; static struct GNUNET_OS_Process current_process; - #if WINDOWS -void -GNUNET_OS_process_set_handle(struct GNUNET_OS_Process *proc, HANDLE handle) +void 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_PipeHandle *control_pipe = (struct GNUNET_DISK_PipeHandle *) cls; + unsigned char code; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_OS_parent_control_handler is invoked because of %d\n", tc->reason); + + if (tc->reason & (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT | GNUNET_SCHEDULER_REASON_PREREQ_DONE)) + { + GNUNET_DISK_pipe_close (control_pipe); + } + else + { + if (GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_READ), &code, sizeof (char)) != sizeof (char)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_DISK_file_read have failed, errno = %d. Closing the control pipe.\n", errno); + GNUNET_DISK_pipe_close (control_pipe); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d\n", code); + switch (code) + { + case 0: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Raising SIGTERM\n"); + raise (SIGTERM); + break; + case 1: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Raising SIGKILL\n"); + raise (SIGKILL); + break; + case 255: + /* read more bytes - a possibility for extended signals + * in case 254 different valies is not enough + */ + break; + default: + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Re-adding parent control handler pipe to the scheduler\n"); + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_READ), GNUNET_OS_parent_control_handler, control_pipe); + } + } } #endif +void +GNUNET_OS_install_parent_control_handler (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc) +{ +#ifdef WINDOWS + char *env_buf; + char *endptr; + HANDLE control_pipe_end[2]; + struct GNUNET_DISK_PipeHandle *control_pipe = NULL; + env_buf = getenv (GNUNET_OS_NT_CONTROL_PIPE); + if (env_buf == NULL || strlen (env_buf) <= 0 || env_buf[0] < '0' || env_buf[0] > '9') + return; + + control_pipe_end[0] = (HANDLE) strtol (env_buf, &endptr, 10); + control_pipe_end[1] = INVALID_HANDLE_VALUE; + + control_pipe = GNUNET_DISK_pipe_from_internal_handles_ (GNUNET_NO, &control_pipe_end[0], &control_pipe_end[1], sizeof (HANDLE)); + if (control_pipe == NULL) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding parent control handler pipe to the scheduler\n"); + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_READ), GNUNET_OS_parent_control_handler, control_pipe); +#endif +} + /** * Get process structure for current process * @@ -66,7 +133,7 @@ current_process.pid = GetCurrentProcessId (); current_process.handle = GetCurrentProcess (); #else - current_process.pid = 0; + current_process.pid = getpid (); #endif return ¤t_process; } @@ -75,27 +142,49 @@ GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig) { #if WINDOWS - if (sig == SIGKILL || sig == SIGTERM) + unsigned char c; + DWORD dwresult, error_code = NO_ERROR; + BOOL bresult = 1; + + switch (sig) { - HANDLE h = proc->handle; - if (NULL == h) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Invalid process information {%d, %08X}\n"), - proc->pid, - h); + case SIGKILL: + c = 1; + break; + case SIGTERM: + c = 0; + break; + default: + errno = EINVAL; return -1; - } - if (!TerminateProcess (h, 0)) + } + + if (GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (proc->control_pipe, GNUNET_DISK_PIPE_END_WRITE), &c, sizeof (c)) != sizeof (c)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to write into control pipe, errno is %d\n", errno); + bresult = 0; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wrote control code into control pipe, now waiting\n"); + SetLastError (0); + dwresult = WaitForSingleObject (proc->handle, 4*1000); + error_code = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Woke up, returned %d, GLE is %d\n", dwresult, error_code); + if (WAIT_OBJECT_0 != dwresult || error_code != NO_ERROR) { - SetErrnoFromWinError (GetLastError ()); - return -1; + SetLastError (0); + bresult = TerminateProcess (proc->handle, 0); + error_code = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TerminateProcess for the child returned %d, GLE is %d\n", bresult, error_code); } - else - return 0; } - errno = EINVAL; - return -1; + if (error_code != NO_ERROR || !bresult) + { + return -1; + } + else + return 0; #else return kill (proc->pid, sig); #endif @@ -130,15 +219,10 @@ extern GNUNET_SIGNAL_Handler w32_sigchld_handler; /** - * Make seaspider happy. - */ -#define DWORD_WINAPI DWORD WINAPI - -/** * @brief Waits for a process to terminate and invokes the SIGCHLD handler * @param proc pointer to process structure */ -static DWORD_WINAPI +static DWORD WINAPI ChildWaitThread (void *arg) { struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg; @@ -368,19 +452,32 @@ STARTUPINFO start; PROCESS_INFORMATION proc; struct GNUNET_OS_Process *gnunet_proc = NULL; + struct GNUNET_DISK_PipeHandle *control_pipe = NULL; + char *env_buf; HANDLE stdin_handle; HANDLE stdout_handle; + HANDLE control_pipe_read_end, control_pipe_write_end; char path[MAX_PATH + 1]; - cmdlen = 0; + if ((HINSTANCE) 32 >= FindExecutableA (filename, NULL, path)) + { + SetErrnoFromWinError (GetLastError ()); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename); + return NULL; + } + + cmdlen = strlen (path) + 3; va_start (ap, filename); while (NULL != (arg = va_arg (ap, char *))) + { cmdlen = cmdlen + strlen (arg) + 3; + } va_end (ap); cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1)); + idx += sprintf (idx, "\"%s\" ", path); va_start (ap, filename); while (NULL != (arg = va_arg (ap, char *))) idx += sprintf (idx, "\"%s\" ", arg); @@ -404,30 +501,42 @@ 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_pipe (GNUNET_NO, 1, 0); + GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_READ), &control_pipe_read_end, sizeof (HANDLE)); + GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_WRITE), &control_pipe_write_end, sizeof (HANDLE)); + GNUNET_asprintf (&env_buf, "%s=%d", GNUNET_OS_NT_CONTROL_PIPE, control_pipe_read_end); + putenv (env_buf); + GNUNET_free (env_buf); + if (!CreateProcessA (path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &start, &proc)) { SetErrnoFromWinError (GetLastError ()); GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path); - return NULL; + goto end; } 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); CloseHandle (proc.hThread); +end: + GNUNET_asprintf (&env_buf, "%s=", GNUNET_OS_NT_CONTROL_PIPE); + putenv (env_buf); + GNUNET_free (env_buf); + + if (gnunet_proc == NULL) + GNUNET_DISK_pipe_close (control_pipe); + else + GNUNET_DISK_pipe_close_end (control_pipe, GNUNET_DISK_PIPE_END_READ); + GNUNET_free (cmd); return gnunet_proc; @@ -557,10 +666,13 @@ int argcount = 0; char non_const_filename[MAX_PATH +1]; struct GNUNET_OS_Process *gnunet_proc = NULL; + struct GNUNET_DISK_PipeHandle *control_pipe = NULL; + char *env_buf; + HANDLE control_pipe_read_end, control_pipe_write_end; GNUNET_assert (lsocks == NULL); - if (32 >= (int) FindExecutableA (filename, NULL, non_const_filename)) + if ((HINSTANCE) 32 >= FindExecutableA (filename, NULL, non_const_filename)) { SetErrnoFromWinError (GetLastError ()); GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "FindExecutable", filename); @@ -583,7 +695,12 @@ arg = (char **) argv; while (*arg) { - non_const_argv[argcount] = GNUNET_strdup (*arg); + if (argcount == 0) + /* Otherwise we'll get the argv[0] without .exe extension, + that will break libtool wrapper */ + non_const_argv[argcount] = GNUNET_strdup (non_const_filename); + else + non_const_argv[argcount] = GNUNET_strdup (*arg); arg++; argcount++; } @@ -610,22 +727,43 @@ memset (&start, 0, sizeof (start)); start.cb = sizeof (start); - if (!CreateProcess - (non_const_filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start, + control_pipe = GNUNET_DISK_pipe (GNUNET_NO, 1, 0); + GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_READ), &control_pipe_read_end, sizeof (HANDLE)); + GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle (control_pipe, GNUNET_DISK_PIPE_END_WRITE), &control_pipe_write_end, sizeof (HANDLE)); + + + GNUNET_asprintf (&env_buf, "%s=%d", GNUNET_OS_NT_CONTROL_PIPE, control_pipe_read_end); + putenv (env_buf); + GNUNET_free (env_buf); + + if (!CreateProcessA + (non_const_filename, cmd, NULL, NULL, TRUE, + DETACHED_PROCESS, NULL, NULL, &start, &proc)) { SetErrnoFromWinError (GetLastError ()); GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "CreateProcess"); - return NULL; + goto end; } 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); CloseHandle (proc.hThread); +end: + GNUNET_asprintf (&env_buf, "%s=", GNUNET_OS_NT_CONTROL_PIPE); + putenv (env_buf); + GNUNET_free (env_buf); + + if (gnunet_proc == NULL) + GNUNET_DISK_pipe_close (control_pipe); + else + GNUNET_DISK_pipe_close_end (control_pipe, GNUNET_DISK_PIPE_END_READ); + GNUNET_free (cmd); while (argcount > 0) Index: src/util/network.c =================================================================== --- src/util/network.c (revision 13625) +++ src/util/network.c (working copy) @@ -470,8 +470,8 @@ #ifdef MSG_DONTWAIT flags |= MSG_DONTWAIT; +#endif -#endif ret = recvfrom (desc->fd, buffer, length, flags, src_addr, addrlen); #ifdef MINGW if (SOCKET_ERROR == ret) @@ -1138,9 +1138,19 @@ { HANDLE h; - DWORD dwBytes; + DWORD dwBytes = 0, error_code; + BOOL bresult; h = *(HANDLE *) GNUNET_CONTAINER_slist_get (i, NULL); - if (!PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL)) + bresult = PeekNamedPipe (h, NULL, 0, NULL, &dwBytes, NULL); + error_code = GetLastError (); + /* FIXME: This should fail when the pipe is _really_ invalid, + * should skip the pipe if the error means that it's + * temporarily unavailable, and should succeed if the pipe + * is broken (or that the otherwise valid pipe is in a condition + * that prevents further reading). + * For now it succeeds only when the pipe is broken or readable. + */ + if (!bresult && error_code != ERROR_BROKEN_PIPE) { retcode = -1; SetErrnoFromWinError (GetLastError ()); @@ -1152,7 +1162,7 @@ #endif goto select_loop_end; } - else if (dwBytes) + else if (dwBytes || error_code == ERROR_BROKEN_PIPE) { GNUNET_CONTAINER_slist_add (handles_read, Index: src/util/disk.c =================================================================== --- src/util/disk.c (revision 13625) +++ src/util/disk.c (working copy) @@ -1910,4 +1910,95 @@ return GNUNET_OK; } +struct GNUNET_DISK_FileHandle +GNUNET_DISK_file_from_internal_handle_ (void *src, size_t src_len) +{ + struct GNUNET_DISK_FileHandle ret; +#ifdef MINGW + if (src_len == sizeof (HANDLE)) + ret.h = *(HANDLE *) src; + else + ret.h = INVALID_HANDLE_VALUE; +#else + if (src_len == sizeof (int)) + ret.fd = *(int *) src; + else + ret.fd = -1; +#endif + return ret; +} + +struct GNUNET_DISK_PipeHandle * +GNUNET_DISK_pipe_from_internal_handles_ (int blocking, void *src0, void *src1, size_t src_len) +{ + struct GNUNET_DISK_PipeHandle *p; + struct GNUNET_DISK_FileHandle *fds; + + p = + GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) + + 2 * sizeof (struct GNUNET_DISK_FileHandle)); + fds = (struct GNUNET_DISK_FileHandle *) &p[1]; + p->fd[0] = &fds[0]; + p->fd[1] = &fds[1]; + fds[0] = GNUNET_DISK_file_from_internal_handle_ (src0, src_len); + fds[1] = GNUNET_DISK_file_from_internal_handle_ (src1, src_len); +#ifndef MINGW + int fd[2]; + int ret; + int flags; + int eno; + + fd[0] = fds[0].fd; + fd[1] = fds[1].fd; + ret = 0; + if (fd[0] >= 0) + { + flags = fcntl (fd[0], F_GETFL); + if (!blocking) + flags |= O_NONBLOCK; + if (0 > fcntl (fd[0], F_SETFL, flags)) + ret = -1; + flags = fcntl (fd[0], F_GETFD); + flags |= FD_CLOEXEC; + if (0 > fcntl (fd[0], F_SETFD, flags)) + ret = -1; + } + if (fd[1] >= 0) + { + flags = fcntl (fd[1], F_GETFL); + if (!blocking) + flags |= O_NONBLOCK; + if (0 > fcntl (fd[1], F_SETFL, flags)) + ret = -1; + flags = fcntl (fd[1], F_GETFD); + flags |= FD_CLOEXEC; + if (0 > fcntl (fd[1], F_SETFD, flags)) + ret = -1; + } + if (ret == -1) + { + eno = errno; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl"); + GNUNET_free (p); + errno = eno; + return NULL; + } +#else + /* Empirical fact: DuplicateHandle() on a handle inherited from the parent + * dies with ERROR_INVALID_HANDLE, so we won't try to duplicate these to + * make them inheritable/uninheritable. + */ + if (!blocking) + { + DWORD mode; + + mode = PIPE_NOWAIT; + SetNamedPipeHandleState (p->fd[0]->h, &mode, NULL, NULL); + SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL); + /* this always fails on Windows 95, so we don't care about error handling */ + } +#endif + return p; +} + /* end of disk.c */ Index: src/util/disk.h =================================================================== --- src/util/disk.h (revision 13625) +++ src/util/disk.h (working copy) @@ -72,4 +72,8 @@ size_t dst_len); +struct GNUNET_DISK_PipeHandle *GNUNET_DISK_pipe_from_internal_handles_ (int blocking, void *src0, void *src1, size_t src_len); + + + #endif /* GNUNET_DISK_H_ */