3 // Copyright (C) 2006 Benedikt Böhm <hollow@gentoo.org>
4 // Based on vserver-utils' vlogin program.
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; version 2 of the License.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include <lib/vserver.h>
34 #include <sys/ioctl.h>
36 #include <sys/socket.h>
41 #include <sys/prctl.h>
43 #define ENSC_WRAPPERS_PREFIX "vlogin: "
44 #define ENSC_WRAPPERS_IOCTL 1
45 #define ENSC_WRAPPERS_UNISTD 1
46 #define ENSC_WRAPPERS_SOCKET 1
47 #define ENSC_WRAPPERS_IO 1
48 #define ENSC_WRAPPERS_TERMIOS 1
49 #define ENSC_WRAPPERS_FCNTL 1
53 int fd; /* terminal file descriptor */
54 struct termios term; /* terminal settings */
55 struct winsize ws; /* terminal size */
56 pid_t pid; /* terminal process id */
57 struct termios termo; /* original terminal settings */
58 enum { TS_RESET, TS_RAW } state; /* terminal state */
61 static struct terminal t;
62 extern int wrapper_exit_code;
64 /* set terminal to raw mode */
70 /* save original terminal settings */
71 Etcgetattr(STDIN_FILENO, &t.termo);
75 /* convert terminal settings to raw mode */
78 /* apply raw terminal settings */
79 Etcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);
84 /* reset terminal to original state */
88 if (t.state != TS_RAW)
91 Etcsetattr(STDIN_FILENO, TCSAFLUSH, &t.termo);
96 /* send signal to terminal */
98 terminal_kill(int sig)
102 /* try to get process group leader */
103 if (ioctl(t.fd, TIOCGPGRP, &pgrp) >= 0 &&
105 kill(-pgrp, sig) != -1)
108 /* fallback using terminal pid */
112 /* redraw the terminal screen */
114 terminal_redraw(void)
116 /* get winsize from stdin */
117 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &t.ws) == -1)
120 /* set winsize in terminal */
121 ioctl(t.fd, TIOCSWINSZ, &t.ws);
123 /* set winsize change signal to terminal */
124 terminal_kill(SIGWINCH);
127 /* copy terminal activities */
129 terminal_copy(int src, int dst)
134 /* read terminal activity */
135 len = read(src, buf, sizeof(buf));
136 if (len == -1 && errno != EINTR) {
138 terminal_kill(SIGTERM);
140 } else if (len == -1)
143 /* write activity to user */
144 EwriteAll(dst, buf, len);
149 /* shuffle all output, and reset the terminal */
157 options = Efcntl(t.fd, F_GETFL, 0) | O_NONBLOCK;
158 Efcntl(t.fd, F_SETFL, options);
160 len = read(t.fd, buf, sizeof(buf));
161 if (len == 0 || len == -1)
163 EwriteAll(STDOUT_FILENO, buf, len);
166 /* in case atexit hasn't been setup yet */
172 signal_handler(int sig)
177 /* catch interrupt */
186 exit(WEXITSTATUS(status));
189 /* window size has changed */
200 void do_vlogin(int UNUSED argc, char *argv[], int ind)
207 if (!isatty(0) || !isatty(1)) {
208 execvp(argv[ind], argv+ind);
212 /* set terminal to raw mode */
215 /* reset terminal to its original mode */
216 atexit(terminal_reset);
218 /* fork new pseudo terminal */
219 if (openpty(&t.fd, &slave, NULL, NULL, NULL) == -1) {
220 perror(ENSC_WRAPPERS_PREFIX "openpty()");
224 /* setup SIGCHLD here, so we're sure to get the signal */
225 signal(SIGCHLD, signal_handler);
230 /* we don't need the master side of the terminal */
233 /* login_tty() stupid dietlibc doesn't have it */
236 Eioctl(slave, TIOCSCTTY, NULL);
245 Eexecvp(argv[ind], argv+ind);
248 /* setup SIGINT and SIGWINCH here, as they can cause loops in the child */
249 signal(SIGWINCH, signal_handler);
250 signal(SIGINT, signal_handler);
252 /* save terminals pid */
255 /* set process title for ps */
256 prctl(PR_SET_NAME, (unsigned long) "login");
258 /* we want a redraw */
263 /* init file descriptors for select */
265 FD_SET(STDIN_FILENO, &rfds);
269 /* wait for something to happen */
270 while (select(n + 1, &rfds, NULL, NULL, NULL) == -1) {
271 if (errno == EINTR || errno == EAGAIN)
273 perror(ENSC_WRAPPERS_PREFIX "select()");
274 exit(wrapper_exit_code);
277 if (FD_ISSET(STDIN_FILENO, &rfds)) {
279 if (terminal_copy(STDIN_FILENO, t.fd) == 0) {
280 terminal_kill(SIGHUP);
285 if (FD_ISSET(t.fd, &rfds)) {
287 if (terminal_copy(t.fd, STDOUT_FILENO) == 0) {
288 terminal_kill(SIGHUP);
294 /* never get here, signal handler exits */