/* * KON - Kanji ON Linux Console - Copyright (C) 1992, 1993, 1994 Takashi * MANABE (manabe@Roy.dsl.tutics.tut.ac.jp) * * KON is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * KON is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 675 * Mass Ave, Cambridge, MA 02139, USA. */ /* * modified for Big5Con by Hung-Chi Chu */ #include "big5con.h" #if defined(__FreeBSD__) #define TCSETA TIOCSETA #define TCGETA TIOCGETA #define SIGCLD SIGCHLD #define XCASE 0 #endif int masterPty; /* master pseudo-tty file descriptor */ #define MAX_TTYNAME 10 static int childPid, sockFd, slavePty; static struct termio oldTio; static char ptyName[MAX_TTYNAME + 1]; static int orgVtNum = -1; struct initInfo { bool display; /* display initialized */ bool utmp; /* utmp set */ bool socket; /* socket opened */ bool termio; /* termio saved */ }; static struct initInfo init; extern int kbd_leds; extern struct keymap vckmap; static void CleanUp(void) { if (init.display && con.active) { TextMode(); } if (init.utmp) ResetUtmp(ptyName); if (init.socket) SocketKill(sockFd); if (init.termio) ioctl(0, TCSETA, &oldTio); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGUSR1, SIG_DFL); signal(SIGUSR2, SIG_DFL); FontDetach(TRUE); } static void ExitTerm(int signum) { fatal(sys_siglist[signum]); } static void ExitPty(int signum) { int stat; #if defined(__FreeBSD__) signal(SIGCLD, SIG_DFL); #endif if (wait3(&stat, WNOHANG, 0) != childPid) { TextMode(); kill(0, SIGTSTP); GraphMode(); kill(childPid, SIGCONT); signal(SIGCLD, ExitPty); return; } if (WEXITSTATUS(stat) & 0x7f) if (WIFSIGNALED(stat)) fatal("child died with signal -- %s\r\n", sys_siglist[WTERMSIG(stat)]); else fatal("child exited with status %d\r\n", WEXITSTATUS(stat) & 0x7f); else if (signum == SIGHUP) { fprintf(stderr, "\r\nBig5Con> switched to new VC\r\n"); exit(EXIT_SUCCESS); } else { fprintf(stderr, "\r\nBig5Con> finished\r\n"); exit(EXIT_SUCCESS); } } static fd_set orgReadFds; static int numFds; void MouseSetRfd(int mfd) { if (mfd > 0) FD_SET(mfd, &orgReadFds); if (mfd > sockFd) numFds = mfd + 1; else numFds = sockFd + 1; } void MouseResetRfd(int mfd) { if (mfd > 0) FD_CLR(mfd, &orgReadFds); numFds = sockFd + 1; } static void ConsoleHandler(void) { static u_char buff[BUFSIZ + 1]; fd_set readFds; int i = 0; struct timeval tv; FD_ZERO(&orgReadFds); FD_SET(0, &orgReadFds); FD_SET(masterPty, &orgReadFds); FD_SET(sockFd, &orgReadFds); if (mInfo.has_mouse && mouseFd > 0) MouseSetRfd(mouseFd); else MouseResetRfd(mouseFd); /* * Note: we use timeout on select call even if cursor blink is off * because of screen saver and mouse cursor timeout. */ while (1) { int v; do { /* Idle loop. */ PollCursor(FALSE); readFds = orgReadFds; tv.tv_sec = 0; tv.tv_usec = 100000;/* 0.1 sec */ v = select(numFds, &readFds, NULL, NULL, &tv); } while (v == 0 || (v < 0 && (errno == EINTR || mouseFd < 0))); if (v < 0) { PerrorExit("select"); } if (FD_ISSET(masterPty, &readFds)) { i = read(masterPty, buff, BUFSIZ); if (i > 0) { if (con.text_mode) { write(1, buff, i); } else { /* buff[i] = 0; */ VtEmu(buff, i); TextRefresh(); } } } if (FD_ISSET(0, &readFds)) { int j; i = read(0, buff, BUFSIZ); for (j = 0; j < i; j++) trans_key(buff[j]); PollCursor(TRUE); } if (FD_ISSET(sockFd, &readFds)) SocketInterface(sockFd); if (mInfo.has_mouse && mouseFd > 0) { if (FD_ISSET(mouseFd, &readFds) && con.active) { i = read(mouseFd, buff, BUFSIZ); if (i > 0) MouseGetPacket(buff, i); PollCursor(TRUE); } } } } static void ProcessArgs(int argc, const char *argv[]) { int i = 0; const char *video = "NORMAL"; if (argc > 0 && argv[0][0] != '-') { video = argv[0]; i++; } ConsoleInit(video); while (i < argc) { const char *arg; if (argv[i][0] != '-') { warn("bad arg `%s'; assumed `-%s'\r\n", argv[i]); arg = (char *)argv[i]; } else arg = (char *)argv[i] + 1; i++; if (i >= argc) { error("no value for `%s'\r\n", arg); break; } if (!strcasecmp(arg, "e")) ConfigExecProg(argv[i]); else if (SetCapArg(arg, argv[i]) < 0) warn("invalid capability `%s' ignored\r\n", arg); i++; } } static int savedArgc; /* argc of startup time */ static const char **savedArgv; /* argv of startup time */ /* Do initialization before reading config file */ void TermInit(int argc, const char *argv[]) { int i; init.display = init.utmp = init.socket = init.termio = FALSE; /* Initialize subsystems. */ CapInit(); ChildInit(); MouseInit(); VtInit(); ProcessArgs(argc, argv); savedArgc = argc; savedArgv = malloc(argc * sizeof(const char *)); for (i = 0; i < argc; i++) { savedArgv[i] = strdup(argv[i]); } } static int TryTermReset(int argc, const char *argv[]) { int i; fprintf(stderr, "Big5Con> resetting kon for args ["); for (i = 0; i < argc; i++) { fprintf(stderr, " %s", argv[i]); } fprintf(stderr, " ]...\r\n"); CapInit(); ConsoleCleanup(); if (mInfo.has_mouse) MouseCleanup(); #if 0 VtCleanup(); #endif init.display = FALSE; MouseInit(); VtInit(); ProcessArgs(argc, argv); return ReadConfig(CONFIG_NAME); } /* Called from SocketInterface with stream fd. */ void TermRestart(int fd) { int i; int argc; char **argv; read(fd, &argc, sizeof(argc)); argv = alloca(argc * sizeof(char *)); for (i = 0; i < argc; i++) { int len; read(fd, &len, sizeof(len)); argv[i] = alloca(len + 1); /* +1 for '\0' */ read(fd, argv[i], (size_t) len); argv[i][len] = '\0'; } TextMode(); if (TryTermReset(argc, (const char **)argv) < 0 && TryTermReset(savedArgc, savedArgv) < 0 && TryTermReset(0, (const char **)NULL) < 0) fatal("giving up\r\n"); if (mInfo.has_mouse) mouseFd = MouseStart(); VtStart(); ConsoleStart(savedArgc, savedArgv); /* by b5c: add argc, argv */ init.display = TRUE; message("reset done\r\n"); } /* Start processing */ void TermStart(int argc, const char *argv[]) { struct termio newTio; char ls, ln; /* Open PTY(master) */ for (ls = 'p'; ls <= 's'; ls++) { for (ln = 0; ln <= 0xF; ln++) { sprintf(ptyName, "/dev/pty%1c%1x", ls, ln); if ((masterPty = open(ptyName, O_RDWR)) >= 0) break; } if (masterPty >= 0) break; } if (masterPty < 0) { message("can not get master pty\r\n"); PerrorExit(ptyName); } ptyName[5] = 't'; if (mInfo.has_mouse) { mouseFd = MouseStart(); } chown("/dev/tty0", getuid(), getgid()); #if defined(linux) sockFd = SocketInit(ttyname(0) + 8); #elif defined(__FreeBSD__) sockFd = SocketInit(ttyname(0) + 9); #endif init.socket = TRUE; /* Get old tio of 0 */ ioctl(0, TCGETA, &oldTio); init.termio = TRUE; /* by b5c: add check GIO_KEYMAP KDGETLED */ if (ioctl(0, GIO_KEYMAP, &vckmap) < 0) { PerrorExit("ioctl"); } if (ioctl(0, KDGETLED, &kbd_leds) < 0) { PerrorExit("ioctl"); } SetUtmp(ptyName); init.utmp = TRUE; /* fork handler */ if ((childPid = fork()) < 0) { PerrorExit("fork"); } if (childPid != 0) { /* I'm parent. */ atexit(CleanUp); ChildCleanup(); /* Signal Setting */ signal(SIGCHLD, ExitPty); signal(SIGHUP, ExitTerm); signal(SIGTERM, ExitTerm); signal(SIGSEGV, ExitTerm); signal(SIGQUIT, ExitTerm); /* by b5c */ /* Set new tio of 0 */ newTio = oldTio; newTio.c_lflag &= ~(ECHO | ISIG | ICANON | XCASE); newTio.c_iflag = 0; newTio.c_oflag &= ~OPOST; newTio.c_cc[VMIN] = 1; newTio.c_cc[VTIME] = 0; #if defined(__FreeBSD__) newTio.c_cc[VDISCARD] = _POSIX_VDISABLE; newTio.c_cc[VLNEXT] = _POSIX_VDISABLE; newTio.c_cc[VSTART] = _POSIX_VDISABLE; newTio.c_cc[VSTOP] = _POSIX_VDISABLE; newTio.c_cc[VINTR] = _POSIX_VDISABLE; newTio.c_cc[VSUSP] = _POSIX_VDISABLE; newTio.c_cc[VDSUSP] = _POSIX_VDISABLE; newTio.c_cc[VQUIT] = _POSIX_VDISABLE; #endif newTio.c_cflag |= CS8; #if defined(linux) newTio.c_line = 0; #endif ioctl(0, TCSETA, &newTio); /* VGA initialize */ VtStart(); ConsoleStart(argc, argv); init.display = TRUE; FontAttach(); ConsoleHandler(); } else { int efd; FILE *errfp; efd = dup(STDERR_FILENO); errfp = fdopen(efd, "w"); /* I'm child */ /* Make me process leader */ setsid(); #if defined(__FreeBSD__) { int devtty; if ((devtty = open("/dev/tty", O_RDWR | O_NDELAY)) >= 0) { ioctl(devtty, TIOCNOTTY, (char *)0); close(devtty); } } #endif /* Open TTY(slave) */ if ((slavePty = open(ptyName, O_RDWR)) < 0) { PerrorExit(ptyName); } close(masterPty); /* Set old tio to TTY */ ioctl(slavePty, TCSETA, &oldTio); #if defined(__FreeBSD__) ioctl(slavePty, TIOCSCTTY, (char *)0); #endif /* Set std??? to pty */ dup2(slavePty, STDIN_FILENO); dup2(slavePty, STDOUT_FILENO); dup2(slavePty, STDERR_FILENO); ChildStart(errfp); } } void ChangeOrgConsole() { int cfd; #if defined(linux) cfd = open("/dev/console", O_WRONLY); if (cfd < 0 && (cfd = open("/dev/console", O_RDONLY)) < 0) { PerrorExit("/dev/console"); } #elif defined(__FreeBSD__) cfd = open("/dev/vga", O_WRONLY); if (cfd < 0 && (cfd = open("/dev/vga", O_RDONLY)) < 0) { PerrorExit("/dev/vga"); } #endif ioctl(cfd, VT_ACTIVATE, orgVtNum); close(cfd); } void ChangeNewConsole() { #if defined(linux) struct vt_stat vts; #endif int cfd, vfd, vtNum, child, parent, mode; char vtty[MAX_TTYNAME + 1]; #if defined(linux) cfd = open("/dev/console", O_WRONLY); if (cfd < 0 && (cfd = open("/dev/console", O_RDONLY)) < 0) fatal("can't open /dev/console"); #elif defined(__FreeBSD__) cfd = open("/dev/vga", O_WRONLY); if (cfd < 0 && (cfd = open("/dev/vga", O_RDONLY)) < 0) fatal("can't open /dev/vga"); #endif ioctl(cfd, KDGETMODE, &mode); if (mode == KD_TEXT) { close(cfd); return; } #if defined(linux) ioctl(cfd, VT_GETSTATE, &vts); orgVtNum = vts.v_active; #endif ioctl(cfd, VT_OPENQRY, &vtNum); if (vtNum < 0) fatal("can't get free VC"); parent = getpid(); if ((child = fork()) == -1) PerrorExit("fork"); if (child) { signal(SIGHUP, ExitPty); pause(); } setsid(); #if defined(linux) sprintf(vtty, "/dev/tty%d", vtNum); #elif defined(__FreeBSD__) sprintf(vtty, "/dev/ttyv%d", vtNum); #endif if ((vfd = open(vtty, O_RDWR)) < 0) fatal("can't open %s", vtty); if (ioctl(cfd, VT_ACTIVATE, vtNum) != 0) fatal("can't activate VC(%d)", vtNum); atexit(ChangeOrgConsole); close(cfd); dup2(vfd, STDIN_FILENO); dup2(vfd, STDOUT_FILENO); dup2(vfd, STDERR_FILENO); kill(parent, SIGHUP); }