/* * Author: Manoj Ampalam * * Copyright (c) 2015 Microsoft Corp. * All rights reserved * * Microsoft openssh win32 port * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "w32fd.h" #include "signal_internal.h" #include "debug.h" /* Apply caution while changing this order of inclusion of below 2 signal.h headers */ #include "inc\signal.h" #undef signal #undef raise #undef SIGINT #undef SIGILL #undef SIGPFE #undef SIGSEGV #undef SIGTERM #undef SIGFPE #undef SIGABRT #undef SIG_DFL #undef SIG_IGN #undef SIG_ERR #undef NSIG #include #undef NSIG #define NSIG 0 /* pending signals to be processed */ sigset_t pending_signals; /* signal handler table*/ sighandler_t sig_handlers[W32_SIGMAX]; extern struct _children children; static VOID CALLBACK sigint_APCProc(_In_ ULONG_PTR dwParam) { debug5("SIGINT APCProc()"); sigaddset(&pending_signals, W32_SIGINT); } static VOID CALLBACK sigterm_APCProc(_In_ ULONG_PTR dwParam) { debug5("SIGTERM APCProc()"); sigaddset(&pending_signals, W32_SIGTERM); } static VOID CALLBACK sigtstp_APCProc(_In_ ULONG_PTR dwParam) { debug5("SIGTSTP APCProc()"); sigaddset(&pending_signals, W32_SIGTSTP); } BOOL WINAPI native_sig_handler(DWORD dwCtrlType) { debug4("Native Ctrl+C handler, CtrlType %d", dwCtrlType); switch (dwCtrlType) { case CTRL_C_EVENT: QueueUserAPC(sigint_APCProc, main_thread, (ULONG_PTR)NULL); return TRUE; case CTRL_BREAK_EVENT: QueueUserAPC(sigtstp_APCProc, main_thread, (ULONG_PTR)NULL); return TRUE; case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: QueueUserAPC(sigterm_APCProc, main_thread, (ULONG_PTR)NULL); /* wait for main thread to terminate */ WaitForSingleObject(main_thread, INFINITE); return TRUE; default: return FALSE; } } static VOID CALLBACK sigwinch_APCProc(_In_ ULONG_PTR dwParam) { debug5("SIGTERM APCProc()"); sigaddset(&pending_signals, W32_SIGWINCH); } void queue_terminal_window_change_event() { QueueUserAPC(sigwinch_APCProc, main_thread, (ULONG_PTR)NULL); } void sw_init_signal_handler_table() { SetConsoleCtrlHandler(native_sig_handler, TRUE); sigemptyset(&pending_signals); /* this automatically sets all to W32_SIG_DFL (0)*/ memset(sig_handlers, 0, sizeof(sig_handlers)); } sighandler_t mysignal(int signum, sighandler_t handler) { return w32_signal(signum, handler); } sighandler_t w32_signal(int signum, sighandler_t handler) { sighandler_t prev; debug4("signal() sig:%d, handler:%p", signum, handler); if (signum >= W32_SIGMAX) { errno = EINVAL; return W32_SIG_ERR; } prev = sig_handlers[signum]; sig_handlers[signum] = handler; return prev; } int w32_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { /* this is only used by sshd to block SIGCHLD while doing waitpid() */ /* our implementation of waidpid() is never interrupted, so no need to implement this for now*/ debug5("sigprocmask() how:%d"); return 0; } int w32_raise(int sig) { debug4("raise sig:%d", sig); if (sig == W32_SIGSEGV) return raise(SIGSEGV); /* raise native exception handler*/ if (sig >= W32_SIGMAX) { errno = EINVAL; return -1; } /* execute user specified disposition */ if (sig_handlers[sig] > W32_SIG_IGN) { sig_handlers[sig](sig); return 0; } /* if set to ignore, nothing to do */ if (sig_handlers[sig] == W32_SIG_IGN) return 0; /* execute any default handlers */ switch (sig) { case W32_SIGCHLD: sw_cleanup_child_zombies(); break; default: /* exit process */ exit(0); } return 0; } /* processes pending signals, return -1 and errno=EINTR if any are processed*/ static int sw_process_pending_signals() { sigset_t pending_tmp = pending_signals; BOOL sig_int = FALSE; /* has any signal actually interrupted */ debug5("process_signals()"); int i, exp[] = { W32_SIGCHLD , W32_SIGINT , W32_SIGALRM, W32_SIGTERM, W32_SIGTSTP, W32_SIGWINCH }; /* check for expected signals*/ for (i = 0; i < (sizeof(exp) / sizeof(exp[0])); i++) sigdelset(&pending_tmp, exp[i]); if (pending_tmp) { /* unexpected signals queued up */ debug3("process_signals() - ERROR unexpected signals in queue: %d", pending_tmp); errno = ENOTSUP; DebugBreak(); return -1; } /* take pending_signals local to prevent recursion in wait_for_any* loop */ pending_tmp = pending_signals; pending_signals = 0; for (i = 0; i < (sizeof(exp) / sizeof(exp[0])); i++) { if (sigismember(&pending_tmp, exp[i])) { if (sig_handlers[exp[i]] != W32_SIG_IGN) { w32_raise(exp[i]); /* dont error EINTR for SIG_ALRM, */ /* sftp client is not expecting it */ if (exp[i] != W32_SIGALRM) sig_int = TRUE; } sigdelset(&pending_tmp, exp[i]); } } /* by now all pending signals should have been taken care of*/ if (pending_tmp) DebugBreak(); if (sig_int) { debug4("process_queued_signals: WARNING - A signal has interrupted and was processed"); errno = EINTR; return -1; } return 0; } /* * Main wait routine used by all blocking calls. * It wakes up on * - any signals (errno = EINTR ) * - any of the supplied events set * - any APCs caused by IO completions * - time out * - Returns 0 on IO completion and timeout, -1 on rest * if milli_seconds is 0, this function returns 0, its called with 0 * to execute any scheduled APCs */ int wait_for_any_event(HANDLE* events, int num_events, DWORD milli_seconds) { HANDLE all_events[MAXIMUM_WAIT_OBJECTS]; DWORD num_all_events; DWORD live_children = children.num_children - children.num_zombies; errno_t r = 0; num_all_events = num_events + live_children; if (num_all_events > MAXIMUM_WAIT_OBJECTS) { debug3("wait() - ERROR max events reached"); errno = ENOTSUP; return -1; } if ((r = memcpy_s(all_events, MAXIMUM_WAIT_OBJECTS * sizeof(HANDLE), children.handles, live_children * sizeof(HANDLE)) != 0) || ( r = memcpy_s(all_events + live_children, (MAXIMUM_WAIT_OBJECTS - live_children) * sizeof(HANDLE), events, num_events * sizeof(HANDLE)) != 0)) { debug3("memcpy_s failed with error: %d.", r); return -1; } debug5("wait() on %d events and %d children", num_events, live_children); /* TODO - implement signal catching and handling */ if (num_all_events) { DWORD ret = WaitForMultipleObjectsEx(num_all_events, all_events, FALSE, milli_seconds, TRUE); if ((ret >= WAIT_OBJECT_0) && (ret <= WAIT_OBJECT_0 + num_all_events - 1)) { /* woken up by event signalled * is this due to a child process going down */ if (live_children && ((ret - WAIT_OBJECT_0) < live_children)) { sigaddset(&pending_signals, W32_SIGCHLD); sw_child_to_zombie(ret - WAIT_OBJECT_0); } } else if (ret == WAIT_IO_COMPLETION) { /* APC processed due to IO or signal*/ } else if (ret == WAIT_TIMEOUT) { /* timed out */ return 0; } else { /* some other error*/ errno = EOTHER; debug3("ERROR: unxpected wait end: %d", ret); return -1; } } else { DWORD ret = SleepEx(milli_seconds, TRUE); if (ret == WAIT_IO_COMPLETION) { /* APC processed due to IO or signal*/ } else if (ret == 0) { /* timed out */ return 0; } else { /* some other error */ errno = EOTHER; debug3("ERROR: unxpected SleepEx error: %d", ret); return -1; } } if (pending_signals) return sw_process_pending_signals(); return 0; } int sw_initialize() { memset(&children, 0, sizeof(children)); sw_init_signal_handler_table(); if (sw_init_timer() != 0) return -1; return 0; }