Skip to content

Instantly share code, notes, and snippets.

@andycandrea
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andycandrea/8892228 to your computer and use it in GitHub Desktop.
Save andycandrea/8892228 to your computer and use it in GitHub Desktop.
A simple Linux shell and one of my first projects written in C. It gave me my first real experience with pointers, structs and memory management, as well as fork() and exec().
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
/*
* This program works as a simple Linux shell. Linux commands with up to four arguments are supported,
* as long as the input string does not exceed 128 characters. The parent process forks a child in order
* to tokenize the command and pass it as arguments to execvp(). Pipelining is not currently supported
* in this shell; attempts to do so will either result in an error message or the first command being
* executed by itself.
*/
#define MAX_ARGS 5 //Maximum number of arguments supported by my shell (counting the command)
#define MAX_CHARS 128 //Maximum number of input characters supported
/* This struct is simply used for congregation of various variables that are used as input for execvp(),
* as well as increasing manageability for myself while developing the shell.
*/
typedef struct {
char *execArgs[MAX_ARGS+1], *path; //MAX_ARGS+1 ensures final entry in execArgs is null
int count; //Number of arguments currently in command
}cmdStructure;
int main() {
while (1){
pid_t childPID;
char inputBuf[MAX_CHARS];
char *cmd;
// Allocating memory to prevent a seg fault:
cmdStructure *command = (cmdStructure*) malloc(sizeof(cmdStructure));
(*command).count = 1;
char* getVal;
// Basic shell prompt:
printf("%% ");
getVal = fgets(inputBuf,MAX_CHARS,stdin);
/* The following conditional is simply for formatting so that the % from my shell does not
* show up prior to the original shell's prompt and to end the program on EOF.
*/
if (getVal == NULL | &inputBuf[0] == NULL){
printf("\n");
return 0;
}
childPID = fork();
if (childPID == -1) {
perror("Error with fork()");
exit(EXIT_FAILURE); // For unexpected errors in fork()
}
if (childPID == 0) { // Code for child process
// Spaces, tabs and newlines are all invalid:
cmd = strtok(inputBuf," \t\n");
(*command).path = cmd;
(*command).execArgs[0] = cmd;
while (cmd != NULL){
if ((*command).count > MAX_ARGS) {
printf("\nToo many arguments.\n");
break;
}
cmd = strtok(NULL," \t\n");
(*command).execArgs[(*command).count] = cmd;
(*command).count++;
}
int execError = 0;
execError = execvp((*command).path, (*command).execArgs);
if (execError == -1){//70
perror("Error with execvp()");
exit(EXIT_FAILURE);
}
else { exit(EXIT_SUCCESS); }
}
else { // Code for parent process
wait(0);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment