cleanup spec
[profile/ivi/mingetty.git] / mingetty.c
1 /*  mingetty.c
2  *
3  *  Copyright (C) 1996 Florian La Roche  <laroche@redhat.com>
4  *  Copyright (C) 2002, 2003 Red Hat, Inc
5  *
6  *  This getty can only be used as a small console getty. Look at mgetty
7  *  for a real modem getty.
8  *
9  *  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License
11  *  as published by the Free Software Foundation; either version
12  *  2 of the License, or (at your option) any later version.
13  */
14
15 /* TODO:
16  * - autologin only at first login
17  * - /etc/mingetty.conf that can be used instead of /etc/inittab for
18  *   command line options
19  * - Can UTF-8 setup be done within mingetty?
20  * - Also add /bin/login-type functionality in here?
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <termios.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <errno.h>
30 #include <sys/stat.h>
31 #include <sys/file.h>
32 #include <signal.h>
33 #include <fcntl.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #include <utmp.h>
37 #include <getopt.h>
38 #include <sys/param.h>
39 #include <syslog.h>
40 #include <sys/utsname.h>
41 #include <time.h>
42
43 /* name of this program (argv[0]) */
44 static char *progname;
45 /* on which tty line are we sitting? (e.g. tty1) */
46 static char *tty;
47 /* some information about this host */
48 static struct utsname uts;
49 /* the hostname */
50 static char hn[MAXHOSTNAMELEN + 1];
51 /* process and session ID of this program */
52 static pid_t pid, sid;
53 /* login program invoked */
54 static char *loginprog = "/bin/login";
55 /* Do not send a reset string to the terminal. */
56 static int noclear = 0;
57 /* Do not print a newline. */
58 static int nonewline = 0;
59 /* Do not print /etc/issue. */
60 static int noissue = 0;
61 /* Do not call vhangup() on the tty. */
62 static int nohangup = 0;
63 /* Do not print any hostname. */
64 static int nohostname = 0;
65 /* Print the whole string of gethostname() instead of just until the next "." */
66 static int longhostname = 0;
67 /* time to wait, seconds */
68 static int delay = 0;
69 /* chroot directory */
70 static char *ch_root = NULL;
71 /* working directory to change into */
72 static char *ch_dir = NULL;
73 /* 'nice' level of the program */
74 static int priority = 0;
75 /* automatic login with this user */
76 static char *autologin = NULL;
77 /* try to read a char before dropping to login prompt */
78 static int loginpause = 0;
79
80 /* error() - output error messages */
81 static void error (const char *fmt, ...)
82 {
83         va_list va_alist;
84
85         va_start (va_alist, fmt);
86         openlog (progname, LOG_PID, LOG_AUTH);
87         vsyslog (LOG_ERR, fmt, va_alist);
88         /* no need, we exit anyway: closelog (); */
89         va_end (va_alist);
90         sleep (5);
91         exit (EXIT_FAILURE);
92 }
93
94 /* update_utmp() - update our utmp entry */
95 static void update_utmp (void)
96 {
97         struct utmp ut;
98         struct utmp *utp;
99         time_t cur_time;
100
101         setutent ();
102         while ((utp = getutent ()))
103                 if (utp->ut_type == INIT_PROCESS && utp->ut_pid == pid)
104                         break;
105
106         if (utp) {
107                 memcpy (&ut, utp, sizeof (ut));
108         } else {
109                 /* some inits don't initialize utmp... */
110                 const char *x = tty;
111                 memset (&ut, 0, sizeof (ut));
112                 if (strncmp (x, "tty", 3) == 0)
113                         x += 3;
114                 if (strlen (x) > sizeof (ut.ut_id))
115                         x += strlen (x) - sizeof (ut.ut_id);
116                 strncpy (ut.ut_id, x, sizeof (ut.ut_id));
117         }
118
119         strncpy (ut.ut_user, "LOGIN", sizeof (ut.ut_user));
120         strncpy (ut.ut_line, tty, sizeof (ut.ut_line));
121         time (&cur_time);
122         ut.ut_time = cur_time;
123         ut.ut_type = LOGIN_PROCESS;
124         ut.ut_pid = pid;
125         ut.ut_session = sid;
126
127         pututline (&ut);
128         endutent ();
129
130         updwtmp (_PATH_WTMP, &ut);
131 }
132
133 /* open_tty - set up tty as standard { input, output, error } */
134 static void open_tty (void)
135 {
136         struct sigaction sa, sa_old;
137         char buf[40];
138         int fd;
139
140         /* Set up new standard input. */
141         if (tty[0] == '/')
142                 strcpy (buf, tty);
143         else {
144                 strcpy (buf, "/dev/");
145                 strcat (buf, tty);
146         }
147         /* There is always a race between this reset and the call to
148            vhangup() that s.o. can use to get access to your tty. */
149         if (chown (buf, 0, 0) || chmod (buf, 0600))
150                 if (errno != EROFS)
151                         error ("%s: %s", tty, strerror (errno));
152
153         sa.sa_handler = SIG_IGN;
154         sa.sa_flags = 0;
155         sigemptyset (&sa.sa_mask);
156         sigaction (SIGHUP, &sa, &sa_old);
157
158         /* vhangup() will replace all open file descriptors in the kernel
159            that point to our controlling tty by a dummy that will deny
160            further reading/writing to our device. It will also reset the
161            tty to sane defaults, so we don't have to modify the tty device
162            for sane settings. We also get a SIGHUP/SIGCONT.
163          */
164         if ((fd = open (buf, O_RDWR, 0)) < 0)
165                 error ("%s: cannot open tty: %s", tty, strerror (errno));
166         if (ioctl (fd, TIOCSCTTY, (void *) 1) == -1)
167                 error ("%s: no controlling tty: %s", tty, strerror (errno));
168         if (!isatty (fd))
169                 error ("%s: not a tty", tty);
170
171         if (nohangup == 0) {
172                 if (vhangup ())
173                         error ("%s: vhangup() failed", tty);
174                 /* Get rid of the present stdout/stderr. */
175                 close (2);
176                 close (1);
177                 close (0);
178                 close (fd);
179                 if ((fd = open (buf, O_RDWR, 0)) != 0)
180                         error ("%s: cannot open tty: %s", tty,
181                                 strerror (errno));
182                 if (ioctl (fd, TIOCSCTTY, (void *) 1) == -1)
183                         error ("%s: no controlling tty: %s", tty,
184                                 strerror (errno));
185         }
186         /* Set up stdin/stdout/stderr. */
187         if (dup2 (fd, 0) != 0 || dup2 (fd, 1) != 1 || dup2 (fd, 2) != 2)
188                 error ("%s: dup2(): %s", tty, strerror (errno));
189         if (fd > 2)
190                 close (fd);
191
192         /* Write a reset string to the terminal. This is very linux-specific
193            and should be checked for other systems. */
194         if (noclear == 0)
195                 write (0, "\033c", 2);
196
197         sigaction (SIGHUP, &sa_old, NULL);
198 }
199
200 static void output_special_char (unsigned char c)
201 {
202         switch (c) {
203         case 's':
204                 printf ("%s", uts.sysname);
205                 break;
206         case 'n':
207                 printf ("%s", uts.nodename);
208                 break;
209         case 'r':
210                 printf ("%s", uts.release);
211                 break;
212         case 'v':
213                 printf ("%s", uts.version);
214                 break;
215         case 'm':
216                 printf ("%s", uts.machine);
217                 break;
218         case 'o':
219                 printf ("%s", uts.domainname);
220                 break;
221         case 'd':
222         case 't':
223                 {
224                         time_t cur_time;
225                         struct tm *tm;
226 #if     0
227                         char buff[20];
228
229                         time (&cur_time);
230                         tm = localtime (&cur_time);
231                         strftime (buff, sizeof (buff),
232                                 c == 'd'? "%a %b %d %Y" : "%X", tm);
233                         fputs (buff, stdout);
234                         break;
235 #else
236                         time (&cur_time);
237                         tm = localtime (&cur_time);
238                         if (c == 'd') /* ISO 8601 */
239                                 printf ("%d-%02d-%02d", 1900 + tm->tm_year,
240                                         tm->tm_mon + 1, tm->tm_mday);
241                         else
242                                 printf ("%02d:%02d:%02d", tm->tm_hour,
243                                         tm->tm_min, tm->tm_sec);
244                         break;
245 #endif
246                 }
247
248         case 'l':
249                 printf ("%s", tty);
250                 break;
251         case 'u':
252         case 'U':
253                 {
254                         int users = 0;
255                         struct utmp *ut;
256                         setutent ();
257                         while ((ut = getutent ()))
258                                 if (ut->ut_type == USER_PROCESS)
259                                         users++;
260                         endutent ();
261                         printf ("%d", users);
262                         if (c == 'U')
263                                 printf (" user%s", users == 1 ? "" : "s");
264                         break;
265                 }
266         default:
267                 putchar (c);
268         }
269 }
270
271 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
272 static void do_prompt (int showlogin)
273 {
274         FILE *fd;
275         int c;
276
277         if (nonewline == 0)
278                 putchar ('\n');
279         if (noissue == 0 && (fd = fopen ("/etc/issue", "r"))) {
280                 while ((c = getc (fd)) != EOF) {
281                         if (c == '\\')
282                                 output_special_char (getc (fd));
283                         else
284                                 putchar (c);
285                 }
286                 fclose (fd);
287         }
288         if (loginpause) {
289                 puts ("[press ENTER to login]");
290                 getc (stdin);
291         }
292         if (nohostname == 0)
293                 printf ("%s ", hn);
294         if (showlogin)
295                 printf ("login: ");
296         fflush (stdout);
297 }
298
299 static char *get_logname (void)
300 {
301         static char logname[40];
302         char *bp;
303         unsigned char c;
304
305         tcflush (0, TCIFLUSH);          /* flush pending input */
306         for (*logname = 0; *logname == 0;) {
307                 do_prompt (1);
308                 for (bp = logname;;) {
309                         if (read (0, &c, 1) < 1) {
310                                 if (errno == EINTR || errno == EIO
311                                         || errno == ENOENT)
312                                         exit (EXIT_SUCCESS);
313                                 error ("%s: read: %s", tty, strerror (errno));
314                         }
315                         if (c == '\n' || c == '\r') {
316                                 *bp = 0;
317                                 break;
318                         } else if (!isprint (c))
319                                 error ("%s: invalid character 0x%x in login"
320                                         " name", tty, c);
321                         else if ((size_t)(bp - logname) >= sizeof (logname) - 1)
322                                 error ("%s: too long login name", tty);
323                         else
324                                 *bp++ = c;
325                 }
326         }
327         return logname;
328 }
329
330 static void usage (void)
331 {
332         error ("usage: '%s [--noclear] [--nonewline] [--noissue] "
333                 "[--nohangup] [--nohostname] [--long-hostname] "
334                 "[--loginprog=/bin/login] [--nice=10] [--delay=10] "
335                 "[--chdir=/home] [--chroot=/chroot] [--autologin=user] "
336                 "[--loginpause] "
337                 "tty' with e.g. tty=tty1", progname);
338 }
339
340 static struct option const long_options[] = {
341         { "autologin", required_argument, NULL, 'a' },
342         { "loginpause", no_argument, &loginpause, 'p' },
343         { "chdir", required_argument, NULL, 'w' },
344         { "chroot", required_argument, NULL, 'r' },
345         { "delay", required_argument, NULL, 'd' },
346         { "noclear", no_argument, &noclear, 1 },
347         { "nonewline", no_argument, &nonewline, 1 },
348         { "noissue", no_argument, &noissue, 1 },
349         { "nohangup", no_argument, &nohangup, 1 },
350         { "no-hostname", no_argument, &nohostname, 1 }, /* compat option */
351         { "nohostname", no_argument, &nohostname, 1 },
352         { "loginprog", required_argument, NULL, 'l' },
353         { "long-hostname", no_argument, &longhostname, 1 },
354         { "nice", required_argument, NULL, 'n' },
355         { 0, 0, 0, 0 }
356 };
357
358 int main (int argc, char **argv)
359 {
360         char *logname, *s;
361         int c;
362
363         progname = argv[0];
364         if (!progname)
365                 progname = "mingetty";
366         uname (&uts);
367         gethostname (hn, MAXHOSTNAMELEN);
368         hn[MAXHOSTNAMELEN] = '\0';
369         pid = getpid ();
370         sid = getsid (0);
371 #if     defined(s390) || defined(__s390__)
372         putenv ("TERM=dumb");
373 #else
374         putenv ("TERM=linux");
375 #endif
376
377         while ((c = getopt_long (argc, argv, "a:p:d:l:n:w:r:", long_options,
378                 (int *) 0)) != EOF) {
379                 switch (c) {
380                 case 0:
381                         break;
382                 case 'a':
383                         autologin = optarg;
384                         break;
385                 case 'd':
386                         delay = atoi (optarg);
387                         break;
388                 case 'l':
389                         loginprog = optarg;
390                         break;
391                 case 'n':
392                         priority = atoi (optarg);
393                         break;
394                 case 'r':
395                         ch_root = optarg;
396                         break;
397                 case 'w':
398                         ch_dir = optarg;
399                         break;
400                 default:
401                         usage ();
402                 }
403         }
404         if (longhostname == 0 && (s = strchr (hn, '.')))
405                 *s = '\0';
406         tty = argv[optind];
407         if (!tty)
408                 usage ();
409
410         if (strncmp (tty, "/dev/", 5) == 0) /* ignore leading "/dev/" */
411                 tty += 5;
412
413         update_utmp ();
414         if (delay)
415                 sleep (delay);
416         open_tty ();
417         if (autologin) {
418                 do_prompt (0);
419                 printf ("login: %s (automatic login)\n", autologin);
420                 logname = autologin;
421         } else
422                 while ((logname = get_logname ()) == 0)
423                         /* do nothing */ ;
424
425         if (ch_root)
426                 chroot (ch_root);
427         if (ch_dir)
428                 chdir (ch_dir);
429         if (priority)
430                 nice (priority);
431
432         execl (loginprog, loginprog, autologin? "-f" : "--", logname, NULL);
433         error ("%s: can't exec %s: %s", tty, loginprog, strerror (errno));
434         sleep (5);
435         exit (EXIT_FAILURE);
436 }