Creando una Shell de Linux (III)


Ya tenemos una shell la mar de maja, que puede navegar por nuestro árbol de directorios mediante el comando “cd”. Es hora de implementar lo necesario para ver los contenidos de estas carpetas y ya aviso que hay que remangarse porque la cosa empieza a ponerse interesante. 🙂
Vamos a allanar el terreno para que en el próximo post podamos escribir nuestra propia versión del comando “ls”. Todas estas funciones detalladas aquí servirán de soporte para ella, y aunque ahora mismo el shell no haga nada nuevo, quedará preparado para ello.

De modo que vamos allá. Para empezar, implementaré un par de procedimientos que, dado un ID de usuario o un ID de grupo, me devuelvan ese grupo o usuario. Las funciones a las que debo llamar son “getgrpgid()” y “getpwduid()”, y si consulto man, puedo ver que ambas dependen de la libreria “sys/types.h” y luego cada una tiene su propio módulo “grp.h” y “pwd.h”:

Las ventajas de usar la documentación de man es que además puedo ver un prototipo de las estructuras de datos con las que operan, así que ya se incluso cómo se almacenan grupos y usuarios y qué dato de la estructura debo leer:

Así que importo esas librerías:

E implemento dos funciones que son la mar de sencillas:

Son prácticamente análogas: creo una variable para acceder luego a la estructura y con el identificador que recibo como argumento, intento recuperarla. Si recibo un valor NULL, habrá ocurrido un error, y sino ya puedo devolver el campo que me interesa (“gr_name” o “pw_name”, según sea una u otra).

Una de las características más acertadas del diseño de UNIX (y por extensión, de Linux, OS X, etc.) es que toda entidad es un fichero; y unos bytes de control son los que se encargan de determinar si ese fichero se corresponde con un directorio, un enlace simbólico, una tubería (pipe), un archivo normal y corriente, etc. También otros bytes son los que nos determinan su “modo” o dicho de otra manera, sus permisos.
Por eso, cuando desde el terminal lanzas un ls -l ves algo así:

Esta cadena está formada por once caracteres: el de tipo de fichero, los nueve de permisos y el Sticky bit si se trata de un directorio.

Vamos a olvidarnos momentáneamente de cómo acceder al modo del archivo en si. Supondremos que nos lo envían como argumento. Vamos a aprovechar el operador “&” de C para hacer una comparación del primer caracter y saber de qué tipo de archivo estamos hablando:

Esto devolverá el char correspondiente.

Desde otra, conseguiremos el resto de caracteres que nos indiquen los permisos del archivo e iremos dando formato a la cadena “mode”:

Como ves, creo una cadena vacía de 11 caracteres y la lleno de guiones. El primer caracter lo establezco llamando a la operación “fileType” que acabamos de escribir. Los demás son simples comparaciones para ver qué permisos están activos. El último es el Sticky bit, que de estar activo se representa generalmente con una T mayúscula. En los sistemas operativos modernos, el Sticky Bit puede indicar incluso más cosas si aparece también en otras posiciones o con la t minúscula pero por simplicidad voy a mantenerlo así en este ejemplo. En el artículo de Wikipedia que he enlazado antes hay mucha más información.
Para terminar, la función devuelve la cadena “mode” ya lista.

De acuerdo, si has llegado hasta aquí, tienes suficientes medios como para ir llenando la información de cada línea de ls… excepto porque no hemos aun accedido a los archivos para ver otras propiedades, como su nombre o su tamaño, por ejemplo.
Para hacerlo, vamos a recurrir a la función “stat”. Puedes consultar más información con:
$ man lstat
Las funciones que hemos escrito y las que nos faltan por crear nos obligan a incluir la librería “sys/stat.h”, así que voy a importarla antes de que se me olvide.
#include <sys/stat.h>
Esto si estás trabajando sobre Linux. Hasta ahora nuestra shell también funcionaba sobre OS X, pero la diferencia en la implementación de ambos sistemas y en lo que instalan o no, hace que esta página de man no esté disponible para la manzana (si alguien sabe como hacerlo, agradeceré la información que me podáis dar, porque no he investigado nada sobre el tema).

El caso es que stat nos permite acceder a los ficheros mediante un struct que se detalla en el manual:

Así que ya no hay dudas de cómo extraer la información que nos falta. Yo voy a hacer un procedimiento que prepare el string correspondiente, con el modo, el numero de enlaces hacia el elemento, el grupo, el propietario, el tamaño y el nombre de archivo.

Este es el código:

Comenzamos creando un buffer llamado “buf” que es una estructura donde guardar el acceso al stat con el que manejaremos el fichero. A continuación reservamos los espacios para los strings de modo, grupo y usuario, así como para la linea “metadata”, que es la cadena en la que concatenaremos todas las anteriores.
A continuación, utilizamos “sprintf” para escribir en la variable “file” la ruta y el nombre del fichero en cuestion (que nos vienen como argumentos del procedimiento) en la forma ruta/nombre.
Así tenemos la ruta absoluta, y la necesitamos para hacer “stat” sobre el archivo. Una vez hecho el “stat”, ya podemos acceder a los campos st_mode, st_guid y st_uid. Son los que debemos pasarle a las funciones que hemos escrito antes para recuperar el modo, grupo y usuario como cadenas. Una vez hecho, basta con usar “sprintf” para ir formateando la cadena “metadata” con lo que queremos que aparezca. Voy a detenerme un momento en ella, ya que estoy utilizando una forma de formatear la salida en columnas un poco chapucera pero efectiva:
sprintf(metadata, "%s %2ld %10s %10s %7ld %s\n", mode, buf.st_nlink, group, user, buf.st_size, name);

Obviamente las máscaras “%s” son las de cadenas de caracteres. El primer string es el de modo, y se coloca sin más. Seguidamente viene un número entero, que es el de número de enlaces hacia ese nodo en el sistema de archivos. Por ser un entero, su máscara es “%d”, pero he añadido un 2 para pedir que deje espacio para que se escriban dos dígitos, y una “l” para alinear los números a la derecha; de forma que queden bien colocaditos.
Lo mismo pasa con “%10s”. Es una máscara para escribir un string reservandole 10 caracteres. Para el tamaño de archivo también he reservado 7, alineados de nuevo a la derecha: “%7ld”.
No te olvides de incluir el salto de línea al terminar: “\n”.
Una vez hecho, ya podemos devolver la cadena “metadata”.

Con esto tenemos todo listo para acceder a un fichero y volcar su información en una línea de texto. ¿Que queda por hacer para que nuestro shell pueda listar directorios? Pues hacer un procedimiento que pueda acceder a los contenidos de un directorio y que, para cada uno de los elementos que contiene la carpeta, ir llamando a la función que vuelca sus metadatos. ¡FÁCIL!
Ya estamos muy cerca de implementar “ls”! 🙂

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s