Enrico Franchi Homepage

Questo sito è obsoleto. La nuova versione di questa pagina è disponibile presso akropolix

This site is old. The new version of this page is available on akropolix

C Simple Shell

There are other very simple shells I've done. You can see how in many cases the logic is the same, the syscalls are the same, but some languages need a lot more code to do auxiliary things like parsing a line to obtain command and arguments and such things. In fact you can have a look at how works parse_arguments here

csimpleshell.c (download)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16

void parse_args(char *buffer, char** args, 
                size_t args_size, size_t *nargs)
{
    char *buf_args[args_size]; /* You need C99 */
    char **cp;
    char *wbuf;
    size_t i, j;
    
    wbuf=buffer;
    buf_args[0]=buffer; 
    args[0] =buffer;
    
    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    }
    
    for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }
    
    *nargs=j;
    args[j]=NULL;
}


int main(int argc, char *argv[], char *envp[]){
    char buffer[BUFFER_SIZE];
    char *args[ARR_SIZE];

    int *ret_status;
    size_t nargs;
    pid_t pid;
    
    while(1){
        printf("$ ");
        fgets(buffer, BUFFER_SIZE, stdin);
        parse_args(buffer, args, ARR_SIZE, &nargs); 

        if (nargs==0) continue;
        if (!strcmp(args[0], "exit" )) exit(0);       
        pid = fork();
        if (pid){
            printf("Waiting for child (%d)\n", pid);
            pid = wait(ret_status);
            printf("Child (%d) finished\n", pid);
        } else {
            if( execvp(args[0], args)) {
                puts(strerror(errno));
                exit(127);
            }
        }
    }    
    return 0;
}

A part from the shell code itself, the most interesting function is parse_args. You may want to understand how it works and save and use it in other projects (but before consider using python, have a look at the python shell). Lets have a look at the core of the program:

csimpleshell.c (download)
1:         if (pid){
2:             printf("Waiting for child (%d)\n", pid);
3:             pid = wait(ret_status);
4:             printf("Child (%d) finished\n", pid);
5:         } else {
6:             if( execvp(args[0], args)) {
7:                 puts(strerror(errno));
8:                 exit(127);
9:             }
10:         }

fork() creates another process identical to current process. In the parent process it returns the child PID, in the child returns 0.
This way you can split child and parent code.

execvp uses $PATH to search the executable pointed by the first argument. The second is the list of parameters it will be called with. The last one should be NULL, the first one the process name. The new executable replaces the current one (so it does not return to this code).
wait() does wait for a child to terminate (for more informations, here). Since there is only one child at time, we don't need to determine which one terminated.
execvp() is part of the family of exec*. There are many similar funcions, they are all meant to run another process.

back

xhtml 1.1 CSS 2.1 RSS 2.0
Made with a Mac Made with BBEdit Made with Brain

All documentation is under FDL and all source code is under BSD, unless differently stated.

24-mar-06