PROYECTO PRÁCTICO PR7
(Gestión de Procesos 7)

El objetivo de este proyecto práctico es la implementación de un sencillo intérprete de mandatos en UNIX/Linux.
El programa a desarrollar ha de  seguir  las especificaciones y requisitos contenidos en este documento.
 

1. Descripción.

            El intérprete de mandatos a desarrollar o minishell  va a  utilizar :

Para el desarrollo de esta práctica se proporciona un  parser   que permite leer los mandatos introducidos por el usuario.  Para realizar la práctica sólo tendreis que preocuparos de implementar el intérprete de mandatos.

            La sintaxis que se propone para parser que se os proporciona es similar a la sintaxis que comprende el parser del intérprete de mandatos del sistema operativo UNIX, y es la siguiente:


1.1 Obtención de la línea de mandatos leída

Para el desarrollo de esta práctica se os proporciona código de apoyo que se describe más abajo (msh.tgz).

Para obtener la línea de mandatos tecleada por el usuario debe utilizarse la función obtain_order  cuyo prototipo es el siguiente:

      int obtain_order(char ***argvv, char **filev, int *bg);

La llamada devuelve 0 en caso de teclear Control-D (EOF), -1 si se encontró un error. Si se ejecuta con éxito la llamada devuelve el número de mandatos más uno. Así, por ejemplo:

El argumento argvv permite tener acceso a todos los mandatos introducidos por el usuario. Con el argumento filev se pueden obtener los ficheros utilizados en la redirección: El argumento bg es 1 si el mandato o secuencia de mandatos debe ejecutarse en background.

Si el usuario teclea ls -l | sort < fichero. Los argumentos anteriores tendrán la disposición que se muestra en la siguiente figura.

Disposición de los argumentos en argvv

Figura 1: Disposición de los argumentos en argvv.




En el archivo  main.c  que se os proporciona (archivo que debeis rellenar con el código del minishell) se invoca a la función obtain_order  y se ejecuta el siguiente bucle:

            for (argvc = 0; (argv = argvv[argvc]); argvc++) {
            for (argc = 0; argv[argc]; argc++)
            printf("%s ", argv[argc]);
            printf("\n");
            }
        if (filev[0]) printf("< %s\n", filev[0]);/* IN */
        if (filev[1]) printf("> %s\n", filev[1]);/* OUT */
        if (filev[2]) printf(">& %s\n", filev[2]);/* ERR */
        if (bg) printf("&\n");

Estas líneas no sirven para nada, salvo para que os familiariceis con las estructuras de datos devueltas por obtain_order.

Se recomienda que, sin modificar este fichero, compileis y ejecuteis este programa minishell, introduciendo diferentes mandatos y secuencias de mandatos para comprender claramente como acceder a cada uno de los mandatos de una secuencia.

1.2. Desarrollo del minishell

Para desarrollar el minishell  debeis seguir una serie de pasos, de tal forma que se construya el minishell de forma incremental. En cada paso se añadirá nueva funcionalidad sobre el anterior. El minishell consta de un bucle infinito del cual sólo se sale tras las detección de una condición EOF (un carácter Control-D) o si se ejecuta el comando interno exit. En la ejecución del bucle infinito, el minishell  (1) imprime el símbolo del prompt, (2) espera la entrada de comandos por parte del usuario y (3) los ejecuta. El código de apoyo que se proporciona en esta práctica contiene el núcleo básico del minishell para realizar el parsing de las líneas de entrada. La práctica consiste en modificar el código para, de forma progresiva, añadir al minishell las siguientes capacidades:

  1. Ejecución de mandatos simples del tipo ls -l , who, etc. Para este apartado deberá usar las llamadas al sistema fork, wait y execve. Para localizar los comandos, haced uso del contenido de la variable de entorno PATH.
  2. Ejecución de mandatos simples con redirecciones (entrada, salida y de error). Además de las llamadas anteriores deberá usar las llamadas dup o dup2.
  3. Ejecución de mandatos simples en background. No se debe usar wait.
  4. Ejecución de secuencias de mandatos conectados por pipes. Usar la llamada pipe.
  5. Tratamiento de señales. Ni el minishell ni los comandos lanzados en background, deben morir por señales generadas desde teclado (SIGINT, SIGQUIT) (man signal), por lo tanto éstas son ignoradas. Por contra, los comandos lanzados en foreground deben morir si le llegan estas señales, por lo tanto mantienen la acción por defecto.
  6. Ejecución de mandatos internos. Un mandato interno es aquél que bien se corresponde directamente con una llamada al sistema o bien es un complemento que ofrece el propio minishell. Para que su efecto sea permanente, ha de ser implementado y ejecutado dentro del propio minishell. Será ejecutado en un subshell (man fork) sólo si se invoca en background o aparece en una secuencia y no es el último.Todo mandato interno comprueba el número de argumentos con que se le invoca y si encuentra este o cualquier otro error, lo notifica (por el estándar error) y termina con valor distinto de cero.

  7. Los mandatos internos del minishell son:
    1.  cd [Directorio]. Cambia el directorio por defecto (man 2 chdir). Si aparece Directorio debe cambiar al mismo. Si no aparece, cambia al directorio especificado en la variable de entorno HOME. Presenta (por la salida estándar) como resultado el camino absoluto al directorio actual de trabajo (man getcwd) con el formato: "%s\n".
    2. umask [Valor]. Cambia la máscara de creación de archivos (man 2 umask). Presenta (por la salida estándar) como resultado el valor de la actual máscara con el formato: "%o\n". Además, si aparece Valor (dado en octal, man strtol), cambia la máscara a dicho valor.


2. Código Fuente de Apoyo

Para facilitar la realización de esta práctica se dispone del fichero msh.tgz que contiene código fuente de apoyo. Al extraer su contenido desde el directorio home de tu cuenta, se crea el directorio msh/, donde se debe desarrollar la práctica. Dentro de este directorio se habrán incluido los siguientes ficheros:
 


3. Recomendaciones Generales