/* * ngetty.c - daemon for virtual console terminals * * Copyright 2007 Andre Oliveira * Copyright 2007,2008 Nikola Vladov * * This program 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. * * This program 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. */ /* * Synopsis: ngetty tty... * * ngetty open()s the virtual console terminals specified as arguments and * poll()s them for user input. Once input is available on one of the * terminals, it fork()s and exec()s the ngetty-helper programs with that * terminal set up as the controlling terminal. ngetty-helper make * utmp/wtmp records, print /etc/issue, ask for user name and then * exec()s login(1) program. When a ngetty session terminates, * the sig_handler() restarts the corresponding terminal. */ #include #include #include #include #include #include #include "lib.h" #include "sig_action.h" #ifdef NEED_HELPER_OPTION static char *helper; #else #define helper NGETTY_HELPER #endif struct tty_name { char *tty; /* original ttyname */ char tmp[23]; /* expanded ttyname; /dev/ttyX or /dev/vc/X */ char st; }; typedef struct tty_name tty_name_; typedef struct pollfd pollfd_; static tty_name_ *tty; /* Number of managed terminals (argc - 1). */ static int npfd; /* * Poll data for the terminals. * When polling for input on a terminal, the fd field stores its * file descriptor, as customary. After fork()ing, though, the parent * process closes the terminal and stores the child's _negative_ pid * in the fd field, so that poll() ignores that entry and * main() knows every child's pid. */ static pollfd_ *pfd; static void nano_sleep(int n) { /* sleep n.5 secs */ struct timespec ts = { n, 500111222 }; nanosleep(&ts, 0); } static void max_copy(char *out,const char *in) { int len; for (len=0; len<10; len++) { if (!(out[len]=in[len])) break; } out[len] = 0; } /* * Exec NGETTY_HELPER. */ static void exechelper(int i, char *login) { pollfd_ *xx = pfd + i; int pid; while ((pid=fork()) < 0) nano_sleep(0); if (pid==0) { tty_name_ *tt = tty + i; char *a1 = (tt->tmp[0]) ? tt->tmp : tt->tty; char *arg[4] = { helper, a1, login, 0 }; #ifndef NGETTY_SELFPIPE if (xx->fd) #endif dup2(xx->fd, 0); i = npfd + 8; while (--i) close(i); execve(*arg, arg, environ); nano_sleep(5); _exit(127); } close(xx->fd); xx->fd = -pid; } /* * Open virtual console terminal i. * * In case of failure, most likely because the device node does not exist, * poll() will simply ignore this entry and users get a blank screen and * locked keyboard on this console. That's enough error reporting. ;-) * No need to add code to check all syscalls' return values. * * Linux does not implement revoke(); the *BSD do. */ void opentty(int k) { tty_name_ *tt = tty + k; char *path = tt->tty, *m = tt->tmp, st; int fd; if (*path != '/') { max_copy(m, "/dev/vc/"); if ('0'<=*path && *path<='9') { max_copy(m+8, path); if (chown(m,0,0)) { m[5]='t'; m[6]='t'; m[7]='y'; } } else { if (path[0]==0) { fd = -1; goto do_it; } max_copy(m+5, path); } path = m; } else m[0] = 0; chown(path, 0, 0); chmod(path, 0600); #ifdef HAVE_REVOKE revoke(path); #endif fd = open(path, O_RDWR | O_NOCTTY); if (fd > npfd + 4) _exit(100); do_it: pfd[k].fd = fd; if (fd < 0) st = 0; else { st = tt->st ^ 1; if (st) exechelper(k,0); } tt->st = st; } #ifndef NGETTY_SELFPIPE static int volatile poll_timeout; #endif /* * SIG handler. */ static void sig_handler(int sig) { if (sig == SIGTERM) { int k; again: for (k = 0; k < npfd; k++) { int pid = -pfd[k].fd; if (pid > 1) kill(pid, sig); } nano_sleep(0); if (sig == SIGKILL) _exit(0); sig = SIGKILL; goto again; } #ifdef NGETTY_SELFPIPE write(1,"",1); #else poll_timeout = 1; #endif } static void set_handler() { struct sigaction sa; sigset_t *mask = &sa.sa_mask; sigemptyset(mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; sa.sa_handler = sig_handler; sigaction(SIGTERM, &sa, 0); sigaction(SIGCHLD, &sa, 0); } #ifdef NGETTY_SORT #include "sortpfd.h" #define SORT_x_PFD k = sortpfd() #define KK_selfpipe k+1 #define KK k #else #define SORT_x_PFD #define KK_selfpipe argc #define KK npfd #endif #ifdef NGETTY_SELFPIPE #define DO_x_POLL poll(pfd-1, KK_selfpipe, -1) #else #define DO_x_POLL poll(pfd, KK, poll_timeout); poll_timeout = 512*1024 #endif /* * Synopsis: ngetty tty... * * Since each terminal's struct pollfd only takes up 8 bytes, just * alloca()te the array on the stack, instead of using the bloated malloc(). */ int main(int argc, char **argv) { int i,k; #ifdef NGETTY_SELFPIPE int pi[6]; #endif #ifdef NEED_HELPER_OPTION if ((helper=argv[1]) && helper[0] == '-' && helper[1] == 'H') { argv++; argc--; helper += 2; } else helper = NGETTY_HELPER; #endif npfd = argc - 1; tty = alloca(npfd * sizeof(tty_name_)); if (tty==0) _exit(111); pfd = alloca(argc * sizeof(pollfd_)); if (pfd==0) _exit(111); #ifdef NGETTY_SELFPIPE pfd->fd = 0; pfd->events = POLLIN; pfd++; #endif for (k = 0; k < npfd; k++) { tty[k].tty = *++argv; tty[k].st = 0; pfd[k].fd = -1; pfd[k].events = POLLIN; } for (k = 0; k < 127; k++) close(k); #ifdef NGETTY_SELFPIPE if (pipe(pi) || pi[0] != 0 || pi[1] != 1) _exit(111); for (k=0; k<2; k++) fcntl(k, F_SETFL, fcntl(k,F_GETFL,0) | O_NONBLOCK); #endif set_handler(); again: while ((i = waitpid(-1, NULL, WNOHANG)) > 1) for (k = 0; k < npfd; k++) if (pfd[k].fd == -i) { pfd[k].fd = -1; break; } for (k = 0; k < npfd; k++) if (pfd[k].fd == -1) opentty(k); SORT_x_PFD; i = DO_x_POLL; if (i==-1) { if (errno != EINTR) sig_handler(SIGTERM); /* poll failed, what to do? */ } else { for (i = 0; i < KK; i++) if (pfd[i].revents) exechelper(i,"login"); } #ifdef NGETTY_SELFPIPE read(0, pi, sizeof pi); #endif goto again; return 1; }