Run our TTYs with our own tty runner
[grml-scripts.git] / compile / grml-runtty.c
diff --git a/compile/grml-runtty.c b/compile/grml-runtty.c
new file mode 100644 (file)
index 0000000..56dcd52
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2011 Christian Hofstaedtler. Licensed under GPL Version 2.
+ *
+ * Most of this is copied from fgetty, which is
+ *   Copyright Felix von Leitner, licensed under GPL Version 2.
+*/
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <signal.h>
+
+char* tty;
+char* prog;
+char* user;
+
+void error(const char* message, int exitcode) {
+  write(2, "E: ", 3);
+  write(2, message, strlen(message));
+  write(2, "\n", 1);
+  syslog(LOG_CRIT, "%s", message);
+  sleep(15);
+  closelog();
+  exit(exitcode);
+}
+
+void sigquit_handler(int signum) {
+  error("SIGQUIT received\n",23);
+}
+
+void setup_tty() {
+  struct sigaction sa;
+  int fd;
+  if (chown(tty,0,0) || chmod(tty,0600))
+    error("could not chown/chmod tty device\n",1);
+  sa.sa_handler=SIG_IGN;
+  sa.sa_flags=0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(SIGHUP,&sa,NULL);
+  sa.sa_handler=sigquit_handler;
+  sigaction(SIGQUIT,&sa,NULL);
+
+  setsid();
+  if ((fd=open(tty, O_RDWR, 0))<0)
+    error("open tty failed", errno);
+  if (ioctl (fd, TIOCSCTTY, (void *)1) == -1)
+    error("ioctl TIOCSCTTY failed", errno);
+  if (!isatty(fd))
+    error("\"not a typewriter\" (isatty failed)", errno);
+  if (vhangup())       /* linux specific */
+    error("vhangup failed", errno);
+  close(2); close(1); close(0); close(fd);
+  if (open(tty, O_RDWR,0) != 0)
+    error("could not (re)open tty", errno);
+  if (dup(0) != 1 || dup(0) != 2)
+    error("could not dup stdout and stderr", errno);
+
+  sa.sa_handler=SIG_DFL;
+  sa.sa_flags=0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(SIGHUP,&sa,NULL);
+}
+
+void setenvvar(char* envvar, char* value) {
+  /* buf ends up directly in env. can't use alloca */
+  char* buf = malloc(strlen(envvar) + strlen(value) + 2);
+  strcpy(buf, envvar);
+  strcat(buf, "=");
+  strcat(buf, value);
+  putenv(buf);
+}
+
+int main(int argc, char* argv[]) {
+  struct passwd* pw;
+
+  tty = argv[1];
+  prog = argv[2];
+  user = argv[3];
+
+  openlog("startprog", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+
+  if (tty == NULL || prog == NULL || user == NULL)
+    error("Usage: startprog /dev/ttyX /bin/bash user", 1);
+
+  pw = getpwnam(user);
+  if (!pw)
+    error("User does not exist", 1);
+
+  clearenv();
+  putenv("TERM=linux");
+  setenvvar("TTY", tty);
+  setenvvar("USER", pw->pw_name);
+  setenvvar("LOGNAME", pw->pw_name);
+  setenvvar("SHELL", pw->pw_shell);
+  setenvvar("HOME", pw->pw_dir);
+  {
+    char uidbuf[15];
+    sprintf(uidbuf, "%i", pw->pw_uid);
+    setenvvar("UID", uidbuf);
+  }
+
+#ifndef DEBUG
+  setup_tty();
+#endif
+  chown(tty, pw->pw_uid, pw->pw_gid);
+
+  if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+    error("initgroups failed", 1);
+  if (setgid(pw->pw_gid) == -1)
+    error("setgid failed", 1);
+  if (setuid(pw->pw_uid) == -1)
+    error("setuid failed", 1);
+
+  {
+    char *Argv[] = {prog, 0};
+    execve(prog, Argv, environ);
+  }
+
+  /* should never come here */
+  error("Program startup failed", 99);
+}