diff --git a/client/X11/xfreerdp.c b/client/X11/xfreerdp.c index 53499504c..1ecb7a01a 100644 --- a/client/X11/xfreerdp.c +++ b/client/X11/xfreerdp.c @@ -904,6 +904,8 @@ int main(int argc, char* argv[]) struct thread_data* data; rdpChanMan* chanman; + freerdp_handle_signals(); + setlocale(LC_ALL, ""); freerdp_chanman_global_init(); diff --git a/cunit/test_utils.c b/cunit/test_utils.c index 395ff4d6d..cbae793a1 100644 --- a/cunit/test_utils.c +++ b/cunit/test_utils.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "test_utils.h" @@ -58,6 +59,7 @@ int add_utils_suite(void) add_test_function(wait_obj); add_test_function(args); add_test_function(passphrase_read); + add_test_function(handle_signals); return 0; } @@ -629,3 +631,42 @@ void test_passphrase_read(void) passphrase_read_prompts_to_stderr_when_no_tty(); passphrase_read_reads_from_stdin_when_no_tty(); } + +void handle_signals_resets_terminal(void) +{ + int status, masterfd; + char* slavedevice; + struct termios test_flags; + pid_t child_pid; + + masterfd = posix_openpt(O_RDWR|O_NOCTTY); + + if (masterfd == -1 + || grantpt (masterfd) == -1 + || unlockpt (masterfd) == -1 + || (slavedevice = ptsname (masterfd)) == NULL) + CU_FAIL_FATAL("Could not create pty"); + + terminal_fildes = open(slavedevice, O_RDWR|O_NOCTTY); + tcgetattr(terminal_fildes, &orig_flags); + new_flags = orig_flags; + new_flags.c_lflag &= ~ECHO; + tcsetattr(terminal_fildes, TCSANOW, &new_flags); + terminal_needs_reset = 1; + + if((child_pid = fork()) == 0) + { + freerdp_handle_signals(); + raise(SIGINT); + } + while(wait(&status) != -1); + tcgetattr(terminal_fildes, &test_flags); + CU_ASSERT_EQUAL(orig_flags.c_lflag, test_flags.c_lflag); + close(masterfd); + close(terminal_fildes); +} + +void test_handle_signals(void) +{ + handle_signals_resets_terminal(); +} diff --git a/cunit/test_utils.h b/cunit/test_utils.h index 39cae3920..1bc37d9ef 100644 --- a/cunit/test_utils.h +++ b/cunit/test_utils.h @@ -29,3 +29,4 @@ void test_load_plugin(void); void test_wait_obj(void); void test_args(void); void test_passphrase_read(void); +void test_handle_signals(void); diff --git a/include/freerdp/utils/signal.h b/include/freerdp/utils/signal.h new file mode 100644 index 000000000..eb54c3f7d --- /dev/null +++ b/include/freerdp/utils/signal.h @@ -0,0 +1,36 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Signal handling + * + * Copyright 2011 Shea Levy + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTILS_SIGNAL_H +#define __UTILS_SIGNAL_H + +#include +#ifndef _WIN32 +#include +#include + +extern volatile sig_atomic_t terminal_needs_reset; +extern int terminal_fildes; +extern struct termios orig_flags; +extern struct termios new_flags; +#endif + +FREERDP_API int freerdp_handle_signals(void); + +#endif /* __UTILS_SIGNAL_H */ diff --git a/libfreerdp-utils/CMakeLists.txt b/libfreerdp-utils/CMakeLists.txt index cc941cc23..9daea12ac 100644 --- a/libfreerdp-utils/CMakeLists.txt +++ b/libfreerdp-utils/CMakeLists.txt @@ -39,6 +39,7 @@ set(FREERDP_UTILS_SRCS registry.c certstore.c semaphore.c + signal.c sleep.c stopwatch.c stream.c diff --git a/libfreerdp-utils/passphrase.c b/libfreerdp-utils/passphrase.c index 220b695bd..d207cc854 100644 --- a/libfreerdp-utils/passphrase.c +++ b/libfreerdp-utils/passphrase.c @@ -32,16 +32,16 @@ char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) #include #include #include +#include char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) { char read_char; char* buf_iter; char term_name[L_ctermid]; - int term_file, write_file, read_file, reset_terminal = 0; + int term_file, write_file; ssize_t nbytes; size_t read_bytes = 0; - struct termios orig_flags, no_echo_flags; if (bufsiz == 0) { @@ -54,29 +54,29 @@ char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) || (term_file = open(term_name, O_RDWR)) == -1) { write_file = STDERR_FILENO; - read_file = STDIN_FILENO; + terminal_fildes = STDIN_FILENO; } else { write_file = term_file; - read_file = term_file; + terminal_fildes = term_file; } - if (tcgetattr(read_file, &orig_flags) != -1) + if (tcgetattr(terminal_fildes, &orig_flags) != -1) { - reset_terminal = 1; - no_echo_flags = orig_flags; - no_echo_flags.c_lflag &= ~ECHO; - no_echo_flags.c_lflag |= ECHONL; - if (tcsetattr(read_file, TCSAFLUSH, &no_echo_flags) == -1) - reset_terminal = 0; + new_flags = orig_flags; + new_flags.c_lflag &= ~ECHO; + new_flags.c_lflag |= ECHONL; + terminal_needs_reset = 1; + if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1) + terminal_needs_reset = 0; } if (write(write_file, prompt, strlen(prompt)) == (ssize_t) -1) goto error; buf_iter = buf; - while ((nbytes = read(read_file, &read_char, sizeof read_char)) == (sizeof read_char)) + while ((nbytes = read(terminal_fildes, &read_char, sizeof read_char)) == (sizeof read_char)) { if (read_char == '\n') break; @@ -93,16 +93,16 @@ char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) if (nbytes == (ssize_t) -1) goto error; - if (reset_terminal) + if (terminal_needs_reset) { - if (tcsetattr(read_file, TCSADRAIN, &orig_flags) == -1) + if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1) goto error; - reset_terminal = 0; + terminal_needs_reset = 0; } - if (read_file != STDIN_FILENO) + if (terminal_fildes != STDIN_FILENO) { - if (close(read_file) == -1) + if (close(terminal_fildes) == -1) goto error; } @@ -113,10 +113,10 @@ char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) int saved_errno = errno; buf_iter = NULL; read_char = '\0'; - if (reset_terminal) - tcsetattr(read_file, TCSANOW, &orig_flags); - if (read_file != STDIN_FILENO) - close(read_file); + if (terminal_needs_reset) + tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags); + if (terminal_fildes != STDIN_FILENO) + close(terminal_fildes); errno = saved_errno; return NULL; } diff --git a/libfreerdp-utils/signal.c b/libfreerdp-utils/signal.c new file mode 100644 index 000000000..8f9f67520 --- /dev/null +++ b/libfreerdp-utils/signal.c @@ -0,0 +1,115 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Signal handling + * + * Copyright 2011 Shea Levy + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#ifdef _WIN32 +#include +int freerdp_handle_signals(void) +{ + errno = ENOSYS; + return -1; +} +#else +volatile sig_atomic_t terminal_needs_reset = 0; +int terminal_fildes = 0; +struct termios orig_flags; +struct termios new_flags; + +static void fatal_handler(int signum) +{ + struct sigaction default_sigaction; + sigset_t this_mask; + + if (terminal_needs_reset) + tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags); + + default_sigaction.sa_handler = SIG_DFL; + sigfillset(&(default_sigaction.sa_mask)); + default_sigaction.sa_flags = 0; + + sigaction(signum, &default_sigaction, NULL); + + sigemptyset(&this_mask); + sigaddset(&this_mask, signum); + pthread_sigmask(SIG_UNBLOCK, &this_mask, NULL); + raise(signum); +} + +int freerdp_handle_signals(void) +{ + const int fatal_signals[] = { + SIGABRT, + SIGALRM, + SIGBUS, + SIGFPE, + SIGHUP, + SIGILL, + SIGINT, + SIGKILL, + SIGPIPE, + SIGQUIT, + SIGSEGV, + SIGSTOP, + SIGTERM, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGUSR1, + SIGUSR2, +#ifdef SIGPOLL + SIGPOLL, +#endif +#ifdef SIGPROF + SIGPROF, +#endif +#ifdef SIGSYS + SIGSYS, +#endif + SIGTRAP, +#ifdef SIGVTALRM + SIGVTALRM, +#endif + SIGXCPU, + SIGXFSZ + }; + int signal_index; + sigset_t orig_set; + struct sigaction orig_sigaction, fatal_sigaction; + + sigfillset(&(fatal_sigaction.sa_mask)); + sigdelset(&(fatal_sigaction.sa_mask), SIGCONT); + pthread_sigmask(SIG_BLOCK, &(fatal_sigaction.sa_mask), &orig_set); + + fatal_sigaction.sa_handler = fatal_handler; + fatal_sigaction.sa_flags = 0; + + for (signal_index = 0; + signal_index < (sizeof fatal_signals / sizeof fatal_signals[0]); + signal_index++) + if (sigaction(fatal_signals[signal_index], + NULL, &orig_sigaction) == 0) + if (orig_sigaction.sa_handler != SIG_IGN) + sigaction(fatal_signals[signal_index], + &fatal_sigaction, NULL); + + pthread_sigmask(SIG_SETMASK, &orig_set, NULL); + return 0; +} +#endif