#! /bin/sh /usr/share/dpatch/dpatch-run
## 11_vlogin.dpatch by Micah Anderson <Micah Anderson <micah@debian.org>>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: No description.

@DPATCH@
diff -urNad trunk~/Makefile.in trunk/Makefile.in
--- trunk~/Makefile.in	2006-03-20 08:16:49.000000000 -0500
+++ trunk/Makefile.in	2006-04-07 13:34:02.000000000 -0400
@@ -965,7 +965,7 @@
 am_src_vattribute_OBJECTS = src/vattribute.$(OBJEXT)
 src_vattribute_OBJECTS = $(am_src_vattribute_OBJECTS)
 src_vattribute_DEPENDENCIES = $(am__DEPENDENCIES_11)
-am_src_vcontext_OBJECTS = src/vcontext.$(OBJEXT)
+am_src_vcontext_OBJECTS = src/vcontext.$(OBJEXT) src/vlogin.$(OBJEXT)
 src_vcontext_OBJECTS = $(am_src_vcontext_OBJECTS)
 src_vcontext_DEPENDENCIES = $(am__DEPENDENCIES_11) \
 	$(am__DEPENDENCIES_9)
@@ -1746,7 +1746,7 @@
 src_chcontext_compat_SOURCES = src/chcontext.c
 src_chcontext_compat_LDADD = $(VSERVER_LDADDS) $(LIBINTERNAL)
 src_chcontext_compat_LDFLAGS = $(VSERVER_LDFLGS)
-src_vcontext_SOURCES = src/vcontext.c
+src_vcontext_SOURCES = src/vcontext.c src/vlogin.c
 src_vcontext_LDADD = $(VSERVER_LDADDS) $(LIBINTERNAL)
 src_vcontext_LDFLAGS = $(VSERVER_LDFLGS)
 src_vsched_SOURCES = src/vsched.c
@@ -3489,6 +3489,8 @@
 	$(LINK) $(src_vattribute_LDFLAGS) $(src_vattribute_OBJECTS) $(src_vattribute_LDADD) $(LIBS)
 src/vcontext.$(OBJEXT): src/$(am__dirstamp) \
 	src/$(DEPDIR)/$(am__dirstamp)
+src/vlogin.$(OBJEXT): src/$(am__dirstamp) \
+	src/$(DEPDIR)/$(am__dirstamp)
 src/vcontext$(EXEEXT): $(src_vcontext_OBJECTS) $(src_vcontext_DEPENDENCIES) src/$(am__dirstamp)
 	@rm -f src/vcontext$(EXEEXT)
 	$(LINK) $(src_vcontext_LDFLAGS) $(src_vcontext_OBJECTS) $(src_vcontext_LDADD) $(LIBS)
@@ -4222,6 +4224,7 @@
 	-rm -f src/vdu.$(OBJEXT)
 	-rm -f src/vhashify.$(OBJEXT)
 	-rm -f src/vlimit.$(OBJEXT)
+	-rm -f src/vlogin.$(OBJEXT)
 	-rm -f src/vnamespace.$(OBJEXT)
 	-rm -f src/vps.$(OBJEXT)
 	-rm -f src/vreboot.$(OBJEXT)
@@ -4593,6 +4596,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vdu.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vhashify.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vlimit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vlogin.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vnamespace.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vps.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/vreboot.Po@am__quote@
diff -urNad trunk~/ensc_wrappers/wrappers-termios.hc trunk/ensc_wrappers/wrappers-termios.hc
--- trunk~/ensc_wrappers/wrappers-termios.hc	1969-12-31 19:00:00.000000000 -0500
+++ trunk/ensc_wrappers/wrappers-termios.hc	2006-04-07 13:34:02.000000000 -0400
@@ -0,0 +1,32 @@
+// $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()");
+}
diff -urNad trunk~/ensc_wrappers/wrappers.h trunk/ensc_wrappers/wrappers.h
--- trunk~/ensc_wrappers/wrappers.h	2006-03-20 08:16:47.000000000 -0500
+++ trunk/ensc_wrappers/wrappers.h	2006-04-07 13:34:02.000000000 -0400
@@ -111,6 +111,10 @@
 #  include "wrappers-stat.hc"
 #endif
 
+#ifdef ENSC_WRAPPERS_TERMIOS
+#  include "wrappers-termios.hc"
+#endif
+
 #undef ENSC_DETAIL2
 #undef ENSC_DETAIL1
 #undef ENSC_DOQUOTE_COND
diff -urNad trunk~/scripts/vserver.functions trunk/scripts/vserver.functions
--- trunk~/scripts/vserver.functions	2006-03-20 08:16:45.000000000 -0500
+++ trunk/scripts/vserver.functions	2006-04-07 13:34:02.000000000 -0400
@@ -39,7 +39,7 @@
 
 declare -a OPTS_VCONTEXT_CREATE=()
 declare -a OPTS_VCONTEXT_MIGRATE=()
-declare -a OPTS_VCONTEXT_ENTER=()
+declare -a OPTS_VCONTEXT_ENTER=( --vlogin )
 declare -a OPTS_VATTRIBUTE=( --flag fakeinit )
 declare -a OPTS_VSCHED=()
 
diff -urNad trunk~/src/Makefile-files trunk/src/Makefile-files
--- trunk~/src/Makefile-files	2006-03-20 08:16:45.000000000 -0500
+++ trunk/src/Makefile-files	2006-04-07 13:34:02.000000000 -0400
@@ -167,7 +167,7 @@
 src_chcontext_compat_LDADD =	$(VSERVER_LDADDS) $(LIBINTERNAL)
 src_chcontext_compat_LDFLAGS =	$(VSERVER_LDFLGS)
 
-src_vcontext_SOURCES		=  src/vcontext.c
+src_vcontext_SOURCES		=  src/vcontext.c src/vlogin.c
 src_vcontext_LDADD		=  $(VSERVER_LDADDS) $(LIBINTERNAL)
 src_vcontext_LDFLAGS		=  $(VSERVER_LDFLGS)
 
diff -urNad trunk~/src/vcontext.c trunk/src/vcontext.c
--- trunk~/src/vcontext.c	2006-03-20 08:16:45.000000000 -0500
+++ trunk/src/vcontext.c	2006-04-07 13:34:02.000000000 -0400
@@ -62,6 +62,7 @@
 #define CMD_NAMESPACE		0x400d
 #define CMD_PERSTYPE		0x400e
 #define CMD_PERSFLAG		0x400f
+#define CMD_VLOGIN		0x4010
 
 
 struct option const
@@ -85,6 +86,7 @@
   { "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  
@@ -100,6 +102,7 @@
     bool		is_initpid;
     bool		is_silentexist;
     bool		set_namespace;
+    bool		do_vlogin;
     uint_least32_t	personality_flags;
     uint_least32_t	personality_type;
     int			verbosity;
@@ -112,6 +115,8 @@
 
 int		wrapper_exit_code = 255;
 
+void do_vlogin(int argc, char *argv[], int ind);
+
 static void
 showHelp(int fd, char const *cmd, int res)
 {
@@ -145,6 +150,7 @@
 	    "    --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"
@@ -233,7 +239,7 @@
 }
 
 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);
@@ -287,12 +293,15 @@
 	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]);
@@ -341,6 +350,7 @@
     .do_migrateself    = false,
     .do_disconnect     = false,
     .do_endsetup       = false,
+    .do_vlogin         = false,
     .is_initpid        = false,
     .is_silentexist    = false,
     .set_namespace     = false,
@@ -363,6 +373,7 @@
       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;
@@ -408,7 +419,7 @@
   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;
 }
diff -urNad trunk~/src/vlogin.c trunk/src/vlogin.c
--- trunk~/src/vlogin.c	1969-12-31 19:00:00.000000000 -0500
+++ trunk/src/vlogin.c	2006-04-07 13:34:02.000000000 -0400
@@ -0,0 +1,281 @@
+// $Id$
+
+// Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+// 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 <strings.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];
+  size_t len;
+
+  /* read terminal activity */
+  len = Eread(src, buf, sizeof(buf));
+
+  /* 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++)
+    bzero(argv[i], 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 */
+}
