2 * sulogin This program gives Linux machines a reasonable
3 * secure way to boot single user. It forces the
4 * user to supply the root password before a
7 * If there is a shadow password file and the
8 * encrypted root password is "x" the shadow
9 * password will be used.
11 * Version: @(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl
13 * Copyright (C) 1998-2003 Miquel van Smoorenburg.
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 #include <sys/types.h>
44 #include <sys/ttydefaults.h>
46 #include <sys/ioctl.h>
48 # include <sys/statfs.h>
49 # include <sys/mount.h>
50 # include <linux/fs.h>
51 # include <linux/magic.h>
52 # include <linux/major.h>
54 # define TMPFS_MAGIC 0x01021994
59 # define dovoid(f) if ((f)){}
67 # include <selinux/selinux.h>
68 # include <selinux/get_context_list.h>
77 #define F_PASSWD "/etc/passwd"
78 #define F_SHADOW "/etc/shadow"
79 #define BINSH "/bin/sh"
80 #define STATICSH "/bin/sash"
82 char *Version
= "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl";
86 static volatile uint32_t openfd
; /* Remember higher file descriptors */
87 static volatile uint32_t *usemask
;
89 static sighandler_t saved_sigint
= SIG_DFL
;
90 static sighandler_t saved_sigtstp
= SIG_DFL
;
91 static sighandler_t saved_sigquit
= SIG_DFL
;
92 static sighandler_t saved_sighup
= SIG_DFL
;
94 static volatile sig_atomic_t alarm_rised
;
95 static volatile sig_atomic_t sigchild
;
102 * Fix the tty modes and set reasonable defaults.
105 void tcinit(struct console
*con
)
108 struct termios
*tio
= &con
->tio
;
114 /* Get line attributes */
115 if (tcgetattr(fd
, tio
) < 0) {
116 con
->flags
|= CON_NOTTY
;
120 /* Handle serial lines here */
121 if (ioctl (fd
, TIOCMGET
, (char*)&serial
) == 0) {
122 speed_t ispeed
, ospeed
;
125 /* this is a modem line */
126 con
->flags
|= CON_SERIAL
;
128 /* Flush input and output queues on modem lines */
129 (void) tcflush(fd
, TCIOFLUSH
);
131 ispeed
= cfgetispeed(tio
);
132 ospeed
= cfgetospeed(tio
);
134 if (!ispeed
) ispeed
= TTYDEF_SPEED
;
135 if (!ospeed
) ospeed
= TTYDEF_SPEED
;
137 tio
->c_iflag
= tio
->c_lflag
= tio
->c_oflag
= 0;
138 tio
->c_cflag
= CREAD
| CS8
| HUPCL
| (tio
->c_cflag
& CLOCAL
);
140 cfsetispeed(tio
, ispeed
);
141 cfsetospeed(tio
, ospeed
);
144 tio
->c_cc
[VTIME
] = 0;
147 if (ioctl(fd
, TIOCGWINSZ
, &ws
) == 0) {
149 if (ws
.ws_row
== 0) {
153 if (ws
.ws_col
== 0) {
157 (void)ioctl(fd
, TIOCSWINSZ
, &ws
);
162 #if defined(SANE_TIO) && (SANE_TIO == 1)
164 * Use defaults of <sys/ttydefaults.h> for base settings
165 * of a local terminal line like a virtual console.
167 tio
->c_iflag
|= TTYDEF_IFLAG
;
168 tio
->c_oflag
|= TTYDEF_OFLAG
;
169 tio
->c_lflag
|= TTYDEF_LFLAG
;
171 tio
->c_lflag
&= ~CBAUD
;
173 tio
->c_cflag
|= (B38400
| TTYDEF_CFLAG
);
175 /* Sane setting, allow eight bit characters, no carriage return delay
176 * the same result as `stty sane cr0 pass8'
178 tio
->c_iflag
|= (BRKINT
| ICRNL
| IMAXBEL
);
179 tio
->c_iflag
&= ~(IGNBRK
| INLCR
| IGNCR
| IXOFF
| IUCLC
| IXANY
| INPCK
| ISTRIP
);
180 tio
->c_oflag
|= (OPOST
| ONLCR
| NL0
| CR0
| TAB0
| BS0
| VT0
| FF0
);
181 tio
->c_oflag
&= ~(OLCUC
| OCRNL
| ONOCR
| ONLRET
| OFILL
| OFDEL
|\
182 NLDLY
|CRDLY
|TABDLY
|BSDLY
|VTDLY
|FFDLY
);
183 tio
->c_lflag
|= (ISIG
| ICANON
| IEXTEN
| ECHO
|ECHOE
|ECHOK
|ECHOKE
);
184 tio
->c_lflag
&= ~(ECHONL
|ECHOCTL
|ECHOPRT
| NOFLSH
| XCASE
| TOSTOP
);
185 tio
->c_cflag
|= (CREAD
| CS8
| HUPCL
);
186 tio
->c_cflag
&= ~(PARODD
| PARENB
);
189 * VTIME and VMIN can overlap with VEOF and VEOL since they are
190 * only used for non-canonical mode. We just set the at the
191 * beginning, so nothing bad should happen.
193 tio
->c_cc
[VTIME
] = 0;
194 tio
->c_cc
[VMIN
] = CMIN
;
195 tio
->c_cc
[VINTR
] = CINTR
;
196 tio
->c_cc
[VQUIT
] = CQUIT
;
197 tio
->c_cc
[VERASE
] = CERASE
; /* ASCII DEL (0177) */
198 tio
->c_cc
[VKILL
] = CKILL
;
199 tio
->c_cc
[VEOF
] = CEOF
;
201 tio
->c_cc
[VSWTC
] = _POSIX_VDISABLE
;
203 tio
->c_cc
[VSWTCH
] = _POSIX_VDISABLE
;
205 tio
->c_cc
[VSTART
] = CSTART
;
206 tio
->c_cc
[VSTOP
] = CSTOP
;
207 tio
->c_cc
[VSUSP
] = CSUSP
;
208 tio
->c_cc
[VEOL
] = _POSIX_VDISABLE
;
209 tio
->c_cc
[VREPRINT
] = CREPRINT
;
210 tio
->c_cc
[VDISCARD
] = CDISCARD
;
211 tio
->c_cc
[VWERASE
] = CWERASE
;
212 tio
->c_cc
[VLNEXT
] = CLNEXT
;
213 tio
->c_cc
[VEOL2
] = _POSIX_VDISABLE
;
216 /* Set line attributes */
217 tcsetattr(fd
, TCSANOW
, tio
);
219 /* Enable blocking mode for read and write */
220 if ((flags
= fcntl(fd
, F_GETFL
, 0)) != -1)
221 (void)fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
226 * Finalize the tty modes on modem lines.
229 void tcfinal(struct console
*con
)
231 struct termios
*tio
= &con
->tio
;
237 if ((con
->flags
& CON_SERIAL
) == 0) {
239 setenv("TERM", "linux", 1);
241 setenv("TERM", "vt100", 1);
245 if (con
->flags
& CON_NOTTY
)
247 setenv("TERM", "vt100", 1);
249 tio
->c_iflag
|= (IXON
| IXOFF
);
250 tio
->c_lflag
|= (ICANON
| ISIG
| ECHO
|ECHOE
|ECHOK
|ECHOKE
);
251 tio
->c_oflag
|= OPOST
;
253 tio
->c_cc
[VINTR
] = CINTR
;
254 tio
->c_cc
[VQUIT
] = CQUIT
;
255 tio
->c_cc
[VERASE
] = con
->cp
.erase
;
256 tio
->c_cc
[VKILL
] = con
->cp
.kill
;
257 tio
->c_cc
[VEOF
] = CEOF
;
259 tio
->c_cc
[VSWTC
] = _POSIX_VDISABLE
;
261 tio
->c_cc
[VSWTCH
] = _POSIX_VDISABLE
;
263 tio
->c_cc
[VSTART
] = CSTART
;
264 tio
->c_cc
[VSTOP
] = CSTOP
;
265 tio
->c_cc
[VSUSP
] = CSUSP
;
266 tio
->c_cc
[VEOL
] = _POSIX_VDISABLE
;
268 if (con
->cp
.eol
== CR
) {
269 tio
->c_iflag
|= ICRNL
;
270 tio
->c_iflag
&= ~(INLCR
|IGNCR
);
271 tio
->c_oflag
|= ONLCR
;
272 tio
->c_oflag
&= ~(OCRNL
|ONLRET
);
275 switch (con
->cp
.parity
) {
278 tio
->c_cflag
&= ~(PARODD
| PARENB
);
279 tio
->c_iflag
&= ~(INPCK
| ISTRIP
);
281 case 1: /* odd parity */
282 tio
->c_cflag
|= PARODD
;
284 case 2: /* even parity */
285 tio
->c_cflag
|= PARENB
;
286 tio
->c_iflag
|= (INPCK
| ISTRIP
);
288 case (1 | 2): /* no parity bit */
289 tio
->c_cflag
&= ~CSIZE
;
294 /* Set line attributes */
295 (void)tcsetattr(fd
, TCSANOW
, tio
);
303 __attribute__((__noinline__
))
304 void alrm_handler(int sig
__attribute__((unused
)))
306 void alrm_handler(int sig
)
317 __attribute__((__noinline__
))
318 void chld_handler(int sig
__attribute__((unused
)))
320 void chld_handler(int sig
)
327 * See if an encrypted password is valid. The encrypted
328 * password is checked for traditional-style DES and
329 * FreeBSD-style MD5 encryption.
332 int valid(const char *pass
)
339 if (pass
[0] == 0) return 1;
341 if (pass
[0] != '$') goto check_des
;
344 * up to 4 bytes for the signature e.g. $1$
346 for(s
= pass
+1; *s
&& *s
!= '$'; s
++)
348 if (*s
++ != '$') return 0;
349 if ((off
= (off_t
)(s
-pass
)) > 4 || off
< 3) return 0;
351 memset(id
, '\0', sizeof(id
));
352 strncpy(id
, pass
, off
);
355 * up to 16 bytes for the salt
357 for(; *s
&& *s
!= '$'; s
++)
359 if (*s
++ != '$') return 0;
360 if ((off_t
)(s
-pass
) > 16) return 0;
364 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
366 if ((strcmp(id
, "$1$") == 0) && (len
< 22 || len
> 24)) return 0;
369 * the SHA-256 hash 43 bytes
371 if ((strcmp(id
, "$5$") == 0) && (len
< 42 || len
> 44)) return 0;
374 * the SHA-512 hash 86 bytes
376 if ((strcmp(id
, "$6$") == 0) && (len
< 85 || len
> 87)) return 0;
385 if (strlen(pass
) != 13) return 0;
386 for (s
= pass
; *s
; s
++) {
387 if ((*s
< '0' || *s
> '9') &&
388 (*s
< 'a' || *s
> 'z') &&
389 (*s
< 'A' || *s
> 'Z') &&
390 *s
!= '.' && *s
!= '/') return 0;
397 * Set a variable if the value is not NULL.
400 void set(char **var
, char *val
)
406 * Get the root password entry.
409 struct passwd
*getrootpwent(int try_manually
)
411 static struct passwd pwd
;
415 static char line
[256];
416 static char sline
[256];
420 * First, we try to get the password the standard
421 * way using normal library calls.
423 if ((pw
= getpwnam("root")) &&
424 !strcmp(pw
->pw_passwd
, "x") &&
425 (spw
= getspnam("root")))
426 pw
->pw_passwd
= spw
->sp_pwdp
;
427 if (pw
|| !try_manually
) return pw
;
430 * If we come here, we could not retrieve the root
431 * password through library calls and we try to
432 * read the password and shadow files manually.
434 pwd
.pw_name
= "root";
436 pwd
.pw_gecos
= "Super User";
442 if ((fp
= fopen(F_PASSWD
, "r")) == NULL
) {
448 * Find root in the password file.
450 while((p
= fgets(line
, 256, fp
)) != NULL
) {
451 if (strncmp(line
, "root:", 5) != 0)
454 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
455 (void)strsep(&p
, ":");
456 (void)strsep(&p
, ":");
457 set(&pwd
.pw_gecos
, strsep(&p
, ":"));
458 set(&pwd
.pw_dir
, strsep(&p
, ":"));
459 set(&pwd
.pw_shell
, strsep(&p
, "\n"));
466 * If the encrypted password is valid
467 * or not found, return.
470 fprintf(stderr
, "sulogin: %s: no entry for root\n\r", F_PASSWD
);
473 if (valid(pwd
.pw_passwd
)) return &pwd
;
476 * The password is invalid. If there is a
477 * shadow password, try it.
479 strcpy(pwd
.pw_passwd
, "");
480 if ((fp
= fopen(F_SHADOW
, "r")) == NULL
) {
481 fprintf(stderr
, "sulogin: %s: root password garbled\n\r", F_PASSWD
);
484 while((p
= fgets(sline
, 256, fp
)) != NULL
) {
485 if (strncmp(sline
, "root:", 5) != 0)
488 set(&pwd
.pw_passwd
, strsep(&p
, ":"));
494 * If the password is still invalid,
495 * NULL it, and return.
498 fprintf(stderr
, "sulogin: %s: no entry for root\n\r", F_SHADOW
);
499 strcpy(pwd
.pw_passwd
, "");
501 if (!valid(pwd
.pw_passwd
)) {
502 fprintf(stderr
, "sulogin: %s: root password garbled\n\r", F_SHADOW
);
503 strcpy(pwd
.pw_passwd
, ""); }
508 * Ask by prompt for the password.
511 void doprompt(const char *crypted
, struct console
*con
)
515 if (con
->flags
& CON_SERIAL
) {
518 * For prompting: map NL in output to CR-NL
519 * otherwise we may see stairs in the output.
521 tty
.c_oflag
|= (ONLCR
| OPOST
);
522 (void) tcsetattr(con
->fd
, TCSADRAIN
, &tty
);
524 if (con
->file
== (FILE*)0) {
525 if ((con
->file
= fdopen(con
->fd
, "r+")) == (FILE*)0)
528 #if defined(USE_ONELINE)
530 fprintf(con
->file
, "Give root password for login: ");
532 fprintf(con
->file
, "Press enter for login: ");
535 fprintf(con
->file
, "Give root password for maintenance\n\r");
537 fprintf(con
->file
, "Press enter for maintenance");
538 fprintf(con
->file
, "(or type Control-D to continue): ");
542 if (con
->flags
& CON_SERIAL
)
543 (void) tcsetattr(con
->fd
, TCSADRAIN
, &con
->tio
);
547 * Make sure to have an own session and controlling terminal
550 void setup(struct console
*con
)
552 pid_t pid
, pgrp
, ppgrp
, ttypgrp
;
555 if (con
->flags
& CON_NOTTY
)
560 * Only go through this trouble if the new
561 * tty doesn't fall in this process group.
565 ppgrp
= getpgid(getppid());
566 ttypgrp
= tcgetpgrp(fd
);
568 if (pgrp
!= ttypgrp
&& ppgrp
!= ttypgrp
) {
569 if (pid
!= getsid(0)) {
570 if (pid
== getpgid(0))
571 setpgid(0, getpgid(getppid()));
575 signal(SIGHUP
, SIG_IGN
);
577 ioctl(0, TIOCNOTTY
, (char *)1);
578 signal(SIGHUP
, saved_sighup
);
579 if (fd
> 0) close(0);
580 if (fd
> 1) close(1);
581 if (fd
> 2) close(2);
583 ioctl(fd
, TIOCSCTTY
, (char *)1);
584 tcsetpgrp(fd
, ppgrp
);
591 for (fd
= 3; fd
< 32; fd
++) {
592 if (openfd
& (1<<fd
)) {
600 * Fetch the password. Note that there is no
601 * default timeout as we normally skip this during boot.
604 char *getpasswd(struct console
*con
)
606 static char pass
[128], *ptr
;
616 if (con
->flags
& CON_NOTTY
)
622 tty
.c_iflag
&= ~(IUCLC
|IXON
|IXOFF
|IXANY
);
623 tty
.c_lflag
&= ~(ECHO
|ECHOE
|ECHOK
|ECHONL
|TOSTOP
|ISIG
);
624 tc
= (tcsetattr(fd
, TCSAFLUSH
, &tty
) == 0);
626 sa
.sa_handler
= alrm_handler
;
628 sigaction(SIGALRM
, &sa
, NULL
);
629 if (timeout
) alarm(timeout
);
632 cp
->eol
= *ptr
= '\0';
634 eightbit
= ((con
->flags
& CON_SERIAL
) == 0 || (tty
.c_cflag
& (PARODD
|PARENB
)) == 0);
635 while (cp
->eol
== '\0') {
636 if (read(fd
, &c
, 1) < 1) {
637 if (errno
== EINTR
|| errno
== EAGAIN
) {
650 fprintf(stderr
, "sulogin: read(%s): %m\n\r", con
->tty
);
658 else if (c
!= (ascval
= (c
& 0177))) {
660 for (bits
= 1, mask
= 1; mask
& 0177; mask
<<= 1) {
664 cp
->parity
|= ((bits
& 1) ? 1 : 2);
684 while (ptr
> &pass
[0])
690 if ((size_t)(ptr
- &pass
[0]) >= (sizeof(pass
) -1 )) {
691 fprintf(stderr
, "sulogin: input overrun at %s\n\r", con
->tty
);
702 (void)tcsetattr(fd
, TCSAFLUSH
, &con
->tio
);
703 if (ret
&& *ret
!= '\0')
711 * Password was OK, execute a shell.
714 void sushell(struct passwd
*pwd
)
722 * Set directory and shell.
724 if (chdir(pwd
->pw_dir
) < 0) {
726 fprintf(stderr
, "sulogin: change of working directory failed: %m\n\r");
728 if ((p
= getenv("SUSHELL")) != NULL
)
730 else if ((p
= getenv("sushell")) != NULL
)
733 if (pwd
->pw_shell
[0])
734 sushell
= pwd
->pw_shell
;
738 if ((p
= strrchr(sushell
, '/')) == NULL
)
742 snprintf(shell
, sizeof(shell
), profile
? "-%s" : "%s", p
);
745 * Set some important environment variables.
747 if (getcwd(home
, sizeof(home
)) == (char*)0)
749 setenv("HOME", home
, 1);
750 setenv("LOGNAME", "root", 1);
751 setenv("USER", "root", 1);
753 setenv("SHLVL","0",1);
756 * Try to execute a shell.
758 setenv("SHELL", sushell
, 1);
759 signal(SIGINT
, saved_sigint
);
760 signal(SIGTSTP
, saved_sigtstp
);
761 signal(SIGQUIT
, saved_sigquit
);
762 signal(SIGHUP
, SIG_DFL
);
764 if (is_selinux_enabled() > 0) {
765 security_context_t scon
=NULL
;
768 if (getseuserbyname("root", &seuser
, &level
) == 0)
769 if (get_default_context_with_level(seuser
, level
, 0, &scon
) == 0) {
770 if (setexeccon(scon
) != 0)
771 fprintf(stderr
, "sulogin: setexeccon failed\n\r");
778 execl(sushell
, shell
, NULL
);
781 setenv("SHELL", BINSH
, 1);
782 execl(BINSH
, profile
? "-sh" : "sh", NULL
);
785 /* Fall back to staticly linked shell if both the users shell
786 and /bin/sh failed to execute. */
787 setenv("SHELL", STATICSH
, 1);
788 execl(STATICSH
, STATICSH
, NULL
);
794 * Make C library standard calls like ttyname(3) work.
796 static uint32_t mounts
;
797 #define MNT_PROCFS 0x0001
798 #define MNT_DEVTMPFS 0x0002
800 static __attribute__((__noinline__
))
803 if (mounts
& MNT_DEVTMPFS
)
804 umount2("/dev", MNT_DETACH
);
805 if (mounts
& MNT_PROCFS
)
806 umount2("/proc", MNT_DETACH
);
809 static __attribute__((__constructor__
))
813 if (statfs("/proc", &st
) == 0 && st
.f_type
!= PROC_SUPER_MAGIC
) {
814 if (mount("proc", "/proc", "proc", MS_RELATIME
, NULL
) == 0)
815 mounts
|= MNT_PROCFS
;
817 if (statfs("/dev", &st
) == 0 && st
.f_type
!= TMPFS_MAGIC
) {
818 if (mount("devtmpfs", "/dev", "devtmpfs", MS_RELATIME
, "mode=0755,nr_inodes=0") == 0) {
819 mounts
|= MNT_DEVTMPFS
;
820 (void)mknod("/dev/console", S_IFCHR
|S_IRUSR
|S_IWUSR
, makedev(TTYAUX_MAJOR
, 1));
821 if (symlink("/proc/self/fd", "/dev/fd") == 0) {
822 dovoid(symlink("fd/0", "/dev/stdin"));
823 dovoid(symlink("fd/1", "/dev/stdout"));
824 dovoid(symlink("fd/2", "/dev/stderr"));
828 if (mounts
) atexit(putmounts
);
835 fprintf(stderr
, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n\r");
838 int main(int argc
, char **argv
)
849 * We are init. We hence need to set uo a session.
851 if ((pid
= getpid()) == 1) {
853 (void)ioctl(0, TIOCSCTTY
, (char *)1);
857 * See if we have a timeout flag.
860 while((c
= getopt(argc
, argv
, "ept:")) != EOF
) switch(c
) {
862 timeout
= atoi(optarg
);
876 if (geteuid() != 0) {
877 fprintf(stderr
, "sulogin: only root can run sulogin.\n\r");
881 saved_sigint
= signal(SIGINT
, SIG_IGN
);
882 saved_sigquit
= signal(SIGQUIT
, SIG_IGN
);
883 saved_sigtstp
= signal(SIGTSTP
, SIG_IGN
);
884 saved_sighup
= signal(SIGHUP
, SIG_IGN
);
887 * See if we need to open an other tty device.
891 if (!tty
|| *tty
== '\0')
892 tty
= getenv("CONSOLE");
895 * Detect possible consoles, use stdin as fallback.
896 * If an optional tty is given, reconnect it to stdin.
898 reconnect
= detect_consoles(tty
, 0);
906 fprintf(stderr
, "sulogin: cannot open console: %m\n\r");
911 * If previous stdin was not the speified tty and therefore reconnected
912 * to the specified tty also reconnect stdout and stderr.
922 * Get the root password.
924 if ((pwd
= getrootpwent(opt_e
)) == NULL
) {
925 fprintf(stderr
, "sulogin: cannot open password database!\n\r");
930 * Prompt for input on the consoles
932 for (con
= consoles
; con
&& con
->id
< CONMAX
; con
= con
->next
) {
934 openfd
|= (1<<con
->fd
);
938 if ((con
->fd
= open(con
->tty
, O_RDWR
| O_NOCTTY
| O_NONBLOCK
)) < 0)
940 openfd
|= (1<<con
->fd
);
944 usemask
= (uint32_t*)mmap(NULL
, sizeof(uint32_t), PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_SHARED
, -1, 0);
946 if (con
->next
== (struct console
*)0)
949 signal(SIGCHLD
, chld_handler
);
951 switch ((con
->pid
= fork())) {
953 signal(SIGCHLD
, SIG_DFL
);
958 char *passwd
= pwd
->pw_passwd
;
960 int failed
= 0, doshell
= 0;
962 doprompt(passwd
, con
);
963 if ((answer
= getpasswd(con
)) == NULL
)
966 if (passwd
[0] == '\0')
970 cryptbuf
= crypt(answer
, passwd
);
971 if (cryptbuf
== NULL
)
972 fprintf(stderr
, "sulogin: crypt failed: %m\n\r");
973 else if (strcmp(cryptbuf
, pwd
->pw_passwd
) == 0)
978 *usemask
|= (1<<con
->id
);
980 *usemask
&= ~(1<<con
->id
);
984 signal(SIGQUIT
, SIG_IGN
);
985 signal(SIGTSTP
, SIG_IGN
);
986 signal(SIGINT
, SIG_IGN
);
989 fprintf(stderr
, "sulogin: can not execute su shell.\n\r");
992 fprintf(stderr
, "Login incorrect.\n\r");
997 fprintf(stderr
, "Timed out.\n\r");
1000 * User may pressed Control-D.
1004 fprintf(stderr
, "sulogin: can not fork: %m\n\r");
1009 } while ((con
= con
->next
) && (con
->id
< CONMAX
));
1011 while ((pid
= wait(&status
))) {
1012 if (errno
== ECHILD
)
1016 for (con
= consoles
; con
&& con
->id
< CONMAX
; con
= con
->next
) {
1017 if (con
->pid
== pid
) {
1018 *usemask
&= ~(1<<con
->id
);
1021 if (kill(con
->pid
, 0) < 0) {
1022 *usemask
&= ~(1<<con
->id
);
1025 if (*usemask
& (1<<con
->id
))
1027 kill(con
->pid
, SIGHUP
);
1029 kill(con
->pid
, SIGKILL
);
1032 signal(SIGCHLD
, SIG_DFL
);