[16271] | 1 | /* mingetty.c |
---|
| 2 | * |
---|
| 3 | * Copyright (C) 1996 Florian La Roche |
---|
| 4 | * florian@jurix.jura.uni-sb.de florian@suse.de |
---|
| 5 | * |
---|
| 6 | * Newer versions should be on susix.jura.uni-sb.de/pub/linux/source/system |
---|
| 7 | * or sunsite.unc.edu/pub/Linux/system/Admin/login or /pub/Linux/system/ |
---|
| 8 | * Daemons/init/. |
---|
| 9 | * |
---|
| 10 | * utmp-handling is from agetty in util-linux 2.5 (probably from |
---|
| 11 | * Peter Orbaek <poe@daimi.aau.dk>) |
---|
| 12 | * |
---|
| 13 | * S.u.S.E. - GmbH has paid some of the time that was needed to write |
---|
| 14 | * this program. Thanks a lot for their support. |
---|
| 15 | * |
---|
| 16 | * This getty can only be used as console getty. It is very small, but |
---|
| 17 | * should be very reliable. For a modem getty, I'd also use nothing else |
---|
| 18 | * but mgetty. |
---|
| 19 | * |
---|
| 20 | * Usage: mingetty [--noclear] tty |
---|
| 21 | * Example entry in /etc/inittab: 1:123:respawn:/sbin/mingetty tty1 |
---|
| 22 | * |
---|
| 23 | * This program is free software; you can redistribute it and/or |
---|
| 24 | * modify it under the terms of the GNU General Public License |
---|
| 25 | * as published by the Free Software Foundation; either version |
---|
| 26 | * 2 of the License, or (at your option) any later version. |
---|
| 27 | * |
---|
| 28 | */ |
---|
| 29 | |
---|
| 30 | #define DEBUG_THIS 0 |
---|
| 31 | |
---|
| 32 | #include <stdio.h> |
---|
| 33 | #include <stdlib.h> |
---|
| 34 | #include <unistd.h> |
---|
| 35 | #include <string.h> |
---|
| 36 | #include <sys/ioctl.h> |
---|
| 37 | #include <errno.h> |
---|
| 38 | #include <sys/stat.h> |
---|
| 39 | #include <sys/file.h> |
---|
| 40 | #include <signal.h> |
---|
| 41 | #include <fcntl.h> |
---|
| 42 | #include <stdarg.h> |
---|
| 43 | #include <ctype.h> |
---|
| 44 | #include <utmp.h> |
---|
| 45 | #include <getopt.h> |
---|
| 46 | |
---|
| 47 | #ifndef _PATH_LOGIN |
---|
| 48 | #define _PATH_LOGIN "/bin/login" |
---|
| 49 | #endif |
---|
| 50 | |
---|
| 51 | #ifdef linux |
---|
| 52 | #include <sys/param.h> |
---|
| 53 | #define USE_SYSLOG |
---|
| 54 | #endif |
---|
| 55 | |
---|
| 56 | /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ |
---|
| 57 | #ifdef USE_SYSLOG |
---|
| 58 | #include <sys/syslog.h> |
---|
| 59 | #endif |
---|
| 60 | |
---|
| 61 | #define ISSUE "/etc/issue" /* displayed before the login prompt */ |
---|
| 62 | #include <sys/utsname.h> |
---|
| 63 | #include <time.h> |
---|
| 64 | |
---|
| 65 | #define LOGIN " login: " /* login prompt */ |
---|
| 66 | |
---|
| 67 | /* name of this program (argv[0]) */ |
---|
| 68 | static char *progname; |
---|
| 69 | /* on which tty line are we sitting? (e.g. tty1) */ |
---|
| 70 | static char *tty; |
---|
| 71 | /* some information about this host */ |
---|
| 72 | static struct utsname uts; |
---|
| 73 | /* the hostname */ |
---|
| 74 | static char hn[MAXHOSTNAMELEN + 1]; |
---|
| 75 | /* process ID of this program */ |
---|
| 76 | static pid_t pid; |
---|
| 77 | /* current time */ |
---|
| 78 | static time_t cur_time; |
---|
| 79 | /* do not send a reset string to the terminal ? */ |
---|
| 80 | static int noclear = 0; |
---|
| 81 | /* Print the whole string of gethostname() instead of just until the next "." */ |
---|
| 82 | static int longhostname = 0; |
---|
| 83 | |
---|
| 84 | |
---|
| 85 | /* |
---|
| 86 | * output error messages |
---|
| 87 | */ |
---|
| 88 | static void error (const char *fmt, ...) |
---|
| 89 | { |
---|
| 90 | va_list va_alist; |
---|
| 91 | char buf[256], *bp; |
---|
| 92 | #ifndef USE_SYSLOG |
---|
| 93 | int fd; |
---|
| 94 | #endif |
---|
| 95 | |
---|
| 96 | #ifdef USE_SYSLOG |
---|
| 97 | buf[0] = '\0'; |
---|
| 98 | bp = buf; |
---|
| 99 | #else |
---|
| 100 | strcpy (buf, progname); |
---|
| 101 | strcat (buf, ": "); |
---|
| 102 | bp = buf + strlen (buf); |
---|
| 103 | #endif |
---|
| 104 | |
---|
| 105 | va_start (va_alist, fmt); |
---|
| 106 | vsprintf (bp, fmt, va_alist); |
---|
| 107 | va_end (va_alist); |
---|
| 108 | |
---|
| 109 | #ifdef USE_SYSLOG |
---|
| 110 | openlog (progname, LOG_PID, LOG_AUTH); |
---|
| 111 | syslog (LOG_ERR, buf); |
---|
| 112 | closelog (); |
---|
| 113 | #else |
---|
| 114 | strcat (bp, "\r\n"); |
---|
| 115 | if ((fd = open ("/dev/console", 1)) >= 0) { |
---|
| 116 | write (fd, buf, strlen (buf)); |
---|
| 117 | close (fd); |
---|
| 118 | } |
---|
| 119 | #endif |
---|
| 120 | exit (1); |
---|
| 121 | } |
---|
| 122 | |
---|
| 123 | /* |
---|
| 124 | * update_utmp - update our utmp entry |
---|
| 125 | * |
---|
| 126 | * The utmp file holds miscellaneous information about things started by |
---|
| 127 | * /sbin/init and other system-related events. Our purpose is to update |
---|
| 128 | * the utmp entry for the current process, in particular the process |
---|
| 129 | * type and the tty line we are listening to. Return successfully only |
---|
| 130 | * if the utmp file can be opened for update, and if we are able to find |
---|
| 131 | * our entry in the utmp file. |
---|
| 132 | */ |
---|
| 133 | static void update_utmp (void) |
---|
| 134 | { |
---|
| 135 | struct utmp ut; |
---|
| 136 | int ut_fd; |
---|
| 137 | struct utmp *utp; |
---|
| 138 | void locktimeout(); |
---|
| 139 | |
---|
| 140 | utmpname (_PATH_UTMP); |
---|
| 141 | setutent (); |
---|
| 142 | while ((utp = getutent ())) |
---|
| 143 | if (utp->ut_type == INIT_PROCESS && utp->ut_pid == pid) |
---|
| 144 | break; |
---|
| 145 | |
---|
| 146 | if (utp) { |
---|
| 147 | memcpy (&ut, utp, sizeof (ut)); |
---|
| 148 | } else { |
---|
| 149 | /* some inits don't initialize utmp... */ |
---|
| 150 | /* XXX we should print out a warning message */ |
---|
| 151 | memset (&ut, 0, sizeof (ut)); |
---|
| 152 | strncpy (ut.ut_id, tty + 3, sizeof (ut.ut_id)); |
---|
| 153 | } |
---|
| 154 | endutent (); |
---|
| 155 | |
---|
| 156 | strncpy (ut.ut_user, "LOGIN", sizeof (ut.ut_user)); |
---|
| 157 | strncpy (ut.ut_line, tty, sizeof (ut.ut_line)); |
---|
| 158 | ut.ut_time = cur_time; |
---|
| 159 | ut.ut_type = LOGIN_PROCESS; |
---|
| 160 | ut.ut_pid = pid; |
---|
| 161 | |
---|
| 162 | pututline (&ut); |
---|
| 163 | endutent (); |
---|
| 164 | |
---|
| 165 | if ((ut_fd = open (_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) { |
---|
| 166 | (void)signal(SIGALRM, locktimeout); |
---|
| 167 | (void)alarm(3); |
---|
| 168 | flock (ut_fd, LOCK_EX); |
---|
| 169 | (void)alarm(0); |
---|
| 170 | write (ut_fd, &ut, sizeof (ut)); |
---|
| 171 | flock (ut_fd, LOCK_UN); |
---|
| 172 | close (ut_fd); |
---|
| 173 | } |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | /* open_tty - set up tty as standard { input, output, error } */ |
---|
| 177 | static void open_tty (void) |
---|
| 178 | { |
---|
| 179 | struct sigaction sa; |
---|
| 180 | char buf[20]; |
---|
| 181 | int fd; |
---|
| 182 | |
---|
| 183 | /* Set up new standard input. */ |
---|
| 184 | strcpy (buf, "/dev/"); |
---|
| 185 | strcat (buf, tty); |
---|
| 186 | if (chown (buf, 0, 0) || chmod (buf, 0600)) |
---|
| 187 | error ("%s: %s", buf, sys_errlist[errno]); |
---|
| 188 | |
---|
| 189 | sa.sa_handler = SIG_IGN; |
---|
| 190 | sa.sa_flags = 0; |
---|
| 191 | sigemptyset (&sa.sa_mask); |
---|
| 192 | sigaction (SIGHUP, &sa, NULL); |
---|
| 193 | |
---|
| 194 | /* vhangup() will replace all open file descriptors that point to our |
---|
| 195 | controlling tty by a dummy that will deny further reading/writing |
---|
| 196 | to our device. It will also reset the tty to sane defaults, so we |
---|
| 197 | don't have to modify the tty device for sane settings. |
---|
| 198 | We also get a SIGHUP/SIGCONT. |
---|
| 199 | */ |
---|
| 200 | if ((fd = open (buf, O_RDWR, 0)) < 0 |
---|
| 201 | || ioctl (fd, TIOCSCTTY, (void *)1) == -1) |
---|
| 202 | error ("%s: cannot open tty: %s", buf, sys_errlist[errno]); |
---|
| 203 | if (!isatty (fd)) |
---|
| 204 | error ("%s: not a tty", buf); |
---|
| 205 | |
---|
| 206 | vhangup (); |
---|
| 207 | /* Get rid of the present stdout/stderr. */ |
---|
| 208 | close (2); |
---|
| 209 | close (1); |
---|
| 210 | close (0); |
---|
| 211 | close (fd); |
---|
| 212 | /* ioctl (0, TIOCNOTTY, (char *)1); */ |
---|
| 213 | |
---|
| 214 | if (open (buf, O_RDWR, 0) != 0) |
---|
| 215 | error ("%s: cannot open as standard input: %s", buf, |
---|
| 216 | sys_errlist[errno]); |
---|
| 217 | |
---|
| 218 | /* Set up standard output and standard error file descriptors. */ |
---|
| 219 | if (dup (0) != 1 || dup (0) != 2) |
---|
| 220 | error ("%s: dup problem: %s", buf, sys_errlist[errno]); |
---|
| 221 | |
---|
| 222 | /* Write a reset string to the terminal. This is very linux-specific |
---|
| 223 | and should be checked for other systems. */ |
---|
| 224 | if (! noclear) |
---|
| 225 | write (0, "\033c", 2); |
---|
| 226 | |
---|
| 227 | sa.sa_handler = SIG_DFL; |
---|
| 228 | sa.sa_flags = 0; |
---|
| 229 | sigemptyset (&sa.sa_mask); |
---|
| 230 | sigaction (SIGHUP, &sa, NULL); |
---|
| 231 | |
---|
| 232 | #if DEBUG_THIS |
---|
| 233 | printf ("session=%d, pid=%d, pgid=%d\n", getsid (0), getpid (), |
---|
| 234 | getpgid (0)); |
---|
| 235 | #endif |
---|
| 236 | } |
---|
| 237 | |
---|
| 238 | static void output_special_char (unsigned char c) |
---|
| 239 | { |
---|
| 240 | switch (c) { |
---|
| 241 | case 's': |
---|
| 242 | printf ("%s", uts.sysname); |
---|
| 243 | break; |
---|
| 244 | case 'n': |
---|
| 245 | printf ("%s", uts.nodename); |
---|
| 246 | break; |
---|
| 247 | case 'r': |
---|
| 248 | printf ("%s", uts.release); |
---|
| 249 | break; |
---|
| 250 | case 'v': |
---|
| 251 | printf ("%s", uts.version); |
---|
| 252 | break; |
---|
| 253 | case 'm': |
---|
| 254 | printf ("%s", uts.machine); |
---|
| 255 | break; |
---|
| 256 | case 'o': |
---|
| 257 | printf ("%s", uts.domainname); |
---|
| 258 | break; |
---|
| 259 | #if 0 |
---|
| 260 | case 'd': |
---|
| 261 | case 't': |
---|
| 262 | { |
---|
| 263 | char *weekday[] = |
---|
| 264 | {"Sun", "Mon", "Tue", "Wed", "Thu", |
---|
| 265 | "Fri", "Sat"}; |
---|
| 266 | char *month[] = |
---|
| 267 | {"Jan", "Feb", "Mar", "Apr", "May", |
---|
| 268 | "Jun", "Jul", "Aug", "Sep", "Oct", |
---|
| 269 | "Nov", "Dec"}; |
---|
| 270 | time_t now; |
---|
| 271 | struct tm *tm; |
---|
| 272 | |
---|
| 273 | time (&now); |
---|
| 274 | tm = localtime (&now); |
---|
| 275 | |
---|
| 276 | if (c == 'd') |
---|
| 277 | printf ("%s %s %d %d", |
---|
| 278 | weekday[tm->tm_wday], month[tm->tm_mon], |
---|
| 279 | tm->tm_mday, |
---|
| 280 | tm->tm_year < 70 ? tm->tm_year + 2000 : |
---|
| 281 | tm->tm_year + 1900); |
---|
| 282 | else |
---|
| 283 | printf ("%02d:%02d:%02d", |
---|
| 284 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
---|
| 285 | |
---|
| 286 | break; |
---|
| 287 | } |
---|
| 288 | #else |
---|
| 289 | case 'd': |
---|
| 290 | case 't': |
---|
| 291 | { |
---|
| 292 | char buff[20]; |
---|
| 293 | struct tm *tm = localtime (&cur_time); |
---|
| 294 | strftime (buff, sizeof (buff), |
---|
| 295 | c == 'd'? "%a %b %d %Y" : "%X", tm); |
---|
| 296 | fputs (buff, stdout); |
---|
| 297 | break; |
---|
| 298 | } |
---|
| 299 | #endif |
---|
| 300 | |
---|
| 301 | case 'l': |
---|
| 302 | printf ("%s", tty); |
---|
| 303 | break; |
---|
| 304 | case 'u': |
---|
| 305 | case 'U': |
---|
| 306 | { |
---|
| 307 | int users = 0; |
---|
| 308 | struct utmp *ut; |
---|
| 309 | setutent (); |
---|
| 310 | while ((ut = getutent ())) |
---|
| 311 | if (ut->ut_type == USER_PROCESS) |
---|
| 312 | users++; |
---|
| 313 | endutent (); |
---|
| 314 | printf ("%d", users); |
---|
| 315 | if (c == 'U') |
---|
| 316 | printf (" user%s", users == 1 ? "" : "s"); |
---|
| 317 | break; |
---|
| 318 | } |
---|
| 319 | default: |
---|
| 320 | putchar (c); |
---|
| 321 | } |
---|
| 322 | } |
---|
| 323 | |
---|
| 324 | /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ |
---|
| 325 | static void do_prompt (void) |
---|
| 326 | { |
---|
| 327 | #if ! OLD |
---|
| 328 | FILE *fd; |
---|
| 329 | #else |
---|
| 330 | int fd; |
---|
| 331 | #endif |
---|
| 332 | char c; |
---|
| 333 | |
---|
| 334 | write (1, "\n", 1); /* start a new line */ |
---|
| 335 | #if ! OLD |
---|
| 336 | if ((fd = fopen (ISSUE, "r"))) { |
---|
| 337 | while ((c = getc (fd)) != EOF) { |
---|
| 338 | if (c == '\\') |
---|
| 339 | output_special_char (getc(fd)); |
---|
| 340 | else |
---|
| 341 | putchar (c); |
---|
| 342 | } |
---|
| 343 | fflush (stdout); |
---|
| 344 | fclose (fd); |
---|
| 345 | } |
---|
| 346 | #else |
---|
| 347 | if ((fd = open (ISSUE, O_RDONLY)) >= 0) { |
---|
| 348 | close (fd); |
---|
| 349 | } |
---|
| 350 | #endif |
---|
| 351 | write (1, hn, strlen (hn)); |
---|
| 352 | write (1, LOGIN, sizeof (LOGIN) - 1); |
---|
| 353 | } |
---|
| 354 | |
---|
| 355 | /* get_logname - get user name, establish speed, erase, kill, eol */ |
---|
| 356 | static char *get_logname (void) |
---|
| 357 | { |
---|
| 358 | static char logname[40]; |
---|
| 359 | char *bp; |
---|
| 360 | unsigned char c; |
---|
| 361 | |
---|
| 362 | ioctl (0, TCFLSH, 0); /* flush pending input */ |
---|
| 363 | |
---|
| 364 | for (*logname = 0; *logname == 0;) { |
---|
| 365 | do_prompt (); |
---|
| 366 | for (bp = logname;;) { |
---|
| 367 | if (read (0, &c, 1) < 1) { |
---|
| 368 | if (errno == EINTR || errno == EIO |
---|
| 369 | || errno == ENOENT) |
---|
| 370 | exit (0); |
---|
| 371 | error ("%s: read: %s", tty, sys_errlist[errno]); |
---|
| 372 | } |
---|
| 373 | if (c == '\n' || c == '\r') { |
---|
| 374 | *bp = 0; |
---|
| 375 | break; |
---|
| 376 | } else if (!isprint (c)) |
---|
| 377 | error ("%s: invalid character %c in login name", |
---|
| 378 | tty, c); |
---|
| 379 | else if (bp - logname >= sizeof (logname) - 1) |
---|
| 380 | error ("%s: too long login name", tty); |
---|
| 381 | else |
---|
| 382 | *bp++ = c; |
---|
| 383 | } |
---|
| 384 | } |
---|
| 385 | return logname; |
---|
| 386 | } |
---|
| 387 | |
---|
| 388 | static void usage (void) |
---|
| 389 | { |
---|
| 390 | error ("usage: '%s tty' with e.g. tty=tty1", progname); |
---|
| 391 | } |
---|
| 392 | |
---|
| 393 | static struct option const long_options[] = { |
---|
| 394 | { "noclear", no_argument, &noclear, 1}, |
---|
| 395 | { "long-hostname", no_argument, &longhostname, 1}, |
---|
| 396 | { 0, 0, 0, 0 } |
---|
| 397 | }; |
---|
| 398 | |
---|
| 399 | /* |
---|
| 400 | * main program |
---|
| 401 | */ |
---|
| 402 | int main (int argc, char **argv) |
---|
| 403 | { |
---|
| 404 | char *logname, *s; |
---|
| 405 | int c; |
---|
| 406 | |
---|
| 407 | progname = argv[0]; |
---|
| 408 | uname (&uts); |
---|
| 409 | gethostname (hn, MAXHOSTNAMELEN); |
---|
| 410 | pid = getpid (); |
---|
| 411 | time (&cur_time); |
---|
| 412 | #if 1 |
---|
| 413 | putenv ("TERM=linux"); |
---|
| 414 | #endif |
---|
| 415 | |
---|
| 416 | while ((c = getopt_long (argc, argv, "", long_options, (int *) 0)) |
---|
| 417 | != EOF) { |
---|
| 418 | switch (c) { |
---|
| 419 | case 0: |
---|
| 420 | break; |
---|
| 421 | default: |
---|
| 422 | usage (); |
---|
| 423 | } |
---|
| 424 | } |
---|
| 425 | if (!longhostname && (s = strchr(hn, '.'))) |
---|
| 426 | *s = '\0'; |
---|
| 427 | tty = argv[optind]; |
---|
| 428 | if (! tty) |
---|
| 429 | usage (); |
---|
| 430 | |
---|
| 431 | update_utmp (); |
---|
| 432 | open_tty (); |
---|
| 433 | #if 0 |
---|
| 434 | #ifdef linux |
---|
| 435 | ioctl (0, TIOCSPGRP, &pid); |
---|
| 436 | #endif |
---|
| 437 | #endif |
---|
| 438 | /* flush input and output queues, important for modems */ |
---|
| 439 | ioctl (0, TCFLSH, 2); |
---|
| 440 | |
---|
| 441 | while ((logname = get_logname ()) == 0); |
---|
| 442 | |
---|
| 443 | execl (_PATH_LOGIN, _PATH_LOGIN, "--", logname, NULL); |
---|
| 444 | error ("%s: can't exec " _PATH_LOGIN ": %s", tty, sys_errlist[errno]); |
---|
| 445 | exit (0); |
---|
| 446 | } |
---|
| 447 | |
---|
| 448 | |
---|
| 449 | void locktimeout() |
---|
| 450 | { |
---|
| 451 | #ifdef USE_SYSLOG |
---|
| 452 | openlog (progname, LOG_PID, LOG_AUTH); |
---|
| 453 | syslog(LOG_ALERT, "Lock failed on wtmp"); |
---|
| 454 | closelog (); |
---|
| 455 | #else |
---|
| 456 | int fd; |
---|
| 457 | char buf[]="Lock failed on wtmp\n"; |
---|
| 458 | if ((fd = open ("/dev/console", 1)) >= 0) { |
---|
| 459 | write (fd, buf, strlen (buf)); |
---|
| 460 | close (fd); |
---|
| 461 | } |
---|
| 462 | #endif |
---|
| 463 | } |
---|