Pipes, Redirección, Señales y Mandatos Internos en un Minishell en C
Pipes
Si el número de mandatos es mayor que 0, entonces se crean pipes. El minishell espera la terminación del último mandato de la secuencia antes de solicitar la siguiente línea de entrada. El valor de una secuencia es el valor del último mandato.
if (ret - 2 > 0) // ret - 2 es el número de mandatos
{
// Creamos el pipe
if (pipe(p) < 0)
{
perror("Pipe: ");
exit(1);
}
// EJECUTO MANDATOS INTERNOS Y EXEC DE NO INTERNOS
}
pid = fork();
switch (pid)
{
case -1:
perror("Fork");
exit(1);
case 0: // hijo
if (ret - 2 > 0)
{
dup2(fd_in, 0); // cambia la entrada según la anterior
if (ret - 2 != argvc)
{
dup2(p[1], 1);
close(p[1]);
}
else
close(p[1]);
close(p[0]);
}
default: // padre
if (bg)
{
printf("[%d]\n", pid);
char pid_s[20];
sprintf(pid_s, "%d", pid);
setenv("bgpid", pid_s, 1);
}
else
{
if (argvc == ret - 2)
{
waitpid(pid, &status, 0);
char str_st[20];
sprintf(str_st, "%d", WEXITSTATUS(status));
setenv("status", str_st, 1);
close(p[0]);
}
if (argvc > 0)
close(fd_in);
close(p[1]);
fd_in = p[0]; // guarda la entrada para el siguiente mandato
}
}Redirección
< usa un fichero como entrada estándar, abriéndolo para lectura.
>fichero usa el fichero como salida estándar. Si no existe se crea y, si existe, se trunca con permisos 0666.
>& fichero igual que el anterior, pero con la salida de error estándar.
if (filev[2 o 1] != NULL && argvc == lineaArgs)
{
int fd = creat(filev[2], 0666);
if (fd < 0)
{
printf("Error al crear el fichero de errores\n");
break;
}
dup2(fd, STDERR_FILENO o STDOUT_FILENO);
close(fd);
}
if (filev[0] != NULL && argvc == 0)
{
int fd = open(filev[0], O_RDONLY);
if (fd < 0)
{
printf("Error al abrir el fichero de entrada\n");
break;
}
dup2(fd, STDIN_FILENO);
close(fd);
}Señales y Background
Señales
Ni el minishell ni el proceso en background mueren por las señales SIGINT y SIGQUIT generadas por teclado. Los mandatos en primer plano deben morir (comportamiento por defecto).
Background
Ejecuta de forma asíncrona en segundo plano, de modo que el minishell no queda bloqueado esperando su terminación. Se actualiza la variable bgpid.
if (bg == 0)
{
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
}
else if (bg == 1)
{
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
}
if (bg)
{
printf("[%d]\n", pid);
char pid_s[20];
sprintf(pid_s, "%d", pid);
setenv("bgpid", pid_s, 1); // al haber background se actualiza la variable bgpid
}Variables especiales
// prompt
setenv("prompt", "msh> ", 1);
// mypid
pid_t pid = getpid();
char spid[20];
// convertir a cadena
sprintf(spid, "%d", pid);
setenv("mypid", spid, 1);
// status
setenv("status", "0", 1);Mandatos internos
Deben implementarse dentro del minishell. Se ejecutarán en un subshell si son background o si aparecen en una secuencia y son el último mandato. Comprobarán el número de argumentos y, si hay error, lo notificarán por stderr terminando con un valor distinto de 0. La variable status se modifica aquí con setenv("status", "0", 1).
cd
char *rut = getenv("HOME");
if (rut == NULL)
fprintf(stderr, ...);
a = chdir(rut);
if (a == 0)
{
char *buffer = getcwd(NULL, 0);
if (buffer != NULL) ...
}
else if (argc == 2)
{
a = chdir(argv[1]);
if (a == -1)
perror(...);
else
getcwd(NULL, 0);
}umask
S_IWGRP | S_IWOTH es el valor 022.
mode_t a;
mode_t mascara = strtol(argv[1], NULL, 8);
// fork: el padre y el hijo hacen umask(mascara); el padre imprime con printf("%o", a);limit
int recursos[] = {RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA, RLIMIT_STACK, RLIMIT_CORE, RLIMIT_NOFILE};
n = sizeof(recursos) / sizeof(recursos[0]);
for (i = 0; i < n; i++)
...set
Si ARGC == 1: se declara extern char **environ; y se recorre e imprime con un bucle while.
Si ARGC == 2: si getenv(argv[1]) != NULL, se imprime %s=%s con argv[1] y getenv(argv[1]).
Si ARGC > 2: setenv(argv[argc][1], valores, 4). Se usa 4 porque, si vale 0, no reescribe la variable.
Metacaracteres
Virgulilla (~)
void metacaracterV(int argc, char **argv)
{
char name[MAX_BUF];
for (argc = 0; argv[argc]; argc++)
{
if (sscanf(argv[argc], "~%[_a-zA-Z0-9]", name))
{
char *home;
if (strcmp(argv[argc], "~") == 0)
home = getenv("HOME");
else
home = getpwnam(name)->pw_dir;
argv[argc] = (char *)realloc(argv[argc], strlen(home) + 1);
if (argv[argc] == NULL)
fprintf(stderr, "Error");
strcpy(argv[argc], home);
}
}
}Dólar ($)
void metacaracterD(int argc, char **argv)
{
for (argc = 0; argv[argc]; argc++)
{
char *pre = (char *)malloc(strlen(argv[argc]) + 1);
char *mid = (char *)malloc(strlen(argv[argc]) + 1);
char *post = (char *)malloc(strlen(argv[argc]) + 1);
if (sscanf(argv[argc], "%[^$]$%[_a-zA-Z0-9]%s", pre, mid, post))
{
char *env = getenv(mid);
if (env != NULL)
{
argv[argc] = (char *)realloc(argv[argc], strlen(pre) + strlen(env) + strlen(post) + 1); // +1 por el \0
if (argv[argc] == NULL)
fprintf(stderr, "Error al realocar memoria");
strcpy(argv[argc], pre);
strcat(argv[argc], env);
strcat(argv[argc], post);
}
}
if (sscanf(argv[argc], "$%[_a-zA-Z0-9]%s", mid, post))
{
char *env = getenv(mid);
if (env != NULL)
{
argv[argc] = (char *)realloc(argv[argc], strlen(env) + strlen(post) + 1); // +1 por el \0
if (argv[argc] == NULL)
fprintf(stderr, "Error al realocar memoria");
strcpy(argv[argc], env);
strcat(argv[argc], post);
}
}
if (sscanf(argv[argc], "$%[_a-zA-Z0-9]", mid))
{
char *env = getenv(mid);
if (env != NULL)
{
argv[argc] = (char *)realloc(argv[argc], strlen(env)); // +1 por el \0
if (argv[argc] == NULL)
fprintf(stderr, "Error al realocar memoria");
strcpy(argv[argc], env);
}
}
free(pre);
free(mid);
free(post);
}
}Expansión de ficheros
Antes de la ejecución de cada orden, cada texto se examina en busca de caracteres comodín. Si aparece algún comodín se realiza la expansión de ficheros. Si no aparece ningún fichero, el texto no se altera. Si en el texto aparece el carácter /, no se tratan los comodines (GLOB_NOESCAPE).
if (strchr(argv[i], '?') != NULL)
{
glob_t pglob;
if (glob(argv[i], GLOB_NOESCAPE, NULL, &pglob) == 0)
{
int j;
char *new = malloc(1);
strcpy(new, "");
for (j = 0; j < pglob.gl_pathc; j++)
{
new = (char *)realloc(new, strlen(pglob.gl_pathv[j]) + 1);
strcat(new, pglob.gl_pathv[j]);
if (j != pglob.gl_pathc - 1)
{
strcat(new, " ");
}
}
argv[i] = (char *)realloc(argv[i], strlen(new) + 1);
strcpy(argv[i], new);
free(new);
}
}Los resultados de una llamada a glob() se almacenan en la estructura apuntada por pglob. Esta estructura es de tipo glob_t (declarada en <glob.h>) e incluye los siguientes elementos definidos por POSIX.2 (puede haber más presentes como extensión):
typedef struct {
size_t gl_pathc; /* Recuento de rutas coincidentes hasta ahora */
char **gl_pathv; /* Lista de nombres de ruta coincidentes */
size_t gl_offs; /* Ranuras para reservar en gl_pathv */
} glob_t;