diff --git a/doc/Changelog b/doc/Changelog index 488baa4..d7b4261 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,10 @@ sysvinit (2.89dsf) UNRELEASED; urgency=low [ Werner Fink ] + * Sulogin: try to detect the real device(s) used for the system console + /dev/console if but only if /dev/console is used. On Linux this can + be more than one device, e.g. a serial line as well as a virtual + console as well as a simple printer. * Fix counting message lines in wall. Patch from Petr Lautrbach. * Fix bad printf conversion specifier in wall. Patch from Sébastien Luttringer. * Add patches from Openwall project. Thanks goes to Solar Designer. diff --git a/src/Makefile b/src/Makefile index 6c04f55..1ca8040 100644 --- a/src/Makefile +++ b/src/Makefile @@ -105,7 +105,7 @@ utmpdump: utmpdump.o runlevel: runlevel.o sulogin: LDLIBS += $(SULOGINLIBS) $(STATIC) -sulogin: sulogin.o +sulogin: sulogin.o consoles.o wall: dowall.o wall.o diff --git a/src/consoles.c b/src/consoles.c index 8db7ad1..d3e6c00 100644 --- a/src/consoles.c +++ b/src/consoles.c @@ -85,8 +85,8 @@ err: #ifdef __linux__ /* - * Read and determine active attribute for tty, - * the caller has to free the result. + * Read and determine active attribute for tty below + * /sys/class/tty, the caller has to free the result. */ static __attribute__((__malloc__)) @@ -109,7 +109,10 @@ err: return ret; } -/* Read and determine device attribute for tty */ +/* + * Read and determine device attribute for tty below + * /sys/class/tty. + */ static dev_t devattr(const char *tty) { @@ -136,6 +139,10 @@ err: } #endif /* __linux__ */ +/* + * Search below /dev for the characer device in + * the local `dev_t comparedev' variable. + */ static dev_t comparedev; static #ifdef __GNUC__ @@ -166,6 +173,9 @@ char* scandev(DIR *dir) return name; } +/* + * Default control characters for an unknown terminal line. + */ static struct chardata initcp = { CERASE, @@ -174,6 +184,12 @@ struct chardata initcp = { 0 }; +/* + * Allocate an aligned `struct console' memory area, + * initialize its default values, and append it to + * the global linked list. + */ + static int concount; /* Counter for console IDs */ static @@ -204,6 +220,12 @@ void consalloc(char * name) consoles->next = tail; } +/* + * Try to detect the real device(s) used for the system console + * /dev/console if but only if /dev/console is used. On Linux + * this can be more than one device, e.g. a serial line as well + * as a virtual console as well as a simple printer. + */ void detect_consoles(const char *device, int fallback) { int fd; diff --git a/src/sulogin.c b/src/sulogin.c index 91cbe7c..99451c2 100644 --- a/src/sulogin.c +++ b/src/sulogin.c @@ -28,8 +28,10 @@ * */ +#include #include #include +#include #include #include #include @@ -44,13 +46,34 @@ #include #if defined(__GLIBC__) # include +# define dovoid(f) if ((f)){} #endif +#ifdef __linux__ +# include +# include +# include +# include +# include +# ifndef TMPFS_MAGIC +# define TMPFS_MAGIC 0x01021994 +# endif +# ifndef MNT_DETACH +# define MNT_DETACH 2 +# endif +#endif + +#define BS CTRL('h') +#define NL CTRL('j') +#define CR CTRL('m') #ifdef WITH_SELINUX # include # include #endif +#include "consoles.h" +#define CONMAX 16 + #define CHECK_DES 1 #define CHECK_MD5 1 @@ -63,102 +86,245 @@ char *Version = "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl"; static int timeout; static int profile; +static volatile uint32_t openfd; /* Remember higher file descriptors */ +static volatile uint32_t *usemask; -static void (*saved_sigint) = SIG_DFL; -static void (*saved_sigtstp) = SIG_DFL; -static void (*saved_sigquit) = SIG_DFL; +static sighandler_t saved_sigint = SIG_DFL; +static sighandler_t saved_sigtstp = SIG_DFL; +static sighandler_t saved_sigquit = SIG_DFL; +static sighandler_t saved_sighup = SIG_DFL; static volatile sig_atomic_t alarm_rised; +static volatile sig_atomic_t sigchild; #ifndef IUCLC # define IUCLC 0 #endif -#if defined(SANE_TIO) && (SANE_TIO == 1) /* * Fix the tty modes and set reasonable defaults. - * (I'm not sure if this is needed under Linux, but..) */ static -void fixtty(void) +void tcinit(struct console *con) { - struct termios tty; - int serial; + int serial, flags; + struct termios *tio = &con->tio; + int fd = con->fd; - /* Skip serial console */ - if (ioctl (0, TIOCMGET, (char*)&serial) == 0) - goto out; /* Expected error */ serial = errno = 0; - tcgetattr(0, &tty); + /* Get line attributes */ + if (tcgetattr(fd, tio) < 0) { + con->flags |= CON_NOTTY; + return; + } - /* Use defaults of for base settings */ - tty.c_iflag |= TTYDEF_IFLAG; - tty.c_oflag |= TTYDEF_OFLAG; - tty.c_lflag |= TTYDEF_LFLAG; - tty.c_cflag |= (TTYDEF_SPEED | TTYDEF_CFLAG); + /* Handle serial lines here */ + if (ioctl (fd, TIOCMGET, (char*)&serial) == 0) { + speed_t ispeed, ospeed; + struct winsize ws; + + /* this is a modem line */ + con->flags |= CON_SERIAL; + + /* Flush input and output queues on modem lines */ + (void) tcflush(fd, TCIOFLUSH); + + ispeed = cfgetispeed(tio); + ospeed = cfgetospeed(tio); + + if (!ispeed) ispeed = TTYDEF_SPEED; + if (!ospeed) ospeed = TTYDEF_SPEED; + + tio->c_iflag = tio->c_lflag = tio->c_oflag = 0; + tio->c_cflag = CREAD | CS8 | HUPCL | (tio->c_cflag & CLOCAL); + + cfsetispeed(tio, ispeed); + cfsetospeed(tio, ospeed); + + tio->c_line = 0; + tio->c_cc[VTIME] = 0; + tio->c_cc[VMIN] = 1; + + if (ioctl(fd, TIOCGWINSZ, &ws) == 0) { + int set = 0; + if (ws.ws_row == 0) { + ws.ws_row = 24; + set++; + } + if (ws.ws_col == 0) { + ws.ws_col = 80; + set++; + } + (void)ioctl(fd, TIOCSWINSZ, &ws); + } + + goto setattr; + } +#if defined(SANE_TIO) && (SANE_TIO == 1) + /* + * Use defaults of for base settings + * of a local terminal line like a virtual console. + */ + tio->c_iflag |= TTYDEF_IFLAG; + tio->c_oflag |= TTYDEF_OFLAG; + tio->c_lflag |= TTYDEF_LFLAG; +# ifdef CBAUD + tio->c_lflag &= ~CBAUD; +# endif + tio->c_cflag |= (B38400 | TTYDEF_CFLAG); /* Sane setting, allow eight bit characters, no carriage return delay * the same result as `stty sane cr0 pass8' */ - tty.c_iflag |= (BRKINT | ICRNL | IMAXBEL); - tty.c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | ISTRIP); - tty.c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0); - tty.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |\ + tio->c_iflag |= (BRKINT | ICRNL | IMAXBEL); + tio->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | INPCK | ISTRIP); + tio->c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0); + tio->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |\ NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); - tty.c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE); - tty.c_lflag &= ~(ECHONL | NOFLSH | XCASE | TOSTOP | ECHOPRT); - tty.c_cflag |= (CREAD | CS8 | B9600); - tty.c_cflag &= ~(PARENB); + tio->c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE); + tio->c_lflag &= ~(ECHONL|ECHOCTL|ECHOPRT | NOFLSH | XCASE | TOSTOP); + tio->c_cflag |= (CREAD | CS8 | HUPCL); + tio->c_cflag &= ~(PARODD | PARENB); - /* VTIME and VMIN can overlap with VEOF and VEOL since they are + /* + * VTIME and VMIN can overlap with VEOF and VEOL since they are * only used for non-canonical mode. We just set the at the * beginning, so nothing bad should happen. */ - tty.c_cc[VTIME] = 0; - tty.c_cc[VMIN] = 1; - tty.c_cc[VINTR] = CINTR; - tty.c_cc[VQUIT] = CQUIT; - tty.c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */ - tty.c_cc[VKILL] = CKILL; - tty.c_cc[VEOF] = CEOF; - tty.c_cc[VSWTC] = _POSIX_VDISABLE; - tty.c_cc[VSTART] = CSTART; - tty.c_cc[VSTOP] = CSTOP; - tty.c_cc[VSUSP] = CSUSP; - tty.c_cc[VEOL] = _POSIX_VDISABLE; - tty.c_cc[VREPRINT] = CREPRINT; - tty.c_cc[VDISCARD] = CDISCARD; - tty.c_cc[VWERASE] = CWERASE; - tty.c_cc[VLNEXT] = CLNEXT; - tty.c_cc[VEOL2] = _POSIX_VDISABLE; - - tcsetattr(0, TCSANOW, &tty); -out: - return; -} + tio->c_cc[VTIME] = 0; + tio->c_cc[VMIN] = CMIN; + tio->c_cc[VINTR] = CINTR; + tio->c_cc[VQUIT] = CQUIT; + tio->c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */ + tio->c_cc[VKILL] = CKILL; + tio->c_cc[VEOF] = CEOF; +# ifdef VSWTC + tio->c_cc[VSWTC] = _POSIX_VDISABLE; +# else + tio->c_cc[VSWTCH] = _POSIX_VDISABLE; +# endif + tio->c_cc[VSTART] = CSTART; + tio->c_cc[VSTOP] = CSTOP; + tio->c_cc[VSUSP] = CSUSP; + tio->c_cc[VEOL] = _POSIX_VDISABLE; + tio->c_cc[VREPRINT] = CREPRINT; + tio->c_cc[VDISCARD] = CDISCARD; + tio->c_cc[VWERASE] = CWERASE; + tio->c_cc[VLNEXT] = CLNEXT; + tio->c_cc[VEOL2] = _POSIX_VDISABLE; #endif +setattr: + /* Set line attributes */ + tcsetattr(fd, TCSANOW, tio); + /* Enable blocking mode for read and write */ + if ((flags = fcntl(fd, F_GETFL, 0)) != -1) + (void)fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +} + + +/* + * Finalize the tty modes on modem lines. + */ +static +void tcfinal(struct console *con) +{ + int serial; + struct termios *tio = &con->tio; + int fd = con->fd; + + /* Expected error */ + serial = errno = 0; + + if ((con->flags & CON_SERIAL) == 0) { +#ifdef __linux__ + setenv("TERM", "linux", 1); +#else + setenv("TERM", "vt100", 1); +#endif + return; + } + if (con->flags & CON_NOTTY) + return; + setenv("TERM", "vt100", 1); + + tio->c_iflag |= (IXON | IXOFF); + tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE); + tio->c_oflag |= OPOST; + + tio->c_cc[VINTR] = CINTR; + tio->c_cc[VQUIT] = CQUIT; + tio->c_cc[VERASE] = con->cp.erase; + tio->c_cc[VKILL] = con->cp.kill; + tio->c_cc[VEOF] = CEOF; +#ifdef VSWTC + tio->c_cc[VSWTC] = _POSIX_VDISABLE; +#else + tio->c_cc[VSWTCH] = _POSIX_VDISABLE; +#endif + tio->c_cc[VSTART] = CSTART; + tio->c_cc[VSTOP] = CSTOP; + tio->c_cc[VSUSP] = CSUSP; + tio->c_cc[VEOL] = _POSIX_VDISABLE; + + if (con->cp.eol == CR) { + tio->c_iflag |= ICRNL; + tio->c_iflag &= ~(INLCR|IGNCR); + tio->c_oflag |= ONLCR; + tio->c_oflag &= ~(OCRNL|ONLRET); + } + + switch (con->cp.parity) { + default: + case 0: + tio->c_cflag &= ~(PARODD | PARENB); + tio->c_iflag &= ~(INPCK | ISTRIP); + break; + case 1: /* odd parity */ + tio->c_cflag |= PARODD; + /* fall through */ + case 2: /* even parity */ + tio->c_cflag |= PARENB; + tio->c_iflag |= (INPCK | ISTRIP); + /* fall through */ + case (1 | 2): /* no parity bit */ + tio->c_cflag &= ~CSIZE; + tio->c_cflag |= CS7; + break; + } + + /* Set line attributes */ + (void)tcsetattr(fd, TCSANOW, tio); +} /* * Called at timeout. */ static # ifdef __GNUC__ +__attribute__((__noinline__)) void alrm_handler(int sig __attribute__((unused))) # else void alrm_handler(int sig) # endif { - /* Timeout expired */ alarm_rised++; +} - signal(SIGINT, saved_sigint); - signal(SIGTSTP, saved_sigtstp); - signal(SIGQUIT, saved_sigquit); - - /* Never use exit(3) or stdio(3) within a signal handler */ +/* + * Called at timeout. + */ +static +# ifdef __GNUC__ +__attribute__((__noinline__)) +void chld_handler(int sig __attribute__((unused))) +# else +void chld_handler(int sig) +# endif +{ + sigchild++; } /* @@ -305,7 +471,7 @@ struct passwd *getrootpwent(int try_manually) * or not found, return. */ if (p == NULL) { - fprintf(stderr, "%s: no entry for root\n", F_PASSWD); + fprintf(stderr, "sulogin: %s: no entry for root\n\r", F_PASSWD); return &pwd; } if (valid(pwd.pw_passwd)) return &pwd; @@ -316,7 +482,7 @@ struct passwd *getrootpwent(int try_manually) */ strcpy(pwd.pw_passwd, ""); if ((fp = fopen(F_SHADOW, "r")) == NULL) { - fprintf(stderr, "%s: root password garbled\n", F_PASSWD); + fprintf(stderr, "sulogin: %s: root password garbled\n\r", F_PASSWD); return &pwd; } while((p = fgets(sline, 256, fp)) != NULL) { @@ -333,67 +499,212 @@ struct passwd *getrootpwent(int try_manually) * NULL it, and return. */ if (p == NULL) { - fprintf(stderr, "%s: no entry for root\n", F_SHADOW); + fprintf(stderr, "sulogin: %s: no entry for root\n\r", F_SHADOW); strcpy(pwd.pw_passwd, ""); } if (!valid(pwd.pw_passwd)) { - fprintf(stderr, "%s: root password garbled\n", F_SHADOW); + fprintf(stderr, "sulogin: %s: root password garbled\n\r", F_SHADOW); strcpy(pwd.pw_passwd, ""); } return &pwd; } /* - * Ask for the password. Note that there is no + * Ask by prompt for the password. + */ +static +void doprompt(const char *crypted, struct console *con) +{ + struct termios tty; + + if (con->flags & CON_SERIAL) { + tty = con->tio; + /* + * For prompting: map NL in output to CR-NL + * otherwise we may see stairs in the output. + */ + tty.c_oflag |= (ONLCR | OPOST); + (void) tcsetattr(con->fd, TCSADRAIN, &tty); + } + if (con->file == (FILE*)0) { + if ((con->file = fdopen(con->fd, "r+")) == (FILE*)0) + goto err; + } +#if defined(USE_ONELINE) + if (crypted[0]) + fprintf(con->file, "Give root password for login: "); + else + fprintf(con->file, "Press enter for login: "); +#else + if (crypted[0]) + fprintf(con->file, "Give root password for maintenance\n\r"); + else + fprintf(con->file, "Press enter for maintenance"); + fprintf(con->file, "(or type Control-D to continue): "); +#endif + fflush(con->file); +err: + if (con->flags & CON_SERIAL) + (void) tcsetattr(con->fd, TCSADRAIN, &con->tio); +} + +/* + * Make sure to have an own session and controlling terminal + */ +static +void setup(struct console *con) +{ + pid_t pid, pgrp, ppgrp, ttypgrp; + int fd; + + if (con->flags & CON_NOTTY) + return; + fd = con->fd; + + /* + * Only go through this trouble if the new + * tty doesn't fall in this process group. + */ + pid = getpid(); + pgrp = getpgid(0); + ppgrp = getpgid(getppid()); + ttypgrp = tcgetpgrp(fd); + + if (pgrp != ttypgrp && ppgrp != ttypgrp) { + if (pid != getsid(0)) { + if (pid == getpgid(0)) + setpgid(0, getpgid(getppid())); + setsid(); + } + + signal(SIGHUP, SIG_IGN); + if (ttypgrp > 0) + ioctl(0, TIOCNOTTY, (char *)1); + signal(SIGHUP, saved_sighup); + if (fd > 0) close(0); + if (fd > 1) close(1); + if (fd > 2) close(2); + + ioctl(fd, TIOCSCTTY, (char *)1); + tcsetpgrp(fd, ppgrp); + } + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + con->fd = 0; + + for (fd = 3; fd < 32; fd++) { + if (openfd & (1<flags & CON_NOTTY) + goto out; + fd = con->fd; + cp = &con->cp; + + tty = con->tio; tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); - tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); - tcsetattr(0, TCSANOW, &tty); - - pass[sizeof(pass) - 1] = 0; + tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG); + tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0); sa.sa_handler = alrm_handler; sa.sa_flags = 0; sigaction(SIGALRM, &sa, NULL); if (timeout) alarm(timeout); - if (read(0, pass, sizeof(pass) - 1) <= 0) - ret = NULL; - else { - for(i = 0; i < (int)sizeof(pass) && pass[i]; i++) - if (pass[i] == '\r' || pass[i] == '\n') { - pass[i] = 0; + ptr = &pass[0]; + cp->eol = *ptr = '\0'; + + eightbit = ((con->flags & CON_SERIAL) == 0 || (tty.c_cflag & (PARODD|PARENB)) == 0); + while (cp->eol == 0) { + if (read(fd, &c, 1) < 1) { + if (errno == EINTR || errno == EAGAIN) { + usleep(1000); + continue; + } + ret = (char*)0; + switch (errno) { + case 0: + case EIO: + case ESRCH: + case EINVAL: + case ENOENT: + break; + default: + fprintf(stderr, "sulogin: read(%s): %m\n\r", con->tty); break; } - } - alarm(0); - tcsetattr(0, TCSANOW, &old); - printf("\n"); + goto quit; + } + if (eightbit) + ascval = c; + else if (c != (ascval = (c & 0177))) { + uint32_t bits, mask; + for (bits = 1, mask = 1; mask & 0177; mask <<= 1) { + if (mask & ascval) + bits++; + } + cp->parity |= ((bits & 1) ? 1 : 2); + } + + switch (ascval) { + case CR: + case NL: + *ptr = '\0'; + cp->eol = ascval; + break; + case BS: + case CERASE: + cp->erase = ascval; + if (ptr > &pass[0]) + ptr--; + break; + case CKILL: + cp->kill = ascval; + while (ptr > &pass[0]) + ptr--; + break; + case CEOF: + goto quit; + default: + if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) { + fprintf(stderr, "sulogin: input overrun at %s\n\r", con->tty); + ret = (char*)0; + goto quit; + } + *ptr++ = ascval; + break; + } + } +quit: + alarm(0); + if (tc) + (void)tcsetattr(fd, TCSAFLUSH, &con->tio); + if (ret && *ret != '\0') + tcfinal(con); + printf("\r\n"); +out: return ret; } @@ -411,7 +722,10 @@ void sushell(struct passwd *pwd) /* * Set directory and shell. */ - (void)chdir(pwd->pw_dir); + if (chdir(pwd->pw_dir) < 0) { + if (chdir("/") < 0) + fprintf(stderr, "sulogin: change of working directory failed: %m\n\r"); + } if ((p = getenv("SUSHELL")) != NULL) sushell = p; else if ((p = getenv("sushell")) != NULL) @@ -431,7 +745,8 @@ void sushell(struct passwd *pwd) /* * Set some important environment variables. */ - getcwd(home, sizeof(home)); + if (getcwd(home, sizeof(home)) == (char*)0) + strcpy(home, "/"); setenv("HOME", home, 1); setenv("LOGNAME", "root", 1); setenv("USER", "root", 1); @@ -445,17 +760,18 @@ void sushell(struct passwd *pwd) signal(SIGINT, saved_sigint); signal(SIGTSTP, saved_sigtstp); signal(SIGQUIT, saved_sigquit); + signal(SIGHUP, SIG_DFL); #ifdef WITH_SELINUX if (is_selinux_enabled() > 0) { - security_context_t scon=NULL; - char *seuser=NULL; - char *level=NULL; - if (getseuserbyname("root", &seuser, &level) == 0) - if (get_default_context_with_level(seuser, level, 0, &scon) == 0) { - if (setexeccon(scon) != 0) - fprintf(stderr, "setexeccon faile\n"); - freecon(scon); - } + security_context_t scon=NULL; + char *seuser=NULL; + char *level=NULL; + if (getseuserbyname("root", &seuser, &level) == 0) + if (get_default_context_with_level(seuser, level, 0, &scon) == 0) { + if (setexeccon(scon) != 0) + fprintf(stderr, "sulogin: setexeccon failed\n\r"); + freecon(scon); + } free(seuser); free(level); } @@ -474,20 +790,68 @@ void sushell(struct passwd *pwd) perror(STATICSH); } +#ifdef __linux__ +/* + * Make C library standard calls like ttyname(3) work. + */ +static uint32_t mounts; +#define MNT_PROCFS 0x0001 +#define MNT_DEVTMPFS 0x0002 + +static __attribute__((__noinline__)) +void putmounts(void) +{ + if (mounts & MNT_DEVTMPFS) + umount2("/dev", MNT_DETACH); + if (mounts & MNT_PROCFS) + umount2("/proc", MNT_DETACH); +} + +static __attribute__((__constructor__)) +void getmounts(void) +{ + struct statfs st; + if (statfs("/proc", &st) == 0 && st.f_type != PROC_SUPER_MAGIC) { + if (mount("proc", "/proc", "proc", MS_RELATIME, NULL) == 0) + mounts |= MNT_PROCFS; + } + if (statfs("/dev", &st) == 0 && st.f_type != TMPFS_MAGIC) { + if (mount("devtmpfs", "/dev", "devtmpfs", MS_RELATIME, "mode=0755,nr_inodes=0") == 0) { + mounts |= MNT_DEVTMPFS; + (void)mknod("/dev/console", S_IFCHR|S_IRUSR|S_IWUSR, makedev(TTYAUX_MAJOR, 1)); + if (symlink("/proc/self/fd", "/dev/fd") == 0) { + dovoid(symlink("fd/0", "/dev/stdin")); + dovoid(symlink("fd/1", "/dev/stdout")); + dovoid(symlink("fd/2", "/dev/stderr")); + } + } + } + if (mounts) atexit(putmounts); +} +#endif + static void usage(void) { - fprintf(stderr, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n"); + fprintf(stderr, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n\r"); } int main(int argc, char **argv) { char *tty = NULL; - char *p; struct passwd *pwd; - int c, fd = -1; + int c, status = 0; int opt_e = 0; - pid_t pid, pgrp, ppgrp, ttypgrp; + struct console *con; + pid_t pid; + + /* + * We are init. We hence need to set uo a session. + */ + if ((pid = getpid()) == 1) { + setsid(); + (void)ioctl(0, TIOCSCTTY, (char *)1); + } /* * See if we have a timeout flag. @@ -510,115 +874,138 @@ int main(int argc, char **argv) } if (geteuid() != 0) { - fprintf(stderr, "sulogin: only root can run sulogin.\n"); + fprintf(stderr, "sulogin: only root can run sulogin.\n\r"); exit(1); } + saved_sigint = signal(SIGINT, SIG_IGN); + saved_sigquit = signal(SIGQUIT, SIG_IGN); + saved_sigtstp = signal(SIGTSTP, SIG_IGN); + saved_sighup = signal(SIGHUP, SIG_IGN); + /* * See if we need to open an other tty device. */ - saved_sigint = signal(SIGINT, SIG_IGN); - saved_sigquit = signal(SIGQUIT, SIG_IGN); - saved_sigtstp = signal(SIGTSTP, SIG_IGN); - if (optind < argc) tty = argv[optind]; + if (optind < argc) + tty = argv[optind]; + if (!tty || *tty == '\0') + tty = getenv("CONSOLE"); - if (tty || (tty = getenv("CONSOLE"))) { + /* + * Detect possible consoles, use stdin as fallback. + */ + detect_consoles(tty, 0); - if ((fd = open(tty, O_RDWR)) < 0) { - perror(tty); - fd = dup(0); - } - - if (!isatty(fd)) { - fprintf(stderr, "%s: not a tty\n", tty); - close(fd); - } else { - - /* - * Only go through this trouble if the new - * tty doesn't fall in this process group. - */ - pid = getpid(); - pgrp = getpgid(0); - ppgrp = getpgid(getppid()); - ttypgrp = tcgetpgrp(fd); - - if (pgrp != ttypgrp && ppgrp != ttypgrp) { - if (pid != getsid(0)) { - if (pid == getpgid(0)) - setpgid(0, getpgid(getppid())); - setsid(); - } - - signal(SIGHUP, SIG_IGN); - if (ttypgrp > 0) - ioctl(0, TIOCNOTTY, (char *)1); - signal(SIGHUP, SIG_DFL); - close(0); - close(1); - close(2); - if (fd > 2) - close(fd); - if ((fd = open(tty, O_RDWR|O_NOCTTY)) < 0) { - perror(tty); - } else { - ioctl(0, TIOCSCTTY, (char *)1); - tcsetpgrp(fd, ppgrp); - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - } else - if (fd > 2) - close(fd); - } - } else if (getpid() == 1) { - /* We are init. We hence need to set a session anyway */ - setsid(); - if (ioctl(0, TIOCSCTTY, (char *)1)) - perror("ioctl(TIOCSCTTY)"); + /* + * Should not happen + */ + if (!consoles) { + if (!errno) + errno = ENOMEM; + fprintf(stderr, "sulogin: cannot open console: %m\n\r"); + exit(1); } -#if defined(SANE_TIO) && (SANE_TIO == 1) - fixtty(); -#endif - /* * Get the root password. */ if ((pwd = getrootpwent(opt_e)) == NULL) { - fprintf(stderr, "sulogin: cannot open password database!\n"); + fprintf(stderr, "sulogin: cannot open password database!\n\r"); sleep(2); } /* - * Ask for the password. + * Prompt for input on the consoles */ - while(pwd) { - int failed = 0; - if ((p = getpasswd(pwd->pw_passwd)) == NULL) break; - if (pwd->pw_passwd[0] == 0 || - strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd) == 0) { - sushell(pwd); - failed++; + for (con = consoles; con && con->id < CONMAX; con = con->next) { + if (con->fd >= 0) { + openfd |= (1<fd); + tcinit(con); + continue; } - signal(SIGQUIT, SIG_IGN); - signal(SIGTSTP, SIG_IGN); - signal(SIGINT, SIG_IGN); - if (failed) { - printf("Can not execute su shell.\n"); - break; - } else - printf("Login incorrect.\n"); + if ((con->fd = open(con->tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) + continue; + openfd |= (1<fd); + tcinit(con); } + con = consoles; + usemask = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); - if (alarm_rised) - printf("Timed out.\n"); + if (con->next == (struct console*)0) + goto nofork; + + signal(SIGCHLD, chld_handler); + do { + switch ((con->pid = fork())) { + case 0: + signal(SIGCHLD, SIG_DFL); + /* fall through */ + nofork: + setup(con); + while (1) { + char *passwd = pwd->pw_passwd; + char *answer; + int failed = 0; + + doprompt(passwd, con); + if ((answer = getpasswd(con)) == NULL) + break; + + if (passwd[0] == '\0' || + strcmp(crypt(answer, passwd), passwd) == 0) { + *usemask |= (1<id); + sushell(pwd); + *usemask &= ~(1<id); + failed++; + } + signal(SIGQUIT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGINT, SIG_IGN); + + if (failed) { + fprintf(stderr, "sulogin: can not execute su shell.\n\r"); + break; + } + fprintf(stderr, "Login incorrect.\n\r"); + } + if (alarm_rised) { + tcfinal(con); + printf("Timed out.\n\r"); + } + /* + * User may pressed Control-D. + */ + exit(0); + case -1: + fprintf(stderr, "sulogin: can not fork: %m\n\r"); + /* fall through */ + default: + break; + } + } while ((con = con->next) && (con->id < CONMAX)); + + while ((pid = wait(&status))) { + if (errno == ECHILD) + break; + if (pid < 0) + continue; + for (con = consoles; con && con->id < CONMAX; con = con->next) { + if (con->pid == pid) { + *usemask &= ~(1<id); + continue; + } + if (kill(con->pid, 0) < 0) { + *usemask &= ~(1<id); + continue; + } + if (*usemask & (1<id)) + continue; + kill(con->pid, SIGHUP); + usleep(5000); + kill(con->pid, SIGKILL); + } + } + signal(SIGCHLD, SIG_DFL); - /* - * User may pressed Control-D. - */ return 0; }