/*
 * ush: ush.c
 *
 * Progetto di Laboratorio di Sistemi Operativi
 * 
 * Autore:              Antonio Ospite 408/244
 * 
 * Versione:    0.1
 * Data:        07/06/2001
 * 
 * Versione:    0.2
 * Data:        28/07/2002
 */


#include "ush.h"

static void sig_chld (int);

int
main (int argc, char *argv[])
{

  int myexit, len, len0, status;
  char line[MAXLINE], *cmdbuf, *arg0;
  pid_t pid;

  /*
   * modalità interattiva 
   */
  if (argc == 1)
  {
    myexit = 0;
    make_joblist (&mylist);

    /*
     * Inizializazzione della shell 
     */
    init_shell ();

    while (myexit != 1)
    {
      /*
       * Se siamo superutenti il prompt
       * ci verra' presentato con il
       * carattere '#', altrimenti con '$'
       */

      printf ("ush%c ", (getuid () == 0 ? '#' : '$'));

      /*
       * lettura comando 
       */
      fgets (line, MAXLINE, stdin);

      /*
       * lunghezza della riga di comando 
       */
      len = strlen (line);

      /*
       * Poichè fgets() non elimina
       * lo '\n' finale, ci pensiamo noi
       */
      line[len - 1] = 0;

      /*
       * Allocazione della stringa di comando
       */
      cmdbuf = malloc (len);
      strncpy (cmdbuf, line, len);

      /*
       * identificazione del primo argomento della riga di comando
       */
      len0 = 0;
      len0 = strcspn (cmdbuf, " ");

      arg0 = malloc (len0);
      strncpy (arg0, cmdbuf, len0);
      arg0[len0] = 0;

      /*
       * Esecuzione di un semplice
       * parsing della stringa di comando
       * con analisi del primo argomento della
       * riga di comando
       */


      /*
       * la riga di comando e' '\n' non si fa nulla 
       */
      if (len == 1);

      /*
       * cambio la working directory 
       */
      else if (strcmp (arg0, "cd") == 0)
      	chdir (cmdbuf + 3);

      else if (strcmp (arg0, "exit") == 0)
	      myexit = 1;

      else if (strcmp (arg0, "myjobs") == 0)
	      printlist (&mylist);

      else if (strcmp (arg0, "fg") == 0)
      {
	      pid = atoi (cmdbuf + 3);
	      put_job_in_fg (pid);
      }

      else if (strcmp (arg0, "back") == 0)
	      exec_bg_job (cmdbuf);


      /*
       * la riga di comando non contiene comandi interni alla shell 
       */
      else
      {
	      /*
	       * se la riga di comando e' una pipeline la si gestisce
	       * oppurtunamente 
	      */
	      if (strchr (cmdbuf, ',') != NULL)
	        execpipe (cmdbuf);

	/*
	 * la riga di comando non e' una pipeline 
	 */
	else
	  exec_fg_job (cmdbuf);

	free (arg0);
      }
    }
    exit (0);
  }
  /*
   * fine modalità interattiva 
   */


  /*
   * opzioni della shell
   */
  else if (argc > 1)
  {
    /*
     * Parsing delle opzioni
     */

    if (strcmp (argv[1], "--help") == 0 || strcmp (argv[1], "-h") == 0)
    {
      printf ("\noptions:\n");
      printf ("\t-h,   --help            this help\n");
      printf ("\t-v,   --version         version number\n");
    }


    else if (strcmp (argv[1], "--version") == 0
	     || strcmp (argv[1], "-v") == 0)
      printf ("%s version %s\n", *argv, VERSION);

    else
      printf ("Opzione sconosciuta.\n");


    exit (0);
  }
  /*
   *  fine opzioni 
   */

  exit (0);
}



void
static init_shell ()
{
  pid_t shell_pgid;

  /*
   * Ignoriamo i segnali che potrebbero terminare la shell  
   */
  signal (SIGINT, SIG_IGN);
  signal (SIGQUIT, SIG_IGN);
  signal (SIGTSTP, SIG_IGN);
  signal (SIGTTIN, SIG_IGN);
  signal (SIGTTOU, SIG_IGN);
  signal (SIGCHLD, sig_chld);

  /*
   * Creazione di un nuovo gruppo di processi  
   */
  shell_pgid = getpid ();
  if (setpgid (shell_pgid, shell_pgid) < 0)
    err_quit ("Impossibile creare un nuovo gruppo di processi");

  /*
   * Otteniamo il terminale di controllo 
   */
  tcsetpgrp (STDIN_FILENO, shell_pgid);

}

static void
exec_fg_job (char *cmdbuf)
{
  int status;
  pid_t pid;

  /*
   * Generazione di un processo figlio
   * con gli opportuni controlli di errore
   */
  if ((pid = fork ()) < 0)
    err_quit ("Errore di fork");

  if (pid == 0)
    launch_process (cmdbuf, getppid (), FOREGROUND);

  /*
   * Il genitore attende che il figlio
   * termini la sua esecuzione
   */
  if (pid > 0)
    add_job_to_list (&mylist, pid, cmdbuf, "running");

  if (waitpid (pid, &status, WUNTRACED) < 0)
    err_quit ("Errore di waitpid");

  if (WIFEXITED (status))
    rm_job_from_list (&mylist, pid);

  if (WIFSTOPPED (status))
  {
    printf ("\nProcesso con ID %d stoppato\n", pid);
    change_job_status (&mylist, pid, "stopped");
  }
  if (WIFSIGNALED (status))
  {
    rm_job_from_list (&mylist, pid);
    printf ("Processo con ID %d terminato per un segnale (%s)\n", pid,
	    sys_siglist[WTERMSIG (status)]);
  }

}

static void
exec_bg_job (char *cmdbuf)
{
  int status;
  pid_t pid;

  if ((pid = fork ()) < 0)
    err_quit ("Errore di fork");

  if (pid == 0)
    launch_process (cmdbuf + 5, 0, BACKGROUND);

  if (pid > 0)
    add_job_to_list (&mylist, pid, cmdbuf + 5, "running");

  if (waitpid (pid, &status, WNOHANG) < 0)
    err_quit ("Errore di waitpid");
}


static void
launch_process (char *command, pid_t pgid, int foreground)
{
  pid_t pid = getpid ();

  /*
   * Creazione di un nuovo gruppo di processi ed eventuale
   * assegnazione del terminale di controllo al nuovo gruppo di processi
   */

  if (pgid == 0)
    pgid = pid;
  setpgid (pid, pgid);
  if (foreground == 1)
    tcsetpgrp (STDIN_FILENO, pgid);

  /*
   * Si ripristina l'handling dei segnali 
   */
  signal (SIGINT, SIG_DFL);
  signal (SIGQUIT, SIG_DFL);
  signal (SIGTSTP, SIG_DFL);
  signal (SIGTTIN, SIG_DFL);
  signal (SIGTTOU, SIG_DFL);
  signal (SIGCHLD, SIG_DFL);


  /*
   * Esecuzione del nuovo processo  
   */
  execl ("/bin/sh", "sh", "-c", command, (char *) 0);

  /*
   * Il codice che segue viene eseguito solo nel caso in cui
   * la chiamata alla exec() fallisca
   */
  err_quit ("Errore di exec");
}


static void
put_job_in_fg (pid_t pid)
{

  int status;

  if (has_job_status (&mylist, pid, "stopped") == 0)
  {
    if (kill (pid, SIGCONT) < 0)
    {
      perror ("Errore !");
      return;
    }

    change_job_status (&mylist, pid, "running");

    if (waitpid (pid, &status, WUNTRACED) < 0)
      perror ("Errore di waitpid (fg)");

    if (WIFEXITED (status))
    {
      rm_job_from_list (&mylist, pid);
      printf ("Processo con  ID %d terminato con successo\n", pid);
    }
    if (WIFSTOPPED (status))
    {
      printf ("\nProcesso con ID %d stoppato\n", pid);
      change_job_status (&mylist, pid, "stopped");
    }
    if (WIFSIGNALED (status))
    {
      rm_job_from_list (&mylist, pid);
      printf ("Processo con ID %d terminato per un segnale (%s)\n", pid,
	      sys_siglist[WTERMSIG (status)]);
    }
  }
  else
    printf ("il processo con ID %d non e\' in stato di STOP.\n", pid);
}


static void
sig_chld (int signo)
{
  pid_t pid;
  int status;

  pid = waitpid (-1, &status, WNOHANG | WUNTRACED);


  if (pid > 0)
    if (WIFEXITED (status))
      rm_job_from_list (&mylist, pid);

    else if (WIFSTOPPED (status))
    {
      printf ("\nProcesso con ID %d stoppato\n", pid);
      change_job_status (&mylist, pid, "stopped");
    }
    else if (WIFSIGNALED (status))
    {
      rm_job_from_list (&mylist, pid);
      printf ("Processo con ID %d terminato per un segnale (%s)\n",
	      pid, sys_siglist[WTERMSIG (status)]);
    }

  return;
}


syntax highlighted by Code2HTML, v. 0.9.1