]> Pileus Git - mkinit/blob - src/gettyd.c
Update gettyd
[mkinit] / src / gettyd.c
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdarg.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <termios.h>
10 #include <fcntl.h>
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/ioctl.h>
15 #include <sys/wait.h>
16 #include <sys/epoll.h>
17 #include <sys/signalfd.h>
18
19 /* TTY data */
20 typedef struct tty_t {
21         char         *path;
22         int           fd;
23         int           pid;
24         struct tty_t *next;
25 } tty_t;
26
27 /* Local Data */
28 static int running;
29 static int epoll;
30 static int sigs;
31 static tty_t *ttys;
32
33 /* Helper functions */
34 void error(const char *fmt, ...)
35 {
36         va_list ap;
37         va_start(ap, fmt);
38         fprintf(stderr, "Error ");
39         vfprintf(stderr, fmt, ap);
40         fprintf(stderr, ": %s\n", strerror(errno));
41         va_end(ap);
42         if (!running)
43                 exit(1);
44 }
45
46 int add_poll(int fd, void *ptr)
47 {
48         struct epoll_event ctl = {
49                 .events = EPOLLIN,
50                 .data.ptr = ptr,
51         };
52         return epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &ctl);
53 }
54
55 int del_poll(int fd)
56 {
57         return epoll_ctl(epoll, EPOLL_CTL_DEL, fd, NULL);
58 }
59
60 static int start_tty(tty_t *tty)
61 {
62         const char *prompt = "[Press enter to login]";
63         struct termios attr;
64
65         if (chown(tty->path, 0, 0) < 0)
66                 return -1;
67         if (chmod(tty->path, 0600) < 0)
68                 return -1;
69         if ((tty->fd = open(tty->path, O_RDWR|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0)) < 0)
70                 return -1;
71         if (add_poll(tty->fd, tty) < 0)
72                 return -1;
73
74         tcgetattr(tty->fd, &attr);
75         attr.c_lflag &= ~ECHO;
76         tcsetattr(tty->fd, TCSANOW, &attr);
77
78         write(tty->fd, "\033c", 2);
79         write(tty->fd, "\033[?1c", 5);
80         write(tty->fd, prompt, strlen(prompt));
81
82         return 0;
83 }
84
85 static void read_tty(tty_t *tty)
86 {
87         int flags;
88         char ch, login;
89         while (read(tty->fd, &ch, 1) == 1)
90                 if (ch == '\n' || ch == '\r')
91                         login = 1;
92         if (!login)
93                 return;
94         if ((tty->pid = fork()) < 0)
95                 return;
96         if (tty->pid == 0) {
97                 if (putenv("TERM=linux"))
98                         error("setting environment");
99                 if (setsid() < 0)
100                         error("setting sid");
101                 if (write(tty->fd, "\033c", 2) < 0)
102                         error("resetting tty");
103                 if ((flags = fcntl(tty->fd, F_GETFL)) < 0)
104                         error("getting fd flags");
105                 if (fcntl(tty->fd, F_SETFL, flags&~O_NONBLOCK) < 0)
106                         error("setting blocking flags");
107                 if (ioctl(tty->fd, TIOCSCTTY, 0) < 0)
108                         error("setting ctty");
109                 if (dup2(tty->fd, 0) != 0)
110                         error("setting stdin");
111                 if (dup2(tty->fd, 1) != 1)
112                         error("setting stdout");
113                 if (dup2(tty->fd, 2) != 2)
114                         error("setting stderr");
115                 if (execl("/bin/login", "login", NULL) < 0)
116                         error("execing login program");
117         }
118         del_poll(tty->fd);
119         close(tty->fd);
120 }
121
122 void on_child(void)
123 {
124         struct signalfd_siginfo info;
125         if (read(sigs, &info, sizeof(info)) != sizeof(info))
126                 return;
127         if (info.ssi_signo != SIGCHLD)
128                 return;
129
130         int status;
131         pid_t pid;
132         tty_t *tty;
133         while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
134                 for (tty = ttys; tty; tty = tty->next) {
135                         if (pid == tty->pid) {
136                                 tty->pid = 0;
137                                 start_tty(tty);
138                         }
139                 }
140         }
141 }
142
143 /* Main */
144 int main(int argc, char **argv)
145 {
146         int i, count;
147         tty_t *tty;
148         sigset_t mask;
149
150         /* Check arguments */
151         if (argc <= 1) {
152                 printf("usage: gettyd <tty> ...\n");
153                 return 0;
154         }
155
156         /* Setup */
157         sigemptyset(&mask);
158         sigaddset(&mask, SIGCHLD);
159         sigaddset(&mask, SIGHUP);
160         if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
161                 error("blocking signals");
162         if ((sigs = signalfd(-1, &mask, SFD_CLOEXEC)) < 0)
163                 error("creating signal fd");
164         if ((epoll = epoll_create1(EPOLL_CLOEXEC)) < 0)
165                 error("creating epoll");
166         if (add_poll(sigs, &sigs) < 0)
167                 error("adding signal epoll");
168
169         /* Open TTYs */
170         for (i = 1; i < argc; i++) {
171                 if (!(tty = malloc(sizeof(tty_t))))
172                         error("allocating memory");
173                 if (asprintf(&tty->path, "/dev/%s", argv[i]) < 0)
174                         error("allocating path name");
175                 if (start_tty(tty) < 0)
176                         error("starting tty '%s'", tty->path);
177                 tty->next = ttys;
178                 ttys = tty;
179         }
180
181         /* Main loop */
182         running = 1;
183         while (1) {
184                 struct epoll_event event;
185                 errno = 0;
186                 count = epoll_wait(epoll, &event, 1, -1);
187                 if (errno == EINTR)
188                         continue;
189                 if (count < 0)
190                         continue;
191                 if (event.data.ptr == &sigs)
192                         on_child();
193                 else
194                         read_tty(event.data.ptr);
195         }
196
197         return 0;
198 }