#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/vt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdarg.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <string.h>
#include <sys/kd.h>


enum{ERROR=-1,OK};

/* Maximale #zeichen für die Eingabe */
#define MAX_PASS_LEN 8
#define PASSWORT "pinguine"


/*Zum Abfragen eines Passwortes*/
char *getpass(const char *prompt)
{/* +1 für das 0-Byte am Ende zum Terminieren */
  static char buf[MAX_PASS_LEN + 1];
  char *ptr;
  sigset_t sig, sigsave;
  struct termios term, termsave;
  FILE *fp;
  int c;

  if ( (fp = fopen(ctermid(NULL), "r+")) == NULL)
       return(NULL);
  setbuf(fp, NULL);    //wir schalten die Pufferung aus

  sigemptyset(&sig);      /* Entfernen der Signale aus Signalmenge */
  sigaddset(&sig, SIGINT);   /*fügen SIGINT dazu */
  sigaddset(&sig, SIGTSTP);   /*fügen SIGSTP dazu */
/*blockieren von SIGINT und SIGSTP und sichern der Alten Signalmenge in sigsave*/
  sigprocmask(SIG_BLOCK, &sig, &sigsave);
  tcgetattr(fileno(fp), &termsave);/* Sichern des aktuellen Terminals in termsave */
  term = termsave;    /* copy */

/*Löschen folgender lokalen Flags: ECHO : Ausgabe */
/*   ECHOE: Gelöschte Zeichen mit Leerzeichen überschreiben */
/*   ECHOK: Zeichen wirklich löschen  */
/*   ECHONL: Ausgabe von Newline */

  term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);

/*Wir setzen unsere neuen Attribute*/
  tcsetattr(fileno(fp), TCSAFLUSH, &term);

/*Gibt "Passwort : " aus*/
  fputs(prompt, fp);

/*Passwort wird Zeichenweise an die Adresse von ptr geschrieben*/
  ptr = buf;
  while ( (c = getc(fp)) != EOF && c != '\n')
    {
       if (ptr < &buf[MAX_PASS_LEN])
           *ptr++ = c;
    }
  ptr = '\0';      /* null terminate */


  putc('\n', fp);      /* echo  newline */

/* Wir stellen unser altes Terminal wieder her*/
  tcsetattr(fileno(fp), TCSAFLUSH, &termsave);

/* Wir stellen die alte Signalmaske wieder her */
  sigprocmask(SIG_SETMASK, &sigsave, NULL);

  fclose(fp);
  return(buf);
}

void fehler_exit(char *error_msg,...)
{
  va_list az;
  va_start(az,error_msg);
  vprintf(error_msg,az);
  va_end(az);
  exit(0);
}


int main()
{
  int vterm_nr, vterm_fd;
  int status;
  struct vt_stat vterm_stat;
  char terminal[100],*passwort;
  pid_t kindprozess;


/*Zuerst öffnen wir die Gerätedatei /dev/tty da wir für ioctl */
/*   ein Filedeskriptor benötigen */
  if((vterm_fd = open("/dev/tty", O_RDWR, 0)) == ERROR)
        fehler_exit("Kann /dev/tty nicht öffnen.....\n");

/*Jetzt prüfen wir ob tty eine virtuelle Konsole ist*/
  if(ioctl(vterm_fd, VT_GETSTATE, &vterm_stat) == ERROR)
       fehler_exit("tty ist keine virtuelle Konsole..\n");

/*Jetzt suchen wir uns eine freie virtuelle Konsole*/
  if(ioctl(vterm_fd, VT_OPENQRY, &vterm_nr) == ERROR || vterm_nr == ERROR)
        fehler_exit("Keine freie konsole vorhanden...\n");

  sprintf(terminal,"/dev/tty%d",vterm_nr);
  printf("Versuche neues Terminal %s zu öffnen........\n",terminal);

/*Jetzt wollen wir überprüfen ob wir auf unserem neuen virt. Term */
/* Lesen und Schreiben dürfen*/
  if(access(terminal, (W_OK|R_OK)) == ERROR)
          fehler_exit("Unzureichend Rechte auf das Terminal......\n");

/*Wir fragen ein Passwort ab -> "pinguine" */
  if(strcmp(passwort=getpass("Passwort : "),PASSWORT)!=0)
         fehler_exit("Falsches Passwort......\n");

/*Jetzt erzeugen wir einen neuen Prozess für unser neues Terminal*/
  if((kindprozess=fork()) == OK)
     {
/*Wir aktivieren die neuen Konsole warten bis sie wirklich aktiviert wird*/
        ioctl(vterm_fd, VT_ACTIVATE, vterm_nr);
        ioctl(vterm_fd, VT_WAITACTIVE, vterm_nr);

/*Wir richten eine neue Session ein*/
        setsid();

/*Wir schließen stdin,stdout und stderr und vterm_fd da wir ja eine neuen */
/*virtuelle Konsole starten wollen und nicht mit dem ganzen vererbten */
/* Eigenschaften des Elternprozesses*/
       close(0);  close(1);  close(2);  close(vterm_fd);

/*Jetzt können wir in unserem Kindprozess die Konsole öffen*/
       vterm_fd = open(terminal, O_RDWR,0);

       dup(vterm_fd);  dup(vterm_fd);

       ioctl(vterm_fd, KIOCSOUND, 1000);   /*beep an*/
       sleep(1);   /*1 sekunde beep*/
       ioctl(vterm_fd, KIOCSOUND, 0);   /*beep aus*/
/*Ausführen von unserer neuen virtuellen Konsole*/

       system("setterm -foreground black -background green");
       printf("...........................Virtuelle Konsolle (tty%d)............................\n\n\n",vterm_nr);
       system("setterm -foreground white -background black");
       execlp("/bin/bash", "bash", NULL);
    }     //Ende Kindprozess

  wait(NULL);   //Elternprozess wartet auf den Kindprozess

/*Alte Konsole wieder aktivieren und Speicher der virtuellen Konsole wieder freigeben*/
  ioctl(vterm_fd, VT_ACTIVATE, vterm_stat.v_active);
  ioctl(vterm_fd, VT_WAITACTIVE, vterm_stat.v_active);
  ioctl(vterm_fd, VT_DISALLOCATE, vterm_nr);

  return 0;
}
