1/* See LICENSE file for copyright and license details. */
2
3#include <sys/ioctl.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6
7#include <fcntl.h>
8#include <limits.h>
9#include <signal.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14#include <utmp.h>
15
16#include "config.h"
17#include "util.h"
18#include "wexec.h"
19
20static char *tty = "/dev/tty1";
21static char *defaultterm = "linux";
22
23static void
24usage(void)
25{
26 eprintf("usage: %s [tty] [term] [cmd] [args...]\n", argv0);
27}
28
29// ?man getty: set terminal mode
30// ?man arguments: tty [term] [cmd] [args ...]
31// ?man set terminal line discipline, speed, and mode
32int
33main(int argc, char *argv[])
34{
35 char term[128], logname[LOGIN_NAME_MAX], c;
36 char hostname[HOST_NAME_MAX + 1];
37 char ut_line_buf[UT_LINESIZE + 1];
38 struct utmp usr;
39 struct sigaction sa;
40 FILE *fp;
41 int fd;
42 unsigned int i = 0;
43 ssize_t n;
44 long pos;
45
46 ARGBEGIN
47 {
48 default:
49 usage();
50 }
51 ARGEND;
52
53 strlcpy(term, defaultterm, sizeof(term));
54 if (argc > 0) {
55 tty = argv[0];
56 if (argc > 1)
57 strlcpy(term, argv[1], sizeof(term));
58 }
59
60 sa.sa_handler = SIG_IGN;
61 sa.sa_flags = 0;
62 sigemptyset(&sa.sa_mask);
63 sigaction(SIGHUP, &sa, NULL);
64
65 setenv("TERM", term, 1);
66
67 setsid();
68
69 fd = open(tty, O_RDWR);
70 if (fd < 0)
71 eprintf("open %s:", tty);
72 if (isatty(fd) == 0)
73 eprintf("%s is not a tty\n", tty);
74
75 /* steal the controlling terminal if necessary */
76 if (ioctl(fd, TIOCSCTTY, (void *)1) != 0)
77 weprintf("TIOCSCTTY: could not set controlling tty\n");
78 vhangup();
79 close(fd);
80
81 fd = open(tty, O_RDWR);
82 if (fd < 0)
83 eprintf("open %s:", tty);
84 dup2(fd, 0);
85 dup2(fd, 1);
86 dup2(fd, 2);
87 if (fchown(fd, 0, 0) < 0)
88 weprintf("fchown %s:", tty);
89 if (fchmod(fd, 0600) < 0)
90 weprintf("fchmod %s:", tty);
91 if (fd > 2)
92 close(fd);
93
94 sa.sa_handler = SIG_DFL;
95 sa.sa_flags = 0;
96 sigemptyset(&sa.sa_mask);
97 sigaction(SIGHUP, &sa, NULL);
98
99 /* Clear all utmp entries for this tty */
100 fp = fopen(UTMP_PATH, "r+");
101 if (fp) {
102 do {
103 pos = ftell(fp);
104 if (fread(&usr, sizeof(usr), 1, fp) != 1)
105 break;
106 if (usr.ut_line[0] == '\0')
107 continue;
108 memcpy(ut_line_buf, usr.ut_line, UT_LINESIZE);
109 ut_line_buf[UT_LINESIZE] = '\0';
110 if (strcmp(ut_line_buf, tty) != 0)
111 continue;
112 memset(&usr, 0, sizeof(usr));
113 fseek(fp, pos, SEEK_SET);
114 if (fwrite(&usr, sizeof(usr), 1, fp) != 1)
115 break;
116 } while (1);
117 if (ferror(fp))
118 weprintf("%s: I/O error:", UTMP_PATH);
119 fclose(fp);
120 }
121
122 if (argc > 2) {
123 wexecvp_self(argv[2], argv + 2);
124 return 0;
125 }
126
127 if (gethostname(hostname, sizeof(hostname)) == 0)
128 printf("%s ", hostname);
129 printf("login: ");
130 fflush(stdout);
131
132 /* Flush pending input */
133 ioctl(0, TCFLSH, (void *)0);
134 memset(logname, 0, sizeof(logname));
135 while (1) {
136 n = read(0, &c, 1);
137 if (n < 0)
138 eprintf("read:");
139 if (n == 0)
140 return 1;
141 if (i >= sizeof(logname) - 1)
142 eprintf("login name too long\n");
143 if (c == '\n' || c == '\r')
144 break;
145 logname[i++] = c;
146 }
147 if (logname[0] == '-')
148 eprintf("login name cannot start with '-'\n");
149 if (logname[0] == '\0')
150 return 1;
151 wexecvp_self("login", (char *const[]){"login", "-p", logname, NULL});
152 return 0;
153}