+++ /dev/null
-// $Id: vlogin.c 2325 2006-09-21 19:42:31Z dhozac $
-
-// Copyright (C) 2006 Benedikt Böhm <hollow@gentoo.org>
-// Based on vserver-utils' vlogin program.
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; version 2 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "util.h"
-#include <lib/vserver.h>
-#include <lib/fmt.h>
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <termios.h>
-#include <signal.h>
-#include <pty.h>
-#include <fcntl.h>
-
-#define ENSC_WRAPPERS_PREFIX "vlogin: "
-#define ENSC_WRAPPERS_IOCTL 1
-#define ENSC_WRAPPERS_UNISTD 1
-#define ENSC_WRAPPERS_SOCKET 1
-#define ENSC_WRAPPERS_IO 1
-#define ENSC_WRAPPERS_TERMIOS 1
-#define ENSC_WRAPPERS_FCNTL 1
-#include <wrappers.h>
-
-struct terminal {
- int fd; /* terminal file descriptor */
- struct termios term; /* terminal settings */
- struct winsize ws; /* terminal size */
- pid_t pid; /* terminal process id */
- struct termios termo; /* original terminal settings */
- enum { TS_RESET, TS_RAW } state; /* terminal state */
-};
-
-static struct terminal t;
-extern int wrapper_exit_code;
-
-/* set terminal to raw mode */
-static void
-terminal_raw(void)
-{
- struct termios buf;
-
- /* save original terminal settings */
- Etcgetattr(STDIN_FILENO, &t.termo);
-
- buf = t.termo;
-
- /* convert terminal settings to raw mode */
- cfmakeraw(&buf);
-
- /* apply raw terminal settings */
- Etcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);
-
- t.state = TS_RAW;
-}
-
-/* reset terminal to original state */
-static void
-terminal_reset(void)
-{
- if (t.state != TS_RAW)
- return;
-
- Etcsetattr(STDIN_FILENO, TCSAFLUSH, &t.termo);
-
- t.state = TS_RESET;
-}
-
-/* send signal to terminal */
-static void
-terminal_kill(int sig)
-{
- pid_t pgrp = -1;
-
- /* try to get process group leader */
- if (ioctl(t.fd, TIOCGPGRP, &pgrp) >= 0 &&
- pgrp != -1 &&
- kill(-pgrp, sig) != -1)
- return;
-
- /* fallback using terminal pid */
- kill(-t.pid, sig);
-}
-
-/* redraw the terminal screen */
-static void
-terminal_redraw(void)
-{
- /* get winsize from stdin */
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &t.ws) == -1)
- return;
-
- /* set winsize in terminal */
- ioctl(t.fd, TIOCSWINSZ, &t.ws);
-
- /* set winsize change signal to terminal */
- terminal_kill(SIGWINCH);
-}
-
-/* copy terminal activities */
-static void
-terminal_copy(int src, int dst)
-{
- char buf[64];
- ssize_t len;
-
- /* read terminal activity */
- len = read(src, buf, sizeof(buf));
- if (len == -1 && errno != EINTR) {
- perror("read()");
- terminal_kill(SIGTERM);
- exit(1);
- } else if (len == -1)
- return;
-
- /* write activity to user */
- EwriteAll(dst, buf, len);
-}
-
-/* shuffle all output, and reset the terminal */
-static void
-terminal_end(void)
-{
- char buf[64];
- ssize_t len;
- long options;
-
- options = Efcntl(t.fd, F_GETFL, 0) | O_NONBLOCK;
- Efcntl(t.fd, F_SETFL, options);
- for (;;) {
- len = read(t.fd, buf, sizeof(buf));
- if (len == 0 || len == -1)
- break;
- EwriteAll(STDOUT_FILENO, buf, len);
- }
-
- /* in case atexit hasn't been setup yet */
- terminal_reset();
-}
-
-/* catch signals */
-static void
-signal_handler(int sig)
-{
- int status;
-
- switch(sig) {
- /* catch interrupt */
- case SIGINT:
- terminal_kill(sig);
- break;
-
- /* terminal died */
- case SIGCHLD:
- terminal_end();
- wait(&status);
- exit(WEXITSTATUS(status));
- break;
-
- /* window size has changed */
- case SIGWINCH:
- terminal_redraw();
- break;
-
- default:
- exit(0);
- }
-
-}
-
-void do_vlogin(int argc, char *argv[], int ind)
-{
- int slave;
- pid_t pid;
- int n, i;
- fd_set rfds;
-
- if (!isatty(0) || !isatty(1)) {
- execvp(argv[ind], argv+ind);
- return;
- }
-
- /* set terminal to raw mode */
- terminal_raw();
-
- /* reset terminal to its original mode */
- atexit(terminal_reset);
-
- /* fork new pseudo terminal */
- if (openpty(&t.fd, &slave, NULL, NULL, NULL) == -1) {
- perror(ENSC_WRAPPERS_PREFIX "openpty()");
- exit(EXIT_FAILURE);
- }
-
- /* setup SIGCHLD here, so we're sure to get the signal */
- signal(SIGCHLD, signal_handler);
-
- pid = Efork();
-
- if (pid == 0) {
- /* we don't need the master side of the terminal */
- close(t.fd);
-
- /* login_tty() stupid dietlibc doesn't have it */
- Esetsid();
-
- Eioctl(slave, TIOCSCTTY, NULL);
-
- Edup2(slave, 0);
- Edup2(slave, 1);
- Edup2(slave, 2);
-
- if (slave > 2)
- close(slave);
-
- Eexecvp(argv[ind], argv+ind);
- }
-
- /* setup SIGINT and SIGWINCH here, as they can cause loops in the child */
- signal(SIGWINCH, signal_handler);
- signal(SIGINT, signal_handler);
-
- /* save terminals pid */
- t.pid = pid;
-
- /* set process title for ps */
- n = strlen(argv[0]);
-
- for (i = 0; i < argc; i++)
- memset(argv[i], '\0', strlen(argv[i]));
-
- strncpy(argv[0], "login", n);
-
- /* we want a redraw */
- terminal_redraw();
-
- /* main loop */
- for (;;) {
- /* init file descriptors for select */
- FD_ZERO(&rfds);
- FD_SET(STDIN_FILENO, &rfds);
- FD_SET(t.fd, &rfds);
- n = t.fd;
-
- /* wait for something to happen */
- while (select(n + 1, &rfds, NULL, NULL, NULL) == -1) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- perror(ENSC_WRAPPERS_PREFIX "select()");
- exit(wrapper_exit_code);
- }
-
- if (FD_ISSET(STDIN_FILENO, &rfds))
- terminal_copy(STDIN_FILENO, t.fd);
-
- if (FD_ISSET(t.fd, &rfds))
- terminal_copy(t.fd, STDOUT_FILENO);
- }
-
- /* never get here, signal handler exits */
-}