PRACTICA DE SISTEMAS OPERATIVOS :

           LISTADO RECURSIVO DE DIRECTORIOS

        AUTORES: Alberto de Miguel Roncal y Javier Vara Garcia



EXPLICACION DE LA PRACTICA.

La practica consiste en listar el contenido de un directorio dado, es decir, el nombre de los ficheros y subdirectorios
que hay en el. Esto lo conseguiremos con la funcion recursiva listar (que hemos creado).

Un directorio contiene siempre los subdirectorios . (el directorio actual) y .. (el directorio padre del actual), pero
tambien puede contener ficheros y otros subdirectorios. Los subdirectorios que esten dentro del directorio actual
tambien seran listados de igual manera que el directorio padre, a traves de procesos (hijos) en los que se llama a
la funcion listar. Esto se consigue introduciendo la recursividad en la funcion listar. De esta manera se logra que
haya varios procesos ejecutandose a la vez (concurrentemente).

Mediante el uso de memoria compartida se implementa la comunicacion y sincronizacion de los procesos, que es
necesaria para que el proceso padre sepa si todos sus procesos hijos han acabado de realizar su tarea (el listado
del subdirectorio). En caso contrario, el proceso padre esperara por los procesos hijos.

Una vez se haya acabado de volcar en el fichero que especifiquemos todos los contenidos de directorios y subdi-
rectorios, se realizara la ordenacion alfabetica de las entradas de este fichero resultante(ponemos el resultado en
otro fichero). Para distinguir ficheros de directorios usaremos <> englobando el nombre de un directorio, y [] englo-
bando el nombre de un fichero.

( VOLVER AL MENU PRINCIPAL )



IMPLEMENTACION DE LA PRACTICA EN C

 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <string.h>
 #include <dirent.h>
 #include "rshmem.h"

 int pasar(char *);        /* prototipos de funciones */
 listar( char *);

FILE *fp;             /* puntero al fichero donde guardar el listado sin ordenar */

main(int argn, char *argv[]) {
  static char directorio[MAXNAMLEN] ;        /* directorio a listar */

/* comprobacion de los argumentos del main */

  if (argn == 1) {
     strcpy(directorio, ".") ;
  } else if (argn == 2) {
     strcpy(directorio, argv[1]) ;
  } else {
    (void) fprintf(stderr, "Numero de argumentos incorrecto\n");
    exit (-1);
  }

  printf("El listado sin ordenar se volcara al fichero salida,el ordenado al fichero salida2\n");

  if( (fp=fopen("salida","w")) == NULL ){
     printf("No se puede abrir el fichero salida\n");
     exit(-4);
  }

  listar(directorio);              /* llamada a la funcion recursiva "listar" */
  fclose(fp);                       /* cierre del fichero salida */
  system("sort -o salida2 salida");  /* ordenacion alfabetica del fichero salida */
  exit(0);
 }     /* fin main */

/****************** DECLARACION DE FUNCIONES ************************/

listar (char *directorio) {

  char directorioact[256];       /* directorio actual que se recorre */
  static char nombreCompleto[256] ;      /* entrada de directorio */
  DIR *dir ;                 /* puntero a directorio a recorrer */
  struct dirent *entrada ;      /*puntero a estructura que contiene informacion sobre una entrada del directorio */
  struct stat entrada_stat ;   /* estructura que contiene informacion sobre el directorio */
  int *nprocfinalizados=NULL;       /* contador del numero de procesos hijos que han acabado */
  int contador=0;          /* contador del num. de procesos hijos creados */

strcpy(directorioact,directorio);

/* busca el posible acceso al directorio */
  if ((dir = opendir(directorio)) == NULL) {
     (void) fprintf(stderr, "No existe el directorio <%s>\n", directorio);
     exit(-2) ;
  }

/* crear zona de memoria compartida */
  if ( !crearMemoria() )
     fprintf(stderr,"error de crearMemoria\n");
  
  nprocfinalizados=(int *)memoria;
  (*nprocfinalizados)=0;

  c: /* etiqueta de goto */

/* lectura de las entradas del directorio */
  while ((entrada=readdir(dir)) != NULL) {

/* formacion del nombre completo de una entrada del directorio */
  sprintf(nombreCompleto, "%s/%s", directorioact, entrada->d_name) ;

  if (stat(nombreCompleto, &entrada_stat)) {
     (void) fprintf(stderr,"No puedo comprobar <%s>\n", nombreCompleto);
     closedir(dir) ;                   /* la entrada del directorio no se puede comprobar */
     exit(-3);
  }

 if (pasar(nombreCompleto)){                   /* la entrada es el directorio padre ..*/
  fprintf(fp,"<%s>\n", nombreCompleto) ;  /* o el directorio hijo .                     */
  fflush(fp);
  strcpy(nombreCompleto,directorioact);
  goto c;
  }

  if (S_ISDIR(entrada_stat.st_mode) ) {           /* la entrada es directorio */
  contador++;
  fprintf(fp,"<%s>\n", nombreCompleto) ;
  fflush(fp);
  if ( 0==fork() ) {             /* creacion de un proceso hijo */
     listar (nombreCompleto);       /* llamada a la funcion listar */
     (*nprocfinalizados)+=1;
     exit (0);
    }
  }  
  else {             /* la entrada es fichero */
     fprintf(fp,"[%s]\n", nombreCompleto) ;
     fflush(fp);
  }

}    /* del while */

  closedir(dir) ;
  while ( (*nprocfinalizados) != contador );      /* espera por los hijos */

/* eliminar la memoria compartida */
  if ( !eliminarMemoria() )
  fprintf(stderr,"error de eliminar memoria\n");

} /* fin listar */

int pasar(char *direc){    /* se ocupa de identificar el directorio . y .. */
   int n;       /* numero de caracteres distintos de \0 */
   n=strlen(direc);
   if(n==3) {
      if ( !strcmp(&direc[1], "/.") )        return 1;
   }
else {
   if ( !strcmp(&direc[n-3], "/..") )  {
     return 1;
   }
  if ( !strcmp(&direc[n-2], "/.") ) {
     return 1;
  }
}
return 0;
}

( VOLVER AL MENU PRINCIPAL )




DOCUMENTACION DE LA PRACTICA

/**********************************************************
*
* NOMBRE : main       
*SINOPSIS : int main ( int argn, char *argv[[)
* ARGUMENTOS :
*     argv[1[==> nombre del directorio a listar
* SALIDA : el resultado del listado del directorio en un fichero ->salida
*                  el resultado del listado ordenado alfabeticamente en fichero -> salida2
* DESCRIPCION :
* El programa recibe un nombre de directorio a listar como argumento. Si el programa no recibe ningun
* argumento, el directorio a listar sera el actual (.). En cualquier otro caso, el programa abandona la
* ejecucion. Luego se llama a la funcion listar que es la encargada de realizar el trabajo de listado.
* Despues el listado del directorio sin ordenar situado en el fichero salida se ordena y se guarda
* en otro fichero(salida2).
*
* VALOR DE RETORNO :      0 ==> si no ha habido problemas
*         ( al S.O. )                  -1,-4 ==> ha habido problemas **********************************************************/

/**********************************************************
*
* NOMBRE : listar
*SINOPSIS : listar ( char *directorio )
* ARGUMENTOS : * directorio==> nombre del directorio a listar
* SALIDA : el resultado de la traduccion, en un fichero
* DESCRIPCION :
* La funcion recibe el nombre del directorio a listar. Intenta acceder a dicho directorio mediante la funcion
* opendir; si se tiene exito, esta funcion nos devuelve un puntero al directorio (dir). Despues se crea una zona
* de memoria compartida, donde situaremos el contador del numero de procesos hijos que han finalizado
* (nprocfinalizados). Este contador sera disminuido en una unidad por cada proceso hijo cuando termine; asi
* el p.padre sabe si puede  acabar el tambien (si todos los hijos han acabado), o no. Luego, con la funcion
* readdir vamos recorriendo cada   entrada (fich. o direct.) del directorio actual, y formamos su nombrecompleto.
* Podemos tener 4 situaciones :
*   - la entrada no se puede comprobar => cerramos el directorio, y abandonamos.
*   - la entrada es fichero => la volcamos al fichero de salida
*   - la entrada es . o .. => la volcamos al fichero, pero no la abrimos, leemos una nueva entrada (con goto)
*   - la entrada es directorio distinto del . y del .. => la volcamos a fichero. Se incrementa el contador de procesos
*     hijos creados. Creamos un proceso hijo,que se encarga del listado de este subdirectorio mediante   una
*     llamada (recursividad) a listar. Se incrementa el numero de ficheros finalizados (cuando acaba la llamada a listar).
*     Una vez que el directorio ( con el que llamamos a listar por 1ª vez) ha acabado de ser listado se cierra, y se espera
*     hasta que sus subdirectorios acaben de ser listados (esto ocurre cuando el nº de procesos finalizados es igual al
*     nº de procesos creados). Se elimina la memoria compartida creada y se sale de la funcion.
*
* VALOR DE RETORNO : 0 ==> si no ha habido problemas
*                                      nº negativo ==> ha habido problemas * **********************************************************/

/**********************************************************
*
* NOMBRE : pasar
* SINOPSIS : int pasar ( char *direc )
* ARGUMENTOS :
*                direc==> nombre del directorio a comprobar                         
* DESCRIPCION :
* La funcion recibe como argumento el nombre de un directorio. Comprueba si este directorio es el directorio actual .
* o el padre del directorio actual . Estas comprobaciones se realizan comparando el final del nombre del directorio con
*  las cadenas de caracteres \. y \..
* VALOR DE RETORNO :      0 ==> el directorio no es el . ni el ..
*                                               1 ==> el directorio es el . o el .. **********************************************************/

( VOLVER AL MENU PRINCIPAL )



EJEMPLO DE APLICACION

Para ver como funciona el programa, usamos este arbol de directorios:
(para distinguir ficheros de directorios, ponemos en negrita los directorios)

                                                      

                                       DIR
                                          |
                  -----------------------------------------------------------
                 |            |               |              |                 |              |              |
             DIR1    fic1         fic2         DIR2            fic3          fic4         fic5
                 |                                           |
        --------------                   ------------------
        |                   |                   |              |            | 
   ficher1          ficher2           DIR4    archivo1    DIR5
                                                                            |
                                                                 -----------------  
                                                                  |           |            |
                                                               DIR6      filea       fileb
                                                                  |
                                                            ---------
                                                            |            |
                                                          file1      file2


  La salida ordenada, contenida en el fichero "salida2", queda:

[dir/dir1/fichero1]
[dir/dir1/fichero2]
[dir/dir2/archivo1]
[dir/dir2/dir5/dir6/file1]
[dir/dir2/dir5/dir6/file2]
[dir/dir2/dir5/filea]
[dir/dir2/dir5/fileb]
[dir/fic1]
[dir/fic2]
[dir/fic3]
[dir/fic4]
[dir/fic5]
<dir/dir1>
<dir/dir1/.>
<dir/dir1/..>
<dir/dir2>
<dir/dir2/dir4>
<dir/dir2/dir4/.>
<dir/dir2/dir4/..>
<dir/dir2/dir5>
<dir/dir2/dir5/dir6>
<dir/dir2/dir5/dir6/.>
<dir/dir2/dir5/dir6/..>
<dir/dir2/dir5/.>
<dir/dir2/dir5/..>
<dir/dir2/.>
<dir/dir2/..>
<dir/.>

( VOLVER AL MENU PRINCIPAL )




FICHEROS AUXILIARES

Para la realizacion de la practica se necesitan ficheros que contienen funciones que se basan en el interface
de aplicacion de memoria compartida en UNIX, y que facilitan el trabajo.

RSHMEM.H

/* rshmem.h Fichero de cabecera con definiciones y declaraciones para usar memoria compartida.*/
#include <stdio.h>
#include <time.h>
#include <sys/types.h>

#include <sys/ipc.h>
#include <sys/shm.h>

#define ARRAY_SIZE 4000
#define MALLOC_SIZE 10000
#define SHM_SIZE 10000
#define SHM_MODE (SHM_R | SHM_W) /* read/write */

#define TRUE 1
#define FALSE 0

#ifdef RUTINAS_SHMEM
    static int shmid; /* handler de memoria compartida */
    char *memoria; /* puntero a zona de memoria compartida */
#else
    extern char *memoria;
#endif
/* Prototipos de funciones de memoria compartida */
void origenTiempo();
void tiempoPasa();
int crearMemoria() ;
int eliminarMemoria() ;
#define TP tiempoPasa();


RSHMEM.C

#define RUTINAS_SHMEM
#include "rshmem.h"

/* Crea memoria compartida.
* - el manejador de memoria es interno
* - manda mensajes de error por salida de error estándar.
*/
int crearMemoria() {
   char *funcName = "crearMemoria";
   if ((shmid=shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE))<0){
       fprintf(stderr, "%s: error de shmget()\n", funcName);
   }
   else if ((memoria=shmat(shmid, 0, 0)) == (void *) -1) {
      fprintf(stderr, "%s: error de shmat()\n", funcName);
    }
   else {
       return TRUE;
   }
    return FALSE;
   }
/* Destruye la memoria compartida creada por crearMemoria() */
int eliminarMemoria() {
   char *funcName = "eliminarMemoria";
   if (shmctl(shmid, IPC_RMID, 0) < 0) {
      fprintf(stderr,"%s: error de shmctl()\n", funcName);     return FALSE ;
   }
   else return TRUE ; }

/* Coloca una semilla en el temporizador del bucle de * tiempoPasa() */
void origenTiempo(){
   srand((unsigned int) time(NULL)) ;
  }

/* Rutina que hace pasar un poco de tiempo con un bucle * sencillo */
void tiempoPasa() {
   unsigned int i;
   int a=3;
/* Los parametros "50" y "2" dependen mucho de la velocidad * de la computadora y de la configuracion del SO. Espero que * funcionen bien */
  for (i=rand()/50; i>0; i--) {
     a = a%3 + i;
  }

( VOLVER AL MENU PRINCIPAL )