Skip to content

Instantly share code, notes, and snippets.

@Minoru
Created September 11, 2011 17:27
Show Gist options
  • Save Minoru/1209852 to your computer and use it in GitHub Desktop.
Save Minoru/1209852 to your computer and use it in GitHub Desktop.
Sample implementation of shell AND-OR list in C89
#include <stdlib.h>
#include <string.h>
#include "routines.h"
int main(int argc, const char *argv[]) {
int status,
status0,
status1;
char **argv0,
**argv1,
**argv2;
argv0 = malloc(3 * sizeof(char*));
argv0[0] = strdup("echo");
argv0[1] = strdup("a");
argv0[2] = NULL;
argv1 = malloc(3 * sizeof(char*));
argv1[0] = strdup("cat");
argv1[1] = strdup("b");
argv1[2] = NULL;
argv2 = malloc(3 * sizeof(char*));
argv2[0] = strdup("echo");
argv2[1] = strdup("c");
argv2[2] = NULL;
/* imitating
* $ echo 'a' && echo 'b' || echo 'c'
*/
OR(
AND(exec_command(argv0), status0, label0,
exec_command(argv1), status1),
status1, label1,
exec_command(argv2),
status);
return status;
}
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "routines.h"
typedef int pipe_t[2];
int exec_command(char** cmd) {
/*
* Executes specified command
*
* Returns exit status of executed command, or, if command was killed by a
* signal, 128+signum, or 127 if command not found by execvp(), or 126 if
* fork() or execvp() failed
*/
int ret = 0;
pid_t pid = fork();
if(pid == -1) {
perror("Error in fork()");
ret = 126;
}
if(pid) {
int retval;
waitpid(pid, &retval, 0);
if(WIFEXITED(retval)) {
ret = WEXITSTATUS(retval);
} else {
/* killed by signal
* return 128+number of signal that teminated our child */
ret = 128 + WTERMSIG(retval);
}
} else {
execvp(cmd[0], (char * const*)cmd);
perror("Error in execvp()");
switch (errno) {
case EACCES:
_exit(127);
break;
default:
_exit(126);
}
}
return ret;
}
void copy_pipe(pipe_t*, pipe_t*);
void close_pipe(pipe_t);
void kill_processes(int, pid_t*);
int pipeline(int ncmds, char*** cmds) {
/*
* Creates chain of commands connected by pipelines
*
* Returns exit status of executed command, or, if command was killed by a
* signal, 128+signum, or 127 if command not found by execvp(), or 126 if
* fork() or execvp() failed
*/
pipe_t in_pipe, out_pipe;
pid_t *processes = NULL;
int i, status;
pid_t pid;
processes = malloc(ncmds * sizeof(pid_t));
if(pipe(out_pipe) == -1) {
perror("Error in pipe():");
free(processes);
exit(126);
}
/* run first command and redirect it's output to pipe */
pid = fork();
if(pid == -1) {
perror("Error in fork():");
close_pipe(out_pipe);
free(processes);
exit(126);
} else if(pid) {
processes[0] = pid;
} else {
dup2(out_pipe[1], 1);
close_pipe(out_pipe);
execvp(cmds[0][0], cmds[0]);
perror("Error in execvp():");
switch(errno) {
case EACCES:
_exit(127);
break;
default:
_exit(126);
}
}
/* now run all commands from second to the one before last one */
for(i = 1; i < ncmds-1; i++) {
copy_pipe(&in_pipe, &out_pipe); /* copy out_pipe to in_pipe */
if(pipe(out_pipe) == -1) {
perror("Error in pipe():");
close_pipe(in_pipe);
kill_processes(i, processes);
free(processes);
exit(126);
}
pid = fork();
if(pid == -1) {
perror("Error in fork():");
close_pipe(in_pipe);
close_pipe(out_pipe);
kill_processes(i, processes);
free(processes);
exit(126);
} else if(pid) {
processes[i] = pid;
} else {
dup2(in_pipe[0], 0);
dup2(out_pipe[1], 1);
close_pipe(in_pipe);
close_pipe(out_pipe);
execvp(cmds[i][0], cmds[i]);
perror("Error in execvp():");
switch(errno) {
case EACCES:
_exit(127);
break;
default:
_exit(126);
}
}
close_pipe(in_pipe);
}
copy_pipe(&in_pipe, &out_pipe);
/* run last command */
pid = fork();
if(pid == -1) {
perror("Error in fork():");
close_pipe(in_pipe);
close_pipe(out_pipe);
kill_processes(ncmds-1, processes);
free(processes);
exit(126);
} else if(pid) {
processes[ncmds-1] = pid;
} else {
dup2(in_pipe[0], 0);
close_pipe(in_pipe);
execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
perror("Error in execvp():");
switch(errno) {
case EACCES:
_exit(127);
break;
default:
_exit(126);
}
}
close_pipe(in_pipe);
/* waiting for processes to end */
for(i = 0; i < ncmds-1; i++)
waitpid(processes[i], NULL, 0);
/* waiting for last process, gather it's exit information */
waitpid(processes[ncmds-1], &status, 0);
free(processes);
if(WIFEXITED(status))
return WEXITSTATUS(status);
else
/* killed by a signal */
return 128 + WTERMSIG(status);
}
/* Utility functions */
void close_pipe(pipe_t p) {
/*
* Closes given pipeline
*/
int i, ret;
for(i = 0; i < 2; i++)
do {
ret = close(p[i]);
} while (ret == -1 && (errno == EINTR || errno == EIO));
}
void kill_processes(int n, pid_t processes[]) {
/*
* Kills given number of processes
*
* n is number of processes to kill
* processes is an array of PIDs
*/
int i;
/* trying to interrupt processes gently */
for(i = 0; i < n; i++)
kill(processes[i], 15);
/* TODO: maybe we should use some delay here to let processes die gracefully :) */
/* killing everyone that left */
for(i = 0; i < n; i++)
kill(processes[i], 9);
}
void copy_pipe(pipe_t *dest, pipe_t *src) {
/*
* Copies file descriptors from source pipes to destination
*/
(*dest)[0] = (*src)[0];
(*dest)[1] = (*src)[1];
}
#ifndef LOKER_ROUTINES_H
#define LOKER_ROUTINES_H
#include <stddef.h> /* NULL */
#define AND(CMD1, STATUS1, LABEL, CMD2, RETVAL) \
STATUS1 = CMD1; \
if(STATUS1 != 0) \
{ \
RETVAL = STATUS1; \
goto LABEL; \
} \
\
RETVAL = CMD2; \
LABEL : ;
#define OR(CMD1, STATUS1, LABEL, CMD2, RETVAL) \
STATUS1 = CMD1; \
if(STATUS1 == 0) \
{ \
RETVAL = STATUS1; \
goto LABEL; \
} \
\
RETVAL = CMD2; \
LABEL : ;
int exec_command(char **);
int pipeline(int, char***);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment