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>
42 #define ENSC_WRAPPERS_PREFIX "vlogin: "
43 #define ENSC_WRAPPERS_IOCTL 1
44 #define ENSC_WRAPPERS_UNISTD 1
45 #define ENSC_WRAPPERS_SOCKET 1
46 #define ENSC_WRAPPERS_IO 1
47 #define ENSC_WRAPPERS_TERMIOS 1
48 #define ENSC_WRAPPERS_FCNTL 1
52 int fd; /* terminal file descriptor */
53 struct termios term; /* terminal settings */
54 struct winsize ws; /* terminal size */
55 pid_t pid; /* terminal process id */
56 struct termios termo; /* original terminal settings */
57 enum { TS_RESET, TS_RAW } state; /* terminal state */
60 static struct terminal t;
61 extern int wrapper_exit_code;
63 /* set terminal to raw mode */
69 /* save original terminal settings */
70 Etcgetattr(STDIN_FILENO, &t.termo);
74 /* convert terminal settings to raw mode */
77 /* apply raw terminal settings */
78 Etcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);
83 /* reset terminal to original state */
87 if (t.state != TS_RAW)
90 Etcsetattr(STDIN_FILENO, TCSAFLUSH, &t.termo);
95 /* send signal to terminal */
97 terminal_kill(int sig)
101 /* try to get process group leader */
102 if (ioctl(t.fd, TIOCGPGRP, &pgrp) >= 0 &&
104 kill(-pgrp, sig) != -1)
107 /* fallback using terminal pid */
111 /* redraw the terminal screen */
113 terminal_redraw(void)
115 /* get winsize from stdin */
116 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &t.ws) == -1)
119 /* set winsize in terminal */
120 ioctl(t.fd, TIOCSWINSZ, &t.ws);
122 /* set winsize change signal to terminal */
123 terminal_kill(SIGWINCH);
126 /* copy terminal activities */
128 terminal_copy(int src, int dst)
133 /* read terminal activity */
134 len = read(src, buf, sizeof(buf));
135 if (len == -1 && errno != EINTR) {
137 terminal_kill(SIGTERM);
139 } else if (len == -1)
142 /* write activity to user */
143 EwriteAll(dst, buf, len);
146 /* shuffle all output, and reset the terminal */
154 options = Efcntl(t.fd, F_GETFL, 0) | O_NONBLOCK;
155 Efcntl(t.fd, F_SETFL, options);
157 len = read(t.fd, buf, sizeof(buf));
158 if (len == 0 || len == -1)
160 EwriteAll(STDOUT_FILENO, buf, len);
163 /* in case atexit hasn't been setup yet */
169 signal_handler(int sig)
174 /* catch interrupt */
183 exit(WEXITSTATUS(status));
186 /* window size has changed */
197 void do_vlogin(int argc, char *argv[], int ind)
204 if (!isatty(0) || !isatty(1)) {
205 execvp(argv[ind], argv+ind);
209 /* set terminal to raw mode */
212 /* reset terminal to its original mode */
213 atexit(terminal_reset);
215 /* fork new pseudo terminal */
216 if (openpty(&t.fd, &slave, NULL, NULL, NULL) == -1) {
217 perror(ENSC_WRAPPERS_PREFIX "openpty()");
221 /* setup SIGCHLD here, so we're sure to get the signal */
222 signal(SIGCHLD, signal_handler);
227 /* we don't need the master side of the terminal */
230 /* login_tty() stupid dietlibc doesn't have it */
233 Eioctl(slave, TIOCSCTTY, NULL);
242 Eexecvp(argv[ind], argv+ind);
245 /* setup SIGINT and SIGWINCH here, as they can cause loops in the child */
246 signal(SIGWINCH, signal_handler);
247 signal(SIGINT, signal_handler);
249 /* save terminals pid */
252 /* set process title for ps */
255 for (i = 0; i < argc; i++)
256 memset(argv[i], '\0', strlen(argv[i]));
258 strncpy(argv[0], "login", n);
260 /* we want a redraw */
265 /* init file descriptors for select */
267 FD_SET(STDIN_FILENO, &rfds);
271 /* wait for something to happen */
272 while (select(n + 1, &rfds, NULL, NULL, NULL) == -1) {
273 if (errno == EINTR || errno == EAGAIN)
275 perror(ENSC_WRAPPERS_PREFIX "select()");
276 exit(wrapper_exit_code);
279 if (FD_ISSET(STDIN_FILENO, &rfds))
280 terminal_copy(STDIN_FILENO, t.fd);
282 if (FD_ISSET(t.fd, &rfds))
283 terminal_copy(t.fd, STDOUT_FILENO);
286 /* never get here, signal handler exits */