$P exec rm -rf /.old &
service -U $target
-# Spawn gettys for tty[23456]
-getty-start:VEPservice -u: hostname-start utmp-start
+# Spawn gettyd for tty[23456]
+gettyd-start:VEPservice -u: hostname-start utmp-start
+ $P exec gettyd tty2 tty3 tty4 tty5 tty6 &
+ service -U $target
+gettyd-stop_cmd=fuser -k /dev/tty2 /dev/tty3 /dev/tty4 /dev/tty5 /dev/tty6
+
+# Spawn agettys for tty[23456]
+agetty-start:VEPservice -u: hostname-start utmp-start
$P respawn setsid agetty 38400 tty2 linux
$P respawn setsid agetty 38400 tty3 linux
$P respawn setsid agetty 38400 tty4 linux
$P respawn setsid agetty 38400 tty5 linux
$P respawn setsid agetty 38400 tty6 linux
service -U $target
-getty-stop_cmd=fuser -k /dev/tty2 /dev/tty3 /dev/tty4 /dev/tty5 /dev/tty6
+agetty-stop_cmd=fuser -k /dev/tty2 /dev/tty3 /dev/tty4 /dev/tty5 /dev/tty6
# Spawn qingys for tty[7]
qingy-start:VEPservice -u: hostname-start utmp-start modules-start
--- /dev/null
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+
+/* TTY data */
+typedef struct tty_t {
+ char *path;
+ int fd;
+ int pid;
+ struct tty_t *next;
+} tty_t;
+
+/* Local Data */
+static int running;
+static int epoll;
+static int sigs;
+static tty_t *ttys;
+
+/* Helper functions */
+void error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "Error ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(errno));
+ va_end(ap);
+ if (!running)
+ exit(1);
+}
+
+int add_poll(int fd, void *ptr)
+{
+ struct epoll_event ctl = {
+ .events = EPOLLIN,
+ .data.ptr = ptr,
+ };
+ return epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &ctl);
+}
+
+int del_poll(int fd)
+{
+ return epoll_ctl(epoll, EPOLL_CTL_DEL, fd, NULL);
+}
+
+static int start_tty(tty_t *tty)
+{
+ const char *prompt = "[Press enter to login]";
+ struct termios attr;
+
+ tty->fd = open(tty->path, O_RDWR|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, 0);
+ if (tty->fd < 0)
+ return tty->fd;
+ tcgetattr(tty->fd, &attr);
+ attr.c_lflag &= ~ECHO;
+ tcsetattr(tty->fd, TCSANOW, &attr);
+ write(tty->fd, "\033c", 2);
+ write(tty->fd, "\033[?1c", 5);
+ write(tty->fd, prompt, strlen(prompt));
+ return add_poll(tty->fd, tty);
+}
+
+static void read_tty(tty_t *tty)
+{
+ int flags;
+ char ch, login;
+ while (read(tty->fd, &ch, 1) == 1)
+ if (ch == '\n' || ch == '\r')
+ login = 1;
+ if (!login)
+ return;
+ if ((tty->pid = fork()) < 0)
+ return;
+ if (tty->pid == 0) {
+ if (putenv("TERM=linux"))
+ error("setting environment");
+ if (setsid() < 0)
+ error("setting sid");
+ if (write(tty->fd, "\033c", 2) < 0)
+ error("resetting tty");
+ if ((flags = fcntl(tty->fd, F_GETFL)) < 0)
+ error("getting fd flags");
+ if (fcntl(tty->fd, F_SETFL, flags&~O_NONBLOCK) < 0)
+ error("setting blocking flags");
+ if (ioctl(tty->fd, TIOCSCTTY, 0) < 0)
+ error("setting ctty");
+ if (dup2(tty->fd, 0) != 0)
+ error("setting stdin");
+ if (dup2(tty->fd, 1) != 1)
+ error("setting stdout");
+ if (dup2(tty->fd, 2) != 2)
+ error("setting stderr");
+ if (execl("/bin/login", "login", NULL) < 0)
+ error("execing login program");
+ }
+ del_poll(tty->fd);
+ close(tty->fd);
+}
+
+void on_child(void)
+{
+ struct signalfd_siginfo info;
+ if (read(sigs, &info, sizeof(info)) != sizeof(info))
+ return;
+ if (info.ssi_signo != SIGCHLD)
+ return;
+
+ int status;
+ pid_t pid;
+ tty_t *tty;
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ for (tty = ttys; tty; tty = tty->next) {
+ if (pid == tty->pid) {
+ tty->pid = 0;
+ start_tty(tty);
+ }
+ }
+ }
+}
+
+/* Main */
+int main(int argc, char **argv)
+{
+ int i, count;
+ tty_t *tty;
+ sigset_t mask;
+
+ /* Check arguments */
+ if (argc <= 1) {
+ printf("usage: gettyd <tty> ...\n");
+ return 0;
+ }
+
+ /* Setup */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGHUP);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
+ error("blocking signals");
+ if ((sigs = signalfd(-1, &mask, SFD_CLOEXEC)) < 0)
+ error("creating signal fd");
+ if ((epoll = epoll_create1(EPOLL_CLOEXEC)) < 0)
+ error("creating epoll");
+ if (add_poll(sigs, &sigs) < 0)
+ error("adding signal epoll");
+
+ /* Open TTYs */
+ for (i = 1; i < argc; i++) {
+ if (!(tty = malloc(sizeof(tty_t))))
+ error("allocating memory");
+ if (asprintf(&tty->path, "/dev/%s", argv[i]) < 0)
+ error("allocating path name");
+ if (start_tty(tty) < 0)
+ error("starting tty '%s'", tty->path);
+ tty->next = ttys;
+ ttys = tty;
+ }
+
+ /* Main loop */
+ running = 1;
+ while (1) {
+ struct epoll_event event;
+ errno = 0;
+ count = epoll_wait(epoll, &event, 1, -1);
+ if (errno == EINTR)
+ continue;
+ if (count < 0)
+ continue;
+ if (event.data.ptr == &sigs)
+ on_child();
+ else
+ read_tty(event.data.ptr);
+ }
+
+ return 0;
+}