--- /dev/null
+// $Id$ --*- c -*--
+
+// Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+//
+// 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.
+
+#ifndef H_ENSC_IN_WRAPPERS_H
+# error wrappers-termios.hc can not be used in this way
+#endif
+
+inline static WRAPPER_DECL void
+Etcgetattr(int fd, struct termios *termios_p)
+{
+ FatalErrnoError(tcgetattr(fd, termios_p)==-1, "tcgetattr()");
+}
+
+inline static WRAPPER_DECL void
+Etcsetattr(int fd, int optional_actions, struct termios *termios_p)
+{
+ FatalErrnoError(tcsetattr(fd, optional_actions, termios_p)==-1, "tcsetattr()");
+}
#define CMD_NAMESPACE 0x400d
#define CMD_PERSTYPE 0x400e
#define CMD_PERSFLAG 0x400f
+#define CMD_VLOGIN 0x4010
struct option const
{ "syncmsg", required_argument, 0, CMD_SYNCMSG },
{ "personality-type", required_argument, 0, CMD_PERSTYPE },
{ "personality-flags", required_argument, 0, CMD_PERSFLAG },
+ { "vlogin", no_argument, 0, CMD_VLOGIN },
#if 1
{ "fakeinit", no_argument, 0, CMD_INITPID }, // compatibility
#endif
bool is_initpid;
bool is_silentexist;
bool set_namespace;
+ bool do_vlogin;
uint_least32_t personality_flags;
uint_least32_t personality_type;
int verbosity;
int wrapper_exit_code = 255;
+void do_vlogin(int argc, char *argv[], int ind);
+
static void
showHelp(int fd, char const *cmd, int res)
{
" --syncmsg <message>\n"
" ... use <message> as synchronization message; by\n"
" default, 'ok' will be used\n"
+ " --vlogin ... enable terminal proxy\n"
"\n"
"'vcontext --create' exits with code 254 iff the context exists already.\n"
"\n"
}
static inline ALWAYSINLINE int
-doit(struct Arguments const *args, char *argv[])
+doit(struct Arguments const *args, int argc, char *argv[])
{
int p[2][2];
pid_t pid = initSync(p, args->do_disconnect);
sys_personality(args->personality_type | args->personality_flags)==-1) {
perror(ENSC_WRAPPERS_PREFIX "personality()");
exit(wrapper_exit_code);
- }
+ }
doExternalSync(ext_sync_fd, args->sync_msg);
doSyncStage1(p, args->do_disconnect);
DPRINTF("doit: pid=%u, ppid=%u\n", getpid(), getppid());
- execvp (argv[optind],argv+optind);
+ if (!args->do_vlogin)
+ execvp (argv[optind],argv+optind);
+ else
+ do_vlogin(argc, argv, optind);
doSyncStage2(p, args->do_disconnect);
PERROR_Q(ENSC_WRAPPERS_PREFIX "execvp", argv[optind]);
.do_migrateself = false,
.do_disconnect = false,
.do_endsetup = false,
+ .do_vlogin = false,
.is_initpid = false,
.is_silentexist = false,
.set_namespace = false,
case CMD_MIGRATE : args.do_migrate = true; break;
case CMD_DISCONNECT : args.do_disconnect = true; break;
case CMD_ENDSETUP : args.do_endsetup = true; break;
+ case CMD_VLOGIN : args.do_vlogin = true; break;
case CMD_INITPID : args.is_initpid = true; break;
case CMD_CHROOT : args.do_chroot = true; break;
case CMD_NAMESPACE : args.set_namespace = true; break;
else if (optind>=argc)
WRITE_MSG(2, "No command given; use '--help' for more information.\n");
else
- return doit(&args, argv);
+ return doit(&args, argc, argv);
return wrapper_exit_code;
}
--- /dev/null
+// $Id$
+
+// 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();
+
+ /* 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);
+
+ /* reset terminal to its original mode */
+ atexit(terminal_reset);
+
+ /* 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 */
+}