/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <unistd.h>30#include <termios.h>31#include <signal.h>32#include <stdlib.h>33#include <stdio.h>3435#include "hostcompat.h"3637/*38* The program name.39* This is used in err.c.40*/41const char *hostcompat_progname = NULL;4243/*44* Unix tty state, for when we're running and to put it back the way it was,45* respectively.46*/47static struct termios hostcompat_runtios;48static struct termios hostcompat_savetios;4950/*51* Put the tty state back the way it was.52*/53static54void55hostcompat_ttyreset(void)56{57tcsetattr(STDIN_FILENO, TCSADRAIN, &hostcompat_savetios);58}5960/*61* Set the tty state back to the way we want it for running.62*/63static64void65hostcompat_ttyresume(void)66{67tcsetattr(STDIN_FILENO, TCSADRAIN, &hostcompat_runtios);68}6970/*71* Set up the tty state stuff.72*/73static74int75hostcompat_ttysetup(void)76{77struct termios tios;7879/* Get the current tty state. */80if (tcgetattr(STDIN_FILENO, &tios) < 0) {81/* stdin is not a tty */82return -1;83}8485hostcompat_savetios = tios;8687/* Turn off canonical ("cooked") input. */88tios.c_lflag &= ~ICANON;8990/*91* With canonical input off, this says how many characters must be92* typed before read() will return.93*/94tios.c_cc[VMIN] = 1;9596/* This can be used to set up read timeouts, but we don't need that. */97tios.c_cc[VTIME] = 0;9899/* Turn off echoing of keypresses. */100tios.c_lflag &= ~(ECHO|ECHONL|ECHOCTL);101102/* Do not support XON/XOFF flow control. */103tios.c_iflag &= ~(IXON|IXOFF);104105/* On input, we want no CR/LF translation. */106tios.c_iflag &= ~(INLCR|IGNCR|ICRNL);107108/* However, on output we want LF ('\n') mapped to CRLF. */109#ifdef OCRNL /* missing on OS X */110tios.c_oflag &= ~(OCRNL);111#endif112tios.c_oflag |= OPOST|ONLCR;113114/* Enable keyboard signals (^C, ^Z, etc.) because they're useful. */115tios.c_lflag |= ISIG;116117/* Set the new tty state. */118hostcompat_runtios = tios;119tcsetattr(STDIN_FILENO, TCSADRAIN, &tios);120121return 0;122}123124/*125* Signal handler for all the fatal signals (SIGSEGV, SIGTERM, etc.)126*/127static128void129hostcompat_die(int sig)130{131/* Set the tty back to the way we found it */132hostcompat_ttyreset();133134/* Make sure the default action will occur when we get another signal*/135signal(sig, SIG_DFL);136137/* Post the signal back to ourselves, to cause the right exit status.*/138kill(getpid(), sig);139140/* Just in case. */141_exit(255);142}143144/*145* Signal handler for the stop signals (SIGTSTP, SIGTTIN, etc.)146*/147static148void149hostcompat_stop(int sig)150{151/* Set the tty back to the way we found it */152hostcompat_ttyreset();153154/* Make sure the default action will occur when we get another signal*/155signal(sig, SIG_DFL);156157/* Post the signal back to ourselves. */158kill(getpid(), sig);159}160161/*162* Signal handler for SIGCONT.163*/164static165void166hostcompat_cont(int sig)167{168(void)sig;169170/* Set the tty to the way we want it for running. */171hostcompat_ttyresume();172173/*174* Reload the signal handlers for stop/continue signals, in case175* they were set up with one-shot signals.176*/177signal(SIGTTIN, hostcompat_stop);178signal(SIGTTOU, hostcompat_stop);179signal(SIGTSTP, hostcompat_stop);180signal(SIGCONT, hostcompat_cont);181}182183/*184* Initialize the hostcompat library.185*/186void187hostcompat_init(int argc, char *argv[])188{189/* Set the program name */190if (argc > 0 && argv[0] != NULL) {191hostcompat_progname = argv[0];192}193194/* Set the tty modes */195if (hostcompat_ttysetup() < 0) {196return;197}198199/* When exit() is called, clean up */200atexit(hostcompat_ttyreset);201202/* stdout/stderr should be unbuffered */203setvbuf(stdout, NULL, _IONBF, 0);204setvbuf(stderr, NULL, _IONBF, 0);205206/* Catch all the fatal signals, so we can clean up */207signal(SIGHUP, hostcompat_die);208signal(SIGINT, hostcompat_die);209signal(SIGQUIT, hostcompat_die);210signal(SIGILL, hostcompat_die);211signal(SIGTRAP, hostcompat_die);212signal(SIGABRT, hostcompat_die);213#ifdef SIGEMT214signal(SIGEMT, hostcompat_die);215#endif216signal(SIGFPE, hostcompat_die);217signal(SIGBUS, hostcompat_die);218signal(SIGSEGV, hostcompat_die);219signal(SIGSYS, hostcompat_die);220signal(SIGPIPE, hostcompat_die);221signal(SIGALRM, hostcompat_die);222signal(SIGTERM, hostcompat_die);223signal(SIGXCPU, hostcompat_die);224signal(SIGXFSZ, hostcompat_die);225signal(SIGVTALRM, hostcompat_die);226signal(SIGPROF, hostcompat_die);227signal(SIGUSR1, hostcompat_die);228signal(SIGUSR2, hostcompat_die);229230/* Catch the stop signals, so we can adjust the tty */231signal(SIGTTIN, hostcompat_stop);232signal(SIGTTOU, hostcompat_stop);233signal(SIGTSTP, hostcompat_stop);234235/* Catch the continue signal, so we can adjust the tty */236signal(SIGCONT, hostcompat_cont);237}238239240