Le Coin Wiki
d'Olivier Dalle
$WikiTagline
 

L’objectif du projet cette année est la réalisation d’un mini-shell.

Vous devrez écrire un programme python utilisant exclusivement les primitives vues en cours ou données en TD/TP. Il est notamment interdit d’utiliser les primitives python qui n’ont pas ete vues telles que popen ou system.

Imperatif (pour avoir la moyenne)

Pour obtenir la moyenne, votre shell doit faire les choses suivantes sans erreur:

  1. Attendre la saisie d’une commande
  2. Analyser la ligne de commande saisie (en affichant eventuellement une erreur si la ligne est incorrecte)
  3. lancer l’execution de la ou des commandes
  4. Attendre la fin de l’execution
  5. Retour a l’étape 1

La complexité des commandes attendue pour avoir la moyenne est la suivante:

  • Enchainement de tubes (exemple : ls -al | sort -k1 | tr ‘A-Z’ ‘a-z’ | more)
  • Redirection des entrées et sorties (Exemple : cat ‹ toto.txt | sort -k1 | wc -w › resultat.txt)
  • Sequence de commande (exemple: ls -al ; ps aux | more ; date )

Evidememnt, ces trois types de complexités doivent pouvoir etre combinées sans aucune limitation.

Ce qui n’est pas attendu:

  • reconnaisance des variables (ex $HOME, $PATH)
  • variables d’environnement (exemple export PATH= …)

Optionnel (pour avoir 20/20)

  • Des commandes internes (executees par le processus du shell sans creation d’un fils): echo, pwd, cd, exit …
  • Gestion des erreurs avec affichage du status, comme dans les deux exemple suivants (processus cat tue avec kill dans le premier cas, et processus more tue avec kill dans le second):
hulotte-2% cat | more
;l;jjj;jjl
zsh: terminated  cat |
zsh: done        more
hulotte-2% cat | more
kjlkljkjkkjljk
zsh: broken pipe  cat |
zsh: terminated   more
  • La redirection de n’importe quel descripteur vers n’imnporte quel autre (exemple ls -al 2>&1 | more)
  • La gestion des jobs
    • Reconnaissance du caractère ‘&’ (exemple: emacs & find 2>1 > toto & ps aux | more)
    • job control: Reconnaissance de Ctrl-Z, commandes internes fg et bg

Ce petit programme python permet de mieux comprendre le fonctionnement du job controle. A essayer d’urgence:

#!/usr/bin/env python3.4
#
# -*- encoding: utf-8 -*-
# Author: Olivier Dalle
# date: May 4th, 2014
#
# Notice: Python version above can safely be changed to any
# 3.x python version available.

""" This small program can be used to trace the signals received by
a process during execution. The traced signals are caught by a tracing
handler and then self-resent by the process after the default handler is
reinstalled in place of the tracing handler. The tracing handlers of the
signals that put the process to sleep (SIGTSTP, SIGTTIN, SIGTTOU) are
reinstalled after the process execution is resumed by a SIGCONT, such
that the job control effects can be observed.

Some interesting experiments:
- run a single instance, and watch: it should display the programm name
at regular interavals
- try Ctrl-Z: see SIGTSTP occuring. Notice process group, process id, and
terminal owner
- restart using fg (foreground), notice SIGCONT occuring
- while in foreground, try to type something at the keyboard and
see your text repeated after typing enter
- Try Ctrl-Z + restart with bg: notice the change in terminal owner. Even
though it is running in background, the process is still able to
output to the controlling terminal.
- try again to type something at the keyboard and observe the effect: the
process is stopped after the first character is typed. The shell prompt is
reprinted followed by the character that was just typed, which is the
demonstration of the system principle: all characters typed in are destined
exclusively to the foreground process, however the bg process can still
progress as long as it does not try to read something from the terminal and
no interaction with the fg process occurs.
"""



import sys,os,signal,time,select

"""32 standard POSIX signals"""
SIGNALS=('ZERO','HUP','INT','QUIT','ILL','TRAP','ABRT','EMT','FPE','KILL',
         'BUS','SEGV','SYS','PIPE','ALRM','TERM','URG','STOP','TSTP','CONT',
         'CHLD','TTIN','TTOU','IO','XCPU','XFSZ','VTALRM','PROF','WINCH','INFO',
         'USR1','USR2')

def process_infos(str="???"):
    """Show some useful data about the current process"""
    # stdin/stdout not always connected to a controlling terminal
    try:
        term_owner0 = os.tcgetpgrp(0)
    except OSError:
        term_owner0 = 0
    try:
        term_owner1 = os.tcgetpgrp(1)
    except OSError:
        term_owner1 = 0
    return "processus %s: pid=%d, pere=%d, groupe=%d, term owner:%d/%d, sid=%d"%(str,os.getpid(),os.getppid(),os.getpgid(0),term_owner0,term_owner1, os.getsid(0))

def sigtrace_handler(sig,ign):
    """Trace signal and resend with default handler to see what happens"""
    global SIGNALS
    print("received SIG%s: %s"%(SIGNALS[sig],process_infos("???")),file=sys.stderr)
    if sig == 2:
        # Python has a special handler for SIGINT that generates
        # a KeyboardInterrupt exception
        signal.signal(sig,signal.default_int_handler)
    elif sig == signal.SIGCONT:
        # When the process restarts after being stopped we re-install
        # tracing handler on Ctrl-Z and TTIN/TTOUT signals so it is
        # possible to play with job control
        signal.signal(signal.SIGTSTP,sigtrace_handler)
        signal.signal(signal.SIGTTOU,sigtrace_handler)
        signal.signal(signal.SIGTTIN,sigtrace_handler)
    else:
        # Once a signal has been received we reinstall the default
        # handler before self-resending the signal
        signal.signal(sig,signal.SIG_DFL)
    # All signal received but SIGCONT are self-resent after being received
    if sig != signal.SIGCONT:
        os.kill(os.getpid(),sig)

def main():
    """Main function. Even though this programm can be used as
a module it is meant to be started as a self-contained program."""

    os.write(2,bytes(process_infos(sys.argv[0])+"\n",'utf-8'))
    # Some signals of interest. Extend the list at will...
    signal.signal(signal.SIGALRM,sigtrace_handler)
    signal.signal(signal.SIGUSR1,sigtrace_handler)
    signal.signal(signal.SIGUSR2,sigtrace_handler)
    signal.signal(signal.SIGINT,sigtrace_handler)
    signal.signal(signal.SIGTERM,sigtrace_handler)
    signal.signal(signal.SIGCONT,sigtrace_handler)
    signal.signal(signal.SIGTSTP,sigtrace_handler)
    signal.signal(signal.SIGTTOU,sigtrace_handler)
    signal.signal(signal.SIGTTIN,sigtrace_handler)
    signal.signal(signal.SIGHUP,sigtrace_handler)


    while True:
        # Receiving a signal while in select will raise an exception
        try:
            # Wait for new input for at most one second
            rrdy,wrdy,erdy = select.select([sys.stdin],[],[],1.0)
            if len(rrdy) > 0:
                # select returns with stdin ready (before one second)
                buf = os.read(0,1024)
                os.write(1, bytes(sys.argv[0]+":",'utf-8')+buf)
            else:
                # select return with nothing to read on stdin
                os.write(1,bytes(sys.argv[0]+"\n","utf-8"))
        except InterruptedError:
            # select interrupted by signal: simply ignore and proceed
            pass
# Notice SIGINT is not included, it is a special case that
# generates a KeyboardInterrupt Exception that is not caught
# in this program (which results in program termination).


if __name__ == "__main__":
    main()