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