1 // $Id: vlogin.c 2525 2007-04-08 00:40:16Z dhozac $
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);
148 /* shuffle all output, and reset the terminal */
156 options = Efcntl(t.fd, F_GETFL, 0) | O_NONBLOCK;
157 Efcntl(t.fd, F_SETFL, options);
159 len = read(t.fd, buf, sizeof(buf));
160 if (len == 0 || len == -1)
162 EwriteAll(STDOUT_FILENO, buf, len);
165 /* in case atexit hasn't been setup yet */
171 signal_handler(int sig)
176 /* catch interrupt */
185 exit(WEXITSTATUS(status));
188 /* window size has changed */
199 void do_vlogin(int argc, char *argv[], int ind)
206 if (!isatty(0) || !isatty(1)) {
207 execvp(argv[ind], argv+ind);
211 /* set terminal to raw mode */
214 /* reset terminal to its original mode */
215 atexit(terminal_reset);
217 /* fork new pseudo terminal */
218 if (openpty(&t.fd, &slave, NULL, NULL, NULL) == -1) {
219 perror(ENSC_WRAPPERS_PREFIX "openpty()");
223 /* setup SIGCHLD here, so we're sure to get the signal */
224 signal(SIGCHLD, signal_handler);
229 /* we don't need the master side of the terminal */
232 /* login_tty() stupid dietlibc doesn't have it */
235 Eioctl(slave, TIOCSCTTY, NULL);
244 Eexecvp(argv[ind], argv+ind);
247 /* setup SIGINT and SIGWINCH here, as they can cause loops in the child */
248 signal(SIGWINCH, signal_handler);
249 signal(SIGINT, signal_handler);
251 /* save terminals pid */
254 /* set process title for ps */
257 for (i = 0; i < argc; i++)
258 memset(argv[i], '\0', strlen(argv[i]));
260 strncpy(argv[0], "login", n);
262 /* we want a redraw */
267 /* init file descriptors for select */
269 FD_SET(STDIN_FILENO, &rfds);
273 /* wait for something to happen */
274 while (select(n + 1, &rfds, NULL, NULL, NULL) == -1) {
275 if (errno == EINTR || errno == EAGAIN)
277 perror(ENSC_WRAPPERS_PREFIX "select()");
278 exit(wrapper_exit_code);
281 if (FD_ISSET(STDIN_FILENO, &rfds)) {
283 if (terminal_copy(STDIN_FILENO, t.fd) == 0) {
284 terminal_kill(SIGHUP);
289 if (FD_ISSET(t.fd, &rfds)) {
291 if (terminal_copy(t.fd, STDOUT_FILENO) == 0) {
292 terminal_kill(SIGHUP);
298 /* never get here, signal handler exits */