I have created a shell in C and here is the code.I have also commented the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#define PATH "path=" //just a constant string
#define DELIM ":" //just a constant string
typedef void (*sighandler_t)(int);
char* cwdtemp = "";
int false=0;
/*structure of functions*/
char* readLine(char* line, size_t len);
void split(char* input, char *argv[]);
int checkpipe( char *input);
void run (char* argv[]);
void runPipe(char *argv[], char *argv2[]);
void setPath(char* inputLine);
char* getPath();
void changedir(char* dir);
char* strcasestr(const char *haystack, const char *needle);
void handle_signal(int signo);
int main(){
char* input=NULL; //string to save user input
char* input2; //string to save the 2nd command if theres a pipe
char *argv[50]; //array to sabe the user input after spliting by spaces
char *argv2[30]; //array to save the second command if thrs a pipe
char argv3[100]; //array for the use of getcwd function
int i; //varibale to save the index of the pipe
char* cwd; //variable to save the current working directory
while (1) { //infinite while loop
signal(SIGINT, SIG_IGN); //capture teh signal and ignore its default action
signal(SIGINT, handle_signal); //capture the signal and parse it to handle signal function
cwd=getcwd(argv3,100); // get the current working directory and save it to cwd
cwdtemp=cwd; //save current directory as string to cwdtemp
printf("%s",cwd); //print cwd
printf(":~mosh:~$ "); //show a prompt
input=readLine(input, 0); //read the user input and save it as a string(char array)
if(strcasestr(input, PATH) == input){ //strcasesrt function will check user input and see if it has "path="(substring matching)
setPath(input); //pass user input to setpath function
}else{
i=checkpipe(input); //saves the index of the ' | ' charactor in user input string
if(i==0){ //if theres no pipe
split(input, argv); //split the user input by spaces and save them in argv array
if(argv[0] == NULL) { //if user input is null continue the loop
continue;
}
/* exit this shell when "exit" is inputed */
if(strcmp(argv[0], "exit") == 0) {
exit(0);
}
if(strcmp(argv[0], "cd") == 0) { //if the user entered cd command(its not found in /bin)
changedir(argv[1]); //pass the name of the directory that should be changed to the changedir function
}
run(argv); //pass argv array to run function
/*checks if the command is valid*/
if(false==-1 ){
if(strcmp(argv[0], "exit") == 0){}
else if(strcmp(argv[0], "cd") == 0){}
else{
printf("no such mosh command\n");}
false=0;
}
}
else{ //if thers a pipe
input2=&input[i+1]; //points input2 to the memory location of the charactor after charactor '|'
input[i]='\0'; //adds the escape charatctor to the charactor before '|' charactor of the input and signal the end of input string
split(input, argv); //split input by spaces and save in argv array
split(input2, argv2); //split input2 by spaces and save in argv2 array
if(fork()){ //creates a child process
wait(NULL); //wait till the child process terminates
}
else{ //else execute the pipe in parent process
runPipe(argv, argv2);
}
}
}
}
}
/*split the input by space and fill to the given array*/
void split(char* input, char *argv[]){
char* p; //char array
int argc;
argc=0;
p=strtok(input, " "); //torkanize user input by spaces and save to p
while(p!=NULL){ //until thrs charactors in p
argv[argc]=p;
argc++;
p=strtok(NULL, " "); //points the torkanizer to the point where it stoped before and torkanize p
}
argv[argc]='\0'; //adds end of string to the last index of argv
}
/* read a line */
char* readLine(char* line, size_t len){
getline(&line, &len, stdin);
*(line+strlen(line)-1)='\0';
return line;
}
/*return the array index of | character if any, otherwise 0*/
int checkpipe( char *input){
int i = 0;
int returnvalue = 0;
for ( ; i < strlen ( input ) ; i++ ){
if ( input[i] == '|' ){ //find the index where '|' in userinput and retuen the index ,if not found return 0
returnvalue = i;
break;
}
}
return returnvalue;
}
/*execute a command without | in another process*/
void run (char* argv[]){
pid_t pid = fork(); //creats a fork and save its pid in pid variable
if(pid == -1) {
perror("fork");
exit(1);
} else if(pid == 0) { //if child process was created successfully
false=execvp(argv[0], argv); //execute the command by execcvp function
//exit(0);
} else {
wait(NULL); //wait till child process terminates
}
}
/*execute a command with a | in two processes*/
void runPipe(char *argv[], char *argv2[]){
int pfds[2]; //int array to be used in file descriptors
pipe(pfds); //creates the pipe
int stdin_bak = dup(0);
if (fork()==0) { //if the creation of fork successfull
close(1); //close current stdout
dup(pfds[1]); //duplicate stdout to the write end of the pipe
close(pfds[0]); //close stdin
printf("i'm child\n");
execvp(argv[0], argv); //execute teh command
exit(0);
} else { //else in parent
close(0); //close current stdin
dup(pfds[0]); //duplicate stdin to the read end of the pipe
close(pfds[1]); //close stdout
wait(NULL); //wait till child process terminates
execvp(argv2[0], argv2);//execute command
}
}
/*add a given path to the environmental variable PATH*/
void setPath(char* inputLine){
char* pathIn = &inputLine[strlen(PATH)]; //save the input path that was given by user(without path=)
char* oldPath = getenv("PATH"); //get the current path from getenv function and save to oldpath
int lenPath = strlen(oldPath) + strlen(pathIn) + 1 + 1;
char* pathOut = malloc(sizeof(char) * lenPath); //allocate memoery to pathout by malloc
strcat(pathOut, oldPath); //combine oldpath and pathout and save to pathout by strcat function
strcat(pathOut, DELIM); //combine oldpath and DELIM(constant string) and save to pathout
strcat(pathOut, pathIn); //combine oldpath and pathinand save to pathout
pathOut[lenPath - 1] = '\0';
setenv("PATH", pathOut, 1); //set the new path by setenv function
free(pathOut); //free the memory allocated by malloc
}
/*change directory if possible*/
void changedir(char* dir){
if(dir=='\0'){ //if user entered nothing after cd
chdir( "/home/" ); //change to home
}else{
if(chdir(dir)==-1){ //if chdir function returned error
printf("%s:Cannot find the directory\n",dir);
}
}
}
void handle_signal(int signo)
{
printf("\n");
printf("%s",cwdtemp);
printf(":~mosh:~$ ");
fflush(stdout); //flush the stdout or printf will be in sameline
}
Here is my problem.
After compiling this code I have given a command like "hdgdg" which is a wrong one.So the output was "command not found".But when I run a command like "hakka|akjk"(which is a pipe command),I've got an infinite loop as the output.So how Can I solve this?
Thank you.
Related
I'm trying to develop a program that will initiate 2 child processes to run and these child processes will read all the files in one directory and pass the information to another child to create those files in another directory.
Below is my code for the same. But I'm not able to read messages from the pipe. If I pass a hardcoded string object, I'm able to read that from the pipe. But not the struct object I'm trying to pass.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //Header file for sleep(). man 3 sleep for details.
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#define BUF_SIZE 1024
// Struct to store the file name and content
struct FileDetails{
char *file_name;
char *file_content;
};
// Helper function to create file at a given path and with given content.
void * createFile(struct FileDetails fileDetail){
int status;
FILE *fptr;
fptr = fopen(fileDetail.file_name,"a");
if(fptr == NULL)
{
perror("Error!");
return NULL;
}
fprintf(fptr,"%s",fileDetail.file_content);
fclose(fptr);
return NULL;
}
//Helper function used to copy two strings into one and return new string
char* strAppend(char* str1, char* str2){
char * new_str ;
if((new_str = malloc(strlen(str1)+strlen(str2)+1)) != NULL){
new_str[0] = '\0'; // ensures the memory is an empty string
strcat(new_str,str1);
strcat(new_str,str2);
} else {
printf("%s","malloc failed!\n");
// exit?
}
return new_str;
}
// Each child will execute this helper funtion. It takes one argument which is the location of the directory to read all files from and send data to other child.
// Second argument is the pipe where we write
void *threadUtility(void *vargp, int* fd1)
{
// Store directory pointer
DIR* directoryPtr;
// Store file pointer
FILE *entry_file;
struct dirent *current_file;
int i;
char* directory = (char *)vargp;
printf("Process started for directory: %s\n", directory);
directoryPtr = opendir(directory);
if(directoryPtr==NULL) {
printf("Error! Unable to read directory: %s\n", directory);
exit(1);
}
close(fd1[0]); // Close reading end of first pipe
while((current_file=readdir(directoryPtr)) != NULL) {
if (!strcmp (current_file->d_name, "."))
continue;
if (!strcmp (current_file->d_name, ".."))
continue;
struct FileDetails fileDetail;
fileDetail.file_name = malloc(strlen(current_file->d_name) + 1);
fileDetail.file_name = current_file->d_name;
// Appending directory path to file name to read contents
char * new_str = strAppend(directory, current_file->d_name);
//printf("FileName FQN: %s\n", fileDetail.file_name);
entry_file = fopen(new_str, "r");
struct stat sb;
stat(new_str, &sb);
char *file_contents = malloc(sb.st_size);
fileDetail.file_content = malloc(sb.st_size);
if (entry_file != NULL) {
//Looping through each line of file to read all lines.
while (fscanf(entry_file, "%[^\n] ", file_contents) != EOF) {
fileDetail.file_content = strAppend(fileDetail.file_content, file_contents) ;
fileDetail.file_content = strAppend(fileDetail.file_content, "/n") ;
}
} else{
perror("Failed: ");
exit(1);
}
fclose(entry_file);
//printf("Writing to pipe: %s\n", fileDetail.file_name);
if (write(fd1[1], &fileDetail, sizeof(struct FileDetails)) < 0) {
printf("error writing");
}
}
close(fd1[1]);
closedir(directoryPtr);
printf("Process ended for directory: %s\n", directory);
return NULL;
}
// This program takes two command line argument which are direectory paths of two location.
int main( int argc, char *argv[] )
{
// If 2 paths not provided the program will terminate
if( argc != 3 ) {
printf("Provide two directory paths: %i\n", argc);
exit(1);
}
//Pipes for communication
int fd1[2]; // Used to store two ends of first pipe
int fd2[2]; // Used to store two ends of second pipe
//Create pipes
if (pipe(fd1)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(fd2)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
// Process ids for two child processes
pid_t dir_one_child_process, dir_two_child_process;
dir_one_child_process = fork();
if (dir_one_child_process == 0) {
/* Child One Process */
threadUtility(strAppend(argv[1],"/"), fd1);
wait(NULL);
// Reading messages sent by 2nd child
close(fd2[1]); // Close writing end of second pip
// Read string from child, print it and close
// reading end.
struct FileDetails readForSecond;
int n_bytes;
while (read(fd2[0], &readForSecond, sizeof(&readForSecond)) > 0) {
printf("From Child One: %s\n", readForSecond.file_name);
// Here you have to call the createFile function in order to create the file once you get the FileDetail object
//I'm stuck here
//sleep(1);
}
printf("From Child Two: %s\n", readForSecond.file_name);
close(fd2[0]);
} else {
dir_two_child_process = fork();
if (dir_two_child_process == 0) {
/* Child Two Process */
close(fd1[1]); // Close writing end of first pipe
struct FileDetails readForFirst;
int n_bytes;
while (read(fd1[0], &readForFirst, sizeof(&readForFirst)) > 0) {
printf("From Child One file_name: %s\n", readForFirst.file_name);
// Here you have to call the createFile function in order to create the file once you get the FileDetail object
//I'm stuck here
//sleep(1);
}
printf("reading ends:");
// Close both reading ends
close(fd1[0]);
threadUtility(strAppend(argv[2],"/"), fd2);
} else {
/* Parent Code waiting for two childs to end*/
waitpid(dir_one_child_process, NULL, 0);
waitpid(dir_two_child_process, NULL, 0);
printf("Main Function Ends. Exiting\n");
}
}
exit(0);
}
Here it one bug:
write(fd1[1], &fileDetail, sizeof(struct FileDetails))
sizeof(struct FileDetails) is not the size of the data that you have stored using the pointers in fileDetail. Since struct FileDetails is
struct FileDetails{
char *file_name;
char *file_content;
};
it's simply the size of two pointers (which is typically 2x4 or 2x8).
You can check the size using:
printf("%zu\n", sizeof(struct FileDetails));
So it looks as-if you sending two pointer values instead of the content of the files. That's not what you want.
Further, this is wrong:
read(fd2[0], &readForSecond, sizeof(&readForSecond))
^^^^^^^^^^^^^^^^^^^^^
This is size-of a single pointer
so I'm working on a simulated linux shell using C (homework), and I have it exiting the program if the user just simply types in "quit".
However, if they throw "exit" somewhere inside a command... "cat file ; exit"
I need it to execute the command like normal, then quit.
I know that I still have to filter the string entered and strip it of the exit, but for now, I'm just simply trying to get it to recognize the substring of "exit" using strstr() and to exit the program.
It just keeps looping if input contains a string with substring "exit" currently.
Thank you.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define bSize 1000
void driveLoop();
char *userInput(void);
int main(int argc, char **argv){
driveLoop();
return 0;
}
void driveLoop(void){
char *comTokens[100];
char *tempTokens;
char *command;
char *cd;
char *cdDir;
char *cdTemp;
char cdBuf[bSize];
char checkExit[] = "exit";
for (;;){
printf("> ");
command = userInput();
if (!command)
break;
char *exitPtr = strstr(command, checkExit); // using strstr on the pointer containing the input
int i = 0;
tempTokens = strtok(command, " \t\n");
while (tempTokens && i < 99){
comTokens[i++] = tempTokens;
tempTokens = strtok(NULL, "\t\n");
}
if (strcmp(comTokens[0], "exit") == 0){ // this exit is working normally
exit(0);
}
if (strcmp(comTokens[0], "cd") == 0){
cd = getcwd(cdBuf, sizeof(cdBuf));
cdDir = strcat(cd, "/");
cdTemp = strcat(cdDir, comTokens[1]);
continue;
}
comTokens[i] = NULL;
pid_t cFork = fork();
if (cFork == (pid_t) - 1){
perror("fork");
}
else if (cFork == 0){
execvp(comTokens[0], comTokens);
perror("exec");
if (exitPtr != NULL){ // here's where I'm calling the exit
exit(0); // nothing happens though. It just keeps looping.
}
}
else {
int status;
waitpid(cFork, &status, 0);
}
}
}
char *userInput(void){
char *input = NULL;
size_t size = 0;
getline(&input, &size, stdin);
return input;
}
else if (cFork == 0){
execvp(comTokens[0], comTokens);
perror("exec");
if (exitPtr != NULL){ // here's where I'm calling the exit
exit(0); // nothing happens though. It just keeps looping.
}
}
execvp doesn't return if it succeeds, so your if will normally never be executed. (And if execvp does return, you probably want to exit the child unconditionally, preferably with _exit().)
What you really want is for the parent process to exit. So you probably wanted to put this code in the parent branch of the fork:
else {
int status;
waitpid(cFork, &status, 0);
// add it here
if (exitPtr != NULL){
exit(0);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFER 64
char *read_command(void);
char **parse_line(char *line);
int execute(char **arguments);
int main(void)
{
char *command = NULL;
char **arguments;
int status;
do
{
printf("protoulis_7968> ");
command = read_command();
arguments = parse_line(command);
status = execute(arguments);
free(arguments);
free(command);
}while(status);
}
char *read_command(void)
{
char *command = NULL;
ssize_t buf = 0;
getline(&command, &buf, stdin);
return command;
}
char **parse_line(char *line)
{
int buffer = BUFFER;
int pos = 0;
char **tokens = malloc(buffer * sizeof(char*));
char *token;
if (!tokens)
{
printf("Error allocating memory with malloc\n");
exit(0);
}
token = strtok(line, " \t\r\n\a");
while(token != NULL)
{
tokens[pos] = token;
pos++;
if (pos >= buffer)
{
buffer += BUFFER;
tokens = realloc(tokens, buffer * sizeof(char*));
if (!tokens)
{
printf("Error reallocating memory!\n");
exit(0);
}
}
token = strtok(NULL, " \t\r\n\a");
}
tokens[pos] = NULL;
return tokens;
}
int execute(char **arguments)
{
// printf("%*c\n", arguments);
int pid, waitPid, status;
pid = fork();
if(pid == 0) //child process
{
if (execvp(arguments[0], arguments) == -1)
perror("Error with EXECVP\n");
}
else if (pid < 0)
perror("Error PID < 0\n");
else //parent process
{
do
{
waitPid = waitpid(pid, &status, WUNTRACED);
}while(!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
Well, I have written the above code simulating a command line interpreter in C. I would like to be able to execute multiple commands by entering them in one line. I mean I want to pass as input for example the line: ls -l ; touch hello.c ; pwd. Having passed this entire line I want to separate the commands by the semicolon and let the system execute each command in any order. I believe I have to use the strtok function but have done many attempts and managed nothing. Any help would be really appreciated!
strtok will not suffice in your case. The reason is that it will take you to the next subcommand but to be able to execute this subcommand, you have to have it as a single string.
Two ways to solve this issue:
Count how many ';' there is, replace them by '\0' to have several contiguous strings in memory, then execute them one by one.
Write a function that splits your command string into a 2d array of subcommands, then execute them one by one.
Here's a code that does that if you need some inspiration:
Using linked lists: https://github.com/yoones/chelpers/blob/master/src/split.c
2d array version: https://github.com/yoones/hsn/blob/master/src/tools/split.c
I have to develop a simple shell in C using system calls fork()/execvp(). So far my code takes in a command, splits it up using strtok into an array argv and then I call fork to create a child and execute the command. Im working on this in ubuntu where most of the commands are in the /bin/ directory, so I append the program name (for example /bin/ls) and use that for the first arg of execvp and then I give it the argv array. My program works if I type in the command "ls", but when trying other commands or even "ls -l" I'm getting an "ls: invalid option." Im not sure what I'm doing wrong here.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_LEN 1024
int main(){
char line[BUFFER_LEN]; //get command line
char* argv[100]; //user command
char* path= "/bin/"; //set path at bin
char progpath[20]; //full file path
int argc; //arg count
while(1){
printf("My shell>> "); //print shell prompt
if(!fgets(line, BUFFER_LEN, stdin)){ //get command and put it in line
break; //if user hits CTRL+D break
}
if(strcmp(line, "exit\n")==0){ //check if command is exit
break;
}
char *token; //split command into separate strings
token = strtok(line," ");
int i=0;
while(token!=NULL){
argv[i]=token;
token = strtok(NULL," ");
i++;
}
argv[i]=NULL; //set last value to NULL for execvp
argc=i; //get arg count
for(i=0; i<argc; i++){
printf("%s\n", argv[i]); //print command/args
}
strcpy(progpath, path); //copy /bin/ to file path
strcat(progpath, argv[0]); //add program to path
for(i=0; i<strlen(progpath); i++){ //delete newline
if(progpath[i]=='\n'){
progpath[i]='\0';
}
}
int pid= fork(); //fork child
if(pid==0){ //Child
execvp(progpath,argv);
fprintf(stderr, "Child process could not do execvp\n");
}else{ //Parent
wait(NULL);
printf("Child exited\n");
}
}
}
The invalid option is because fgets() keeps the '\n' when you press enter, try this
if(!fgets(line, BUFFER_LEN, stdin))
break;
size_t length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
when you are trying to call ls -l you are passing "-l\n" as the option, hence the message.
You will have to change
strcmp(line, "exit\n")
to
strcmp(line, "exit")
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define BUFFER_LEN 1024
int main(){
char user_input[BUFFER_LEN]; //get command line
char* argv[120]; //user command
int argc ; //argument count
char* path= "/bin/"; //set path at bin
char file_path[50];//full file path
while(1){
printf("Simple Shell>> "); // Greeting shell during startup
if(!fgets(user_input,BUFFER_LEN, stdin)){
break; //break if the command length exceed the defined BUFFER_LEN
}
size_t length = strlen(user_input);
if(length == 0){
break;
}
if (user_input[length - 1] == '\n'){
user_input[length - 1] = '\0'; // replace last char by '\0' if it is new line char
}
//split command using spaces
char *token;
token = strtok(user_input," ");
int argc=0;
if(token == NULL){
continue;
}
while(token!=NULL){
argv[argc]=token;
token = strtok(NULL," ");
argc++;
}
argv[argc]=NULL;
strcpy(file_path, path); //Assign path to file_path
strcat(file_path, argv[0]); //conctanate command and file path
if (access(file_path,F_OK)==0){ //check the command is available in /bin
pid_t pid, wpid;
int status;
pid = fork();
if (pid == 0) { //child process
if (execvp(file_path,argv) == -1) {
perror("Child proccess end");
}
exit(EXIT_FAILURE);
}
else if (pid > 0) { // parent process
wpid = waitpid(pid, &status, WUNTRACED);
while (!WIFEXITED(status) && !WIFSIGNALED(status)){
wpid = waitpid(pid, &status, WUNTRACED);
}
}
else {
perror("Fork Failed"); //process id can not be null
}
}
else {
printf("Command is not available in the bin\n"); //Command is not available in the bin
}
}
}
I am writing a C program to emulate a simple shell. This shell will basically evaluate commands like any other shell (ls, cat, etc.), as well as handle pipelining and redirection.
Currently, I am trying to start out by getting user input, tokenizing it, and executing the command provided (e.g. executing only "ls" and not "ls -l"). However, I am having a lot of difficulty with the forking. It seems that every time I fork, something goes wrong and hundreds of identical processes are created, leading to my computer freezing and me having to restart. The code appears to be correct, but I have no idea what is causing this behaviour. Below is the relevant portion of my code (main method and input tokenizer method).
int main() {
char inputLine[512]; //user input
char *args[10]; //arguments
char* pathVar = "/bin/";//path for argument
char programPath[512]; //pathVar + args[0]
int n; //count variable
//loop
while (1) {
//print prompt, get input
printf("input> ");
fgets(inputLine, 512, stdin);
n = tokenizer(inputLine, args);
//fork process
pid_t pid = fork();
if (pid != 0) { //if parent
wait(NULL);
} else { //if child
//format input for execution
strcpy(programPath, pathVar);
strcat(programPath, args[0]);
//execute user command
int returnVal = execv(programPath, args);
}
}
return 0;
}
int tokenizer(char *input, char *args[]) {
char *line; //current line
int i = 0; //count variable
line = input;
args[i] = strtok(line, " ");
do {
i++;
line = NULL;
args[i] = strtok(line, " ");
} while (args[i] != NULL);
return i;
}
Putting it all together:
You need to check fork and execv for failure.
You should exit after an execv failure (and perhaps after a fork failure).
And you need to add \n to the strtok delimiters (or remove the newline from the input line in some other way).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXARGS 10
#define PATH "/bin/"
int main() {
char inputLine[BUFSIZ];
char *args[MAXARGS];
char programPath[BUFSIZ + sizeof(PATH) + 10];
while (1) {
printf(":-> ");
if (fgets(inputLine, BUFSIZ, stdin) == NULL) /* ctrl-D entered */
break;
tokenize(inputLine, args);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid != 0) { /* parent */
wait(NULL);
} else { /* child */
strcpy(programPath, PATH);
strcat(programPath, args[0]);
execv(programPath, args); /* will not return unless it fails */
perror("execv");
exit(EXIT_FAILURE);
}
}
return 0;
}
int tokenize(char *input, char *args[]) {
int i = 0;
args[0] = strtok(input, " \n");
for (i = 0; args[i] && i < MAXARGS-1; ++i)
args[++i] = strtok(NULL, " \n");
return i;
}
You should check that execv doesn't fail and also be sure to exit() at the end of the child block.
//execute user command
int returnVal = execv(programPath, args);
// check return from execv
if (returnVal < 0) {
perror("execv");
exit(1);
}
Also, beware using functions like strcpy in this context since they may lead to buffer overflows. If an untrusted attacker type is talking to your shell this type of security issue could let them break out of the "sandbox".