/* * Copyright (c) 1992, 2015, Oracle and/or its affiliates. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * fbconsole - fallback console */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char LogPath[MAXPATHLEN]; /* pathname of log file */ /* * Default settings to use if they can't actually be obtained from a * descriptor relevant to the application. * * XXX: These settings shouldn't be used unless absolutely necessary, since * they're almost certain to get out of sync with the kernel's defaults * (which is what they're intended to be). */ static struct termios termios_dflt = { BRKINT|ICRNL|IXON|IGNPAR|IMAXBEL, /* input modes */ OPOST|ONLCR, /* output modes */ B9600|(B9600 << IBSHIFT)|CS8|HUPCL|CREAD, /* control modes */ ISIG|ICANON|ECHO|IEXTEN|ECHOE|ECHOK|ECHOCTL|ECHOKE, /* local modes */ /* control characters */ CINTR, /* VINTR */ CQUIT, /* VQUIT */ CERASE, /* VERASE */ CKILL, /* VKILL */ CEOT, /* VEOF */ CEOL, /* VEOL */ CEOL2, /* VEOL2 */ CNUL, /* VSWTCH */ CSTART, /* VSTART */ CSTOP, /* VSTOP */ CSUSP, /* VSUSP */ CDSUSP, /* VDSUSP */ CRPRNT, /* VRPRNT */ CFLUSH, /* VDISCARD */ CWERASE, /* VWERASE */ CLNEXT, /* VLNEXT */ }; #ifdef NOTDEF /* * OpenConsole * Returns a file descriptor which has had the console redirected to it * * This version (unused) opens a pipe and redirects the console to it. */ static int OpenConsole(void) { int fds[2]; int fdcons; if (pipe(fds) == -1) { fprintf(stderr,"Couldn't open console pipes\n"); perror("pipe"); exit(1); } if ((fdcons = open("/dev/console", O_RDONLY)) == -1) { fprintf(stderr,"Couldn't open /dev/console\n"); perror("open"); exit(1); } if (ioctl(fdcons, SRIOCSREDIR, fds[0]) == -1) { fprintf(stderr,"Couldn't redirect console to console pipe\n"); exit(1); } return fds[1]; } #endif /* NOTDEF */ /* * OpenConsole * * Opens a pty, copies tty settings into it from /dev/console, and redirects * console output to it. Returns the master end of the pty. */ static int OpenConsole(void) { int console; int master; int slave; char *slavename; struct termios termios; if ((console = open("/dev/console", O_RDONLY | O_NOCTTY, 0)) == -1) { perror("fbconsole: open /dev/console"); exit(1); } if ((master = open("/dev/ptmx", O_RDWR, 0)) == -1) { perror("fbconsole: open /dev/ptmx"); exit(1); } if (grantpt(master) == -1) { fputs("fbconsole: grantpt failed\n", stderr); exit(1); } if (unlockpt(master) == -1) { fputs("fbconsole: unlockpt failed\n", stderr); exit(1); } if ((slavename = ptsname(master)) == NULL) { fputs("fbconsole: ptsname failed\n", stderr); exit(1); } #ifdef DEBUG fprintf(stderr, "slavename = \"%s\"\n", slavename); #endif if ((slave = open(slavename, O_RDWR, 0)) == -1) { perror("fbconsole: open slave"); exit(1); } if (ioctl(slave, I_PUSH, "ptem") == -1) { perror("fbconsole: push ptem"); exit(1); } if (ioctl(slave, I_PUSH, "ldterm") == -1) { perror("fbconsole: push ldterm"); exit(1); } /* * Propagate tty settings from the real console to the new console. * If the erase character is zero, apply default settings to the new * console. If the erase character is nonzero, leave most of the * settings intact and apply default values only to the modes and to * the EOF and EOL character. (Why apply defaults for EOF and EOL?) * * Code originally taken from XView, lib/libxview/ttysw/tty_gtty.c */ if (tcgetattr(console, &termios) == -1) { perror("fbconsole: tcgetattr"); exit(1); } if (termios.c_cc[VERASE] == 0) { termios = termios_dflt; } else { termios.c_iflag = termios_dflt.c_iflag; termios.c_oflag = termios_dflt.c_oflag; termios.c_cflag = termios_dflt.c_cflag; termios.c_lflag = termios_dflt.c_lflag; termios.c_cc[VEOF] = termios_dflt.c_cc[VEOF]; termios.c_cc[VEOL] = termios_dflt.c_cc[VEOL]; } if (tcsetattr(slave, TCSANOW, &termios) == -1) { perror("fbconsole: tcsetattr"); exit(1); } /* redirect console output into the slave side */ if (ioctl(console, SRIOCSREDIR, slave) == -1) { perror("fbconsole: ioctl SRIOCSREDIR"); exit(1); } return master; } /* * OpenLog * Opens the console log file; returns a file descriptor */ static FILE * OpenLog( const char *dpyName, char *path) { const char *tmpName; FILE *log; int tmpFd; if (path == NULL) { tmpName = getenv("TMPDIR"); if (tmpName == NULL) { tmpName = P_tmpdir; } if (snprintf(LogPath, sizeof(LogPath), "%s/wscon-%s-XXXXXX", tmpName, dpyName) > sizeof(LogPath)) { tmpFd = -1; } else { tmpFd = mkstemp(LogPath); } path = LogPath; } else { LogPath[0] = '\0'; if ((strcmp(path,"-") == 0) || (strcmp(path,"stderr") == 0)) { return stderr; } tmpFd = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR|S_IWUSR); } #ifdef DEBUG fprintf(stderr, "log file = \"%s\"\n", path); #endif if ( (tmpFd < 0) || (log = fdopen(tmpFd, "w")) == NULL) { fprintf(stderr, "fbconsole: couldn't open console log file '%s'\n",path); exit(1); } setvbuf(log, NULL, _IONBF, 0); fchmod(fileno(log), S_IRUSR|S_IWUSR); return log; } /* * CloseLog */ static void CloseLog(void) { if (LogPath[0] == '\0') return; if (unlink(LogPath) == -1) perror("unlink"); } /* * CleanupAndExit * Closes log file and exits */ static void CleanupAndExit(void) { CloseLog(); exit(0); } /* * SignalHandler * The signal handler for SIGINT and SIGTERM. */ /*ARGSUSED*/ void SignalHandler(int sig) { CleanupAndExit(); } /* * DisplayErrorHandler * X I/O error handler. */ /*ARGSUSED*/ int DisplayErrorHandler(Display *dpy) { CleanupAndExit(); return 0; } /* * LogConsole * Reads a console message and writes it to the console log file */ static void LogConsole( int console, FILE *log) { char buf[1024]; int rcount; rcount = read(console, buf, 1024); if (rcount == -1) return; (void) fwrite(buf, rcount, 1, log); } /* * InputLoop * Waits for input from the console message pipe or the xserver. * On input from the console - logs it to the console log file. * On any input (or EOF) from the xserver, exits */ static void InputLoop( Display *dpy, int console, FILE *log) { struct pollfd fds[2]; XEvent event; int fdcount = 1; #define CONSOLE_FD 0 #define XSERVER_FD 1 fds[CONSOLE_FD].fd = console; fds[CONSOLE_FD].events = POLLIN; if (dpy != NULL) { fds[XSERVER_FD].fd = ConnectionNumber(dpy); fds[XSERVER_FD].events = POLLIN; fdcount++; } while (poll(fds, fdcount, -1) >= 0) { if ((dpy != NULL) && (fds[XSERVER_FD].revents)) { while (XPending(dpy)) XNextEvent(dpy, &event); } if (fds[CONSOLE_FD].revents & POLLIN) { LogConsole(console,log); } } } /* * Usage * Prints a usage message */ static void Usage(void) { fprintf(stderr,"Usage: fbconsole [-d ] [-f ] [-n]\n"); exit(1); } /* * main */ int main(int argc, char **argv) { Display *dpy = NULL; char *dpyName = NULL; int console; char *logFile = NULL; FILE *log; int opt; int noDisplay = False; while ((opt = getopt(argc, argv, "d:f:n")) != EOF) { switch (opt) { case 'd': dpyName = optarg; break; case 'n': noDisplay = True; break; case 'f': logFile = optarg; break; case '?': Usage(); break; } } if (noDisplay) { dpyName = XDisplayName(dpyName); } else { if ((dpy = XOpenDisplay(dpyName)) == NULL) { fprintf(stderr, "Couldn't open display connection %s\n", XDisplayName(dpyName)); exit(1); } (void) XSetIOErrorHandler(DisplayErrorHandler); dpyName = XDisplayString(dpy); } console = OpenConsole(); log = OpenLog(dpyName, logFile); (void)sigset(SIGHUP, SignalHandler); (void)sigset(SIGINT, SignalHandler); (void)sigset(SIGQUIT, SignalHandler); (void)sigset(SIGTERM, SignalHandler); (void)sigset(SIGPIPE, SignalHandler); InputLoop(dpy, console, log); return(0); }