/* * esh - the 'extensible' shell. * * Utility functions for system calls. * * Developed by Godmar Back for CS 3214 Fall 2009 * Virginia Tech. */ #include #include #include #include #include #include #include #include #include #include #include "esh-sys-utils.h" static const char rcsid [] = "$Id: esh-sys-utils.c,v 1.4 2011/01/21 20:13:06 cs3214 Exp $"; /* Utility function for esh_sys_fatal_error and esh_sys_error */ static void vesh_sys_error(char *fmt, va_list ap) { char errmsg[1024]; strerror_r(errno, errmsg, sizeof errmsg); vfprintf(stderr, fmt, ap); fprintf(stderr, "%s\n", errmsg); } /* Print information about the last syscall error */ void esh_sys_error(char *fmt, ...) { va_list ap; va_start(ap, fmt); vesh_sys_error(fmt, ap); va_end(ap); } /* Print information about the last syscall error and then exit */ void esh_sys_fatal_error(char *fmt, ...) { va_list ap; va_start(ap, fmt); vesh_sys_error(fmt, ap); va_end(ap); exit(EXIT_FAILURE); } static int terminal_fd = -1; /* the controlling terminal */ static struct termios saved_tty_state; /* the state of the terminal when shell was started. */ /* Initialize tty support. Return pointer to saved initial terminal state */ struct termios * esh_sys_tty_init(void) { char *tty; assert(terminal_fd == -1 || !!!"esh_sys_tty_init already called"); terminal_fd = open(tty = ctermid(NULL), O_RDWR); if (terminal_fd == -1) esh_sys_fatal_error("opening controlling terminal %s failed: ", tty); esh_sys_tty_save(&saved_tty_state); return &saved_tty_state; } /* Save current terminal settings. * This function is used when a job is suspended.*/ void esh_sys_tty_save(struct termios *saved_tty_state) { int rc = tcgetattr(terminal_fd, saved_tty_state); if (rc == -1) esh_sys_fatal_error("tcgetattr failed: "); } /* Restore terminal to saved settings. * This function is used when resuming a suspended job. */ void esh_sys_tty_restore(struct termios *saved_tty_state) { int rc = tcsetattr(terminal_fd, TCSADRAIN, saved_tty_state); if (rc == -1) esh_sys_fatal_error("could not restore tty attributes tcsetattr: "); } /* Get a file descriptor that refers to controlling terminal */ int esh_sys_tty_getfd(void) { assert(terminal_fd != -1 || !!!"esh_sys_tty_init() must be called"); return terminal_fd; } /* Return true if this signal is blocked */ bool esh_signal_is_blocked(int sig) { sigset_t mask; if (sigprocmask(0, NULL, &mask) == -1) esh_sys_error("sigprocmask failed while retrieving current mask"); return sigismember(&mask, sig); } /* Helper for esh_signal_block and esh_signal_unblock */ static bool __mask_signal(int sig, int how) { sigset_t mask, omask; sigemptyset(&mask); sigaddset(&mask, sig); if (sigprocmask(how, &mask, &omask) != 0) esh_sys_error("sigprocmask failed for %d/%d", sig, how); return sigismember(&omask, sig); } /* Block a signal. Returns true it was blocked before */ bool esh_signal_block(int sig) { return __mask_signal(sig, SIG_BLOCK); } /* Unblock a signal. Returns true it was blocked before */ bool esh_signal_unblock(int sig) { return __mask_signal(sig, SIG_UNBLOCK); } /* Install signal handler for signal 'sig' */ void esh_signal_sethandler(int sig, sa_sigaction_t handler) { sigset_t emptymask; sigemptyset(&emptymask); struct sigaction sa = { .sa_sigaction = handler, /* do not block any additional signals (besides 'sig') when * signal handler is entered. */ .sa_mask = emptymask, /* restart system calls when possible */ .sa_flags = SA_RESTART | SA_SIGINFO }; if (sigaction(sig, &sa, NULL) != 0) esh_sys_fatal_error("sigaction failed for signal %d", sig); }