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
}
}
}
Related
I am creating a Linux type shell program for a school project. So far I have implemented the basic Linux external commands like "ls","ps", etc., using execvp and basic pipes. As part of the project, the user can either run the program in interactive mode or batch mode. In interactive mode the user just enters a command when prompted. For batch mode, the user specifies a file in the command line where there is a list of commands to execute.
The problem I am having is in batch mode. In batch mode, if an invalid command is listed (e.g. "kdfg", for which "kdfg: command not found" while be the output), everything afterward continues, but everything afterward is executed twice. so if I have a "kghd" on one line and the next line is "ls", then the "ls" command will be executed twice. I've literally been looking at my code for hours and have tried a bunch of stuff, but to no avail.
My code is displayed below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include<sys/wait.h>
#include<unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char* InputString();
char** GetArgs(char* com);
void Free(char** args);
char** GetCommands(char** line);
void PipedCommands(char* line);
int batch = 0; //Acts as bool for if there is a batch file given at command line
FILE* bFile; //This is just to make a quick tweek to the file if necssary to prevent any undefined behavior and keep track of where we are in the fil.
int b_fd;
int stdin_cpy;
int main(int argc, char** argv)
{
pid_t pid;
int status;
int fd;
int exitCom = 0; //acts as bool to check if an exit command was given.
char* line;
if(argc > 1) //check if batch file was given.
{
/*
if(freopen(argv[1], "r", stdin) == NULL) //I added this in case the file isn't found
{
printf("\nCould not open \"%s\". Terminated\n\n", argv[1]);
exit(1);
}
*/
//The following is to append a newline at the end of the file (if there isn't one).
//For some reaosn, there seems to be some undefined behavior if the input file isn't
//stricitly ended with a newline.
bFile = fopen(argv[1], "r+"); //open for reading and updating
if(bFile == NULL)
{
printf("\nCould not open \"%s\". Terminated\n\n", argv[1]);
exit(1);
}
fseek(bFile, -1, SEEK_END); //go to last character of file
if(fgetc(bFile) != '\n') //If last character is not a newline, append a newline to the file.
{
fprintf(bFile, "\n");
}
fclose(bFile); //close the file.
bFile = fopen(argv[1], "r"); //open file to keep track of when it ends
b_fd = open(argv[1], O_RDONLY); //open file again (with file descriptor this time) to duplicate it to stdin
stdin_cpy = dup(fileno(stdin)); //keep track of stdin file.
dup2(b_fd, 0); //duplicate to stdin so program takes input from bFile
close(b_fd);
batch = 1;
}
//int i=0; //this was used for debugging purposes
while(1)
{
printf("\n");
char** coms = GetCommands(&line);
for(int i=0; coms[i] != NULL; ++i) //loop goes through each command returned from GetCommands(...)
{
//fork and wait.
pid = fork();
wait(&status);
if(pid == 0)
{
int pipedCommand = 0;
//printf("\ncoms[%d]: %s\n", i, coms[i]);
for(int j=0; j<strlen(coms[i]); ++j)
{
if(coms[i][j] == '|')
{
pipedCommand = 1;
break;
}
}
if(pipedCommand == 1)
{
PipedCommands(coms[i]);
exit(1);
}
char** args = GetArgs(coms[i]);
//printf("\nargs[0]: %s\n", args[0]);
if(strcmp(args[0],"exit") == 0)
{
exit(5); //if exit command was given, exit status will be 5 (I just used 5 becuse I felt like it).
}
//printf("\nNo exit\n");
printf("\n");
execvp(args[0],args);
printf("%s: command not found\n", args[0]);
exit(1); //Normal exit exits with 1 or 0.
}
//Parent continues after child exits
else if(pid > 0)
{
//check exit status of child
if(WEXITSTATUS(status) == 5)
exitCom = 1; //set bool exitCom to 1 (true), indicating that the exit command was given
}
}
if(pid > 0)
{
free(line);
free(coms);
//Now that all commands in the line were executed, check exitCom and if it is 1 (exit command was given), the shell can now exit.
if(exitCom == 1)
{
printf("\n");
exit(0);
}
}
/*
if(i >= 5)
{
printf("\nFORCED EXIT\n"); //this was used for debugging purposes
exit(1);
}
++i;
*/
}
return 0;
}
char* InputString()
{
int len = 20;
char* str = (char*)malloc(sizeof(char)*len);
char* buff;
unsigned int i=0;
if(str != NULL)
{
int c = EOF;
//printf("%c", fgetc(bFile));
while( ((c = getchar()) != '\n') && (c != EOF) )
{
/*
//printf("%c", fgetc(bFile));
//fgetc(bFile);
if(feof(bFile))
{
printf("\n\nEnd of the line\n\n");
}
*/
str[i++] = (char)c;
if(i == len)
{
len = len*2;
str = (char*)realloc(str,sizeof(char)*len);
}
}
str[i] = '\0';
buff = (char*)malloc(i);
}
if(batch == 1)
{
if(fgets(buff, i, bFile) == NULL) //Once the end of file has been reached
{
dup2(stdin_cpy, 0); //revert input back to original stdin file so user can now enter commands interactively (this happens if exit command was not given)
close(stdin_cpy); //close stdin_copy
fclose(bFile); //close bFile as we have reached the end of it
batch = 0;
}
}
printf("\n");
return str;
}
//User enters a line of commands (1 or more). Commands are separated with a ';' being the delimeter.
char** GetCommands(char** line)
{
char** coms = (char**)malloc(sizeof(char*));
char delim[] = ";";
if(batch == 0)
printf("prompt> ");
fflush(stdout);
*line = InputString();
if(batch == 1)
printf("%s\n", *line);
strcat(*line, ";");
int i=0;
coms[i] = strtok(*line, delim);
while(coms[i] != NULL)
{
++i;
coms = (char**)realloc(coms, sizeof(char*) * (i+1));
coms[i] = strtok(NULL, delim);
//printf("\ni: %d\n", i);
}
return coms;
}
//A command obtained from GetCommands(...) is separated into various arguments with a space, ' ', being the delimiter.
char** GetArgs(char* com)
{
char** args = (char**)malloc(sizeof(char*));
char delim[] = " ";
//printf("\nline: %s\n", line);
int i=0;
args[i] = strtok(com, delim);
while(args[i] != NULL)
{
++i;
args = (char**)realloc(args, sizeof(char*) * (i+1));
args[i] = strtok(NULL, delim);
}
return args;
}
void PipedCommands(char* line)
{
char** coms = (char**)malloc(sizeof(char*));
int numComs;
char delim[] = "|";
int i=0;
coms[i] = strtok(line, delim);
while(coms[i] != NULL)
{
++i;
coms = (char**)realloc(coms, sizeof(char*) * (i+1));
coms[i] = strtok(NULL, delim);
}
numComs = i;
int fd[2];
pid_t pid;
int status;
int prev_p = 0;
// printf("\nnumComs: %d\n", numComs);
for(int i=0; i<numComs; ++i)
{
//printf("\ni: %d\n", i);
pipe(fd);
pid = fork();
wait(&status);
if(pid == 0)
{
//printf("\nChild\n");
if(i < numComs-1)
{
//printf("\ni < numComs-1\n");
//printf("%s", coms[i]);
//printf("coms[%d]: %s", i, coms[i]);
//printf("\nBefore dup2\n");
char** args = GetArgs(coms[i]);
//printf("\nexecvp in if\n");
if(prev_p != 0)
{
dup2(prev_p, 0);
close(prev_p);
}
dup2(fd[1], 1);
close(fd[1]);
execvp(args[0],args);
printf("%s: command not found\n", args[0]);
exit(3);
}
else
{
//printf("\nelse\n");
//printf("coms[%d]: %s", i, coms[i]);
//printf("\nBefore dup2 in else\n");
if(prev_p != 0)
{
dup2(prev_p, 0);
close(prev_p);
}
//close(fd[0]);
close(fd[1]);
char** args = GetArgs(coms[i]);
printf("\n");
execvp(args[0],args);
printf("%s: command not found\n", args[0]);
exit(3);
}
}
close(prev_p);
close(fd[1]);
prev_p = fd[0];
if(WEXITSTATUS(status) == 3)
{
close(fd[0]);
close(prev_p);
close(fd[1]);
return;
}
}
close(fd[0]);
close(prev_p);
close(fd[1]);
}
You can probably ignore the PipedCommands(...) function as I do not think the problem lies there.
Below is a simple batch file:
kldfg
whoami
Below is the output using the above batch file
kldfg
kldfg: command not found
whoami
jco0100
whoami
jco0100
The whoami command should only execute once, but it appears to execute twice. After that, the program reverts to interactive mode as it should and everything runs fine from there. Does anyone have any idea why this is happening. This only happens when an unknown command is entered. If all commands in the batch file are valid, nothing is outputted twice. It's only for batch files that have an unknown command that all commands after the unknown one are outputted twice.
Here's another example:
Batch file:
date
kldfg
whoami; ls | wc -l
date | wc -c
Output:
date
Tue Apr 13 19:43:19 CDT 2021
kldfg
kldfg: command not found
whoami; ls | wc -l
jco0100
34
date | wc -c
29
whoami; ls | wc -l
jco0100
34
date | wc -c
29
I got it working by disconnecting stdin on the child process before running the command:
...
freopen("/dev/null", "r", stdin); // disconnect
execcvp(args[0], args);
...
From this link: If I fork() and then do an execv(), who owns the console?
I had a crack at debugging it. To get you started on that road:
Compile your C program with debugging symbols:
$ gcc --debug your-program.c
Now debug your C program:
$ gdb a.out
This start a gdb interactive shell.
In gdb itself:
(gdb) list
(gdb) set follow-fork-mode parent
(gdb) breakpoint 69
list your code
tell the debugger to follow the parent when fork()
set a breakpoint at line 69
Run the program:
(gdb) run batch.txt
It will pause at line 69. Execute next line:
(gdb) next
Print a variable:
(gdb) print *coms
Continue running:
(gdb) continue
I leave the rest for you to explore.
I'm still not sure what's wrong with it. Something strange happens in InputString() after your fork fails with an unknown command. InputString() begins returning duplicates from getchar().
I didn't even know you could do that with stdin. Maybe just read from the file in a normal fashion, rather than clobbering stdin, and see if the problem goes away.
Don't write linux code much, I am trying to do more of that.
I took the fork and wait commands out so I could build it in mingw64 (because they arent supported in windows builds) and can't seem to reproduce the issue.
So I think the issue is in the multi-threading setup you have going there.
The "pid" variable is shared between every fork. which means when the fork command is called "pid" is set to whatever the last fork in the loop returned.
It looks like you are using an if statement checking the "pid" variable to see if this thread can execute the command. But wouldn't the main thread keep running right through that?
I don't know what fork() returns but "pid" is uninitialized, don't know if that matters.
Maybe this helps?
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);
}
}
I am writing a program to implement a simple shell in C
but when I execute the program at the end when I type exit it does not exit.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.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
size_t length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
break; //if user hits CTRL+D break
}
if (strcmp(line, "exit") == 0) { //check if command is exit
exit(0);
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");
}
}
}
I am executing in on cygwin hence a.exe is generated.
execution of program
$ ./a.exe
My shell>> ls
ls
a.exe p3.4.cpp p3.6.cpp p3.6.cpp~ p3.7.cpp p3.7.cpp~ p3.cpp shell.c shell.c~ test.txt
Child exited
My shell>> cat test.txt
cat.txt
Child process could not do execvp
My shell>> exit
exit
Child process could not do execvp
My shell>> exit()
exit()
Child process could not do execvp
My shell>> exit
exit
Child process could not do execvp
My shell>> exit
exit
Here you can see in the above output the program is not able to exit when I am typing an exit on the command prompt. What is the mistake I am doing in above program?
You probably mixed removing the new line with the handling of CTRL-D. Your code
if (!fgets(line, BUFFER_LEN, stdin)) { //get command and put it in line
size_t length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
break; //if user hits CTRL+D break
}
removed the new line only if fgets fails, e.g. end of file has been reached.
Also, line[length-1] will yield undefined behaviour if length==0.
I'd replace the logic:
if (!fgets(line, BUFFER_LEN, stdin)) { //get command and put it in line
break; //if user hits CTRL+D break
} else {
line[strcspn(line, "\n")] = '\0';
}
strcmp(line, "exit\n")==0
What happens when you do not include the newline (\n) ?
I am implementing a simple shell in c for a class. There are a number of requirements but the one thing that I am concerned about is this sequence of commands:
ls > test
wc < test
which will output the results of the ls command to the file test and the wc
command will then count the number of words, bytes, characters (or something
like that) in that file.
Anyway, the first command works and the test file is successfully created with
the expected content. The wc command doesn't work however. It triggers the error associated with the execv statement "Command can't be executed. My input redirection works as a command like:
grep test < test works perfectly. My question is, Why doesn't my shell recognize the wc command?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
void parse(char buffer[2048], char *arguments[512]){
//these characters(space, tab, new line, return)represent white space
//that separate words
char * delim = " \t\r\n\f";
char * token;
int count = 0;
//Finds the first word in buffer
token = strtok (buffer, delim);
//While a token still exists
while (token != NULL){
//if token is not empty it is added to arguments
if (strlen (token) > 0){
arguments[count]=token;
count++;
}
//Find the next token.
token = strtok (NULL, delim);
arguments[count+1]=NULL;
}
int checkInput(char *arguments[512]){
int loc = 0;
int count = 0;
while (arguments[count]!=NULL){
if (strcmp(arguments[count],"<")==0){
loc = count;
}
count++;
}
return loc;
}
void redirectInput(int input,char *arguments[512]){
int in;
int in2;
char*inFile = arguments[input+1];
in = open(inFile, O_RDONLY);
if (in < 0){
perror("Error Opening File");
exit(1);
}
in2 = dup2(in, 0);
if (in2 < 0){
perror("Error redirecting stdin");
exit(1);
}
close(in);
}
int checkOutput(char *arguments[512]){
int loc = 0;
int count = 0;
while (arguments[count]!=NULL){
if (strcmp(arguments[count],">")==0){
loc = count;
}
count++;
}
return loc;
}
void redirectOutput(int output,char *arguments[512]){
int out;
int out2;
char*outFile=arguments[output+1];
out = open(outFile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
if (out < 0){
perror("Error Opening File");
exit(1);
}
out2 = dup2(out, 1);
if (out2 < 0){
perror("Error redirecting stdout");
exit(1);
}
close(out);
}
int main(int argc, char **argv){
//buffer is to hold the commands that the user will type in
char buffer[512];
char buffer2[512];
//bin/program_name is the arguments to pass to execv
//if we want to run ls, "/bin/ls" is required to be passed to execv()
char* path = "/bin/";
char * arguments[512];
char * args_copy[512];
//This will be the final path to the program is passed to execv
char prog[512];
char directory[512];
pid_t pid,w;
int status;
int isValid;
int input;
int output;
getcwd(directory,sizeof(directory));
while(1){
isValid = 0;
while(!isValid){
//print the prompt
printf(":");
fflush(stdout);
//get input
fgets(buffer, 512, stdin);
if(buffer[0]!='\n' && buffer[0] != '#'){
isValid=1;
}
}
strcpy(buffer2, buffer);
parse(buffer2, args_copy);
//Handle the builtin functions without before forking
if (strcmp(args_copy[0],"exit")==0){
exit(0);
}
else if (strcmp(args_copy[0],"status")==0){
printf("exit value %d\n",WEXITSTATUS(status));
}else if( strcmp(args_copy[0],"cd") == 0 ){
if(args_copy[1]==NULL){
chdir(directory);
}
else{
chdir(args_copy[1]);
}
}
else{
//fork!
pid = fork();
//Error checking to see if fork works
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
//If pid !=0 then it's the parent
if(pid!=0){
do {
w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
}
else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
}
else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
}
else if (WIFCONTINUED(status)) {
printf("continued\n");
}
}while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
else{
//parse the user input into an array of strings(arguments)
parse(buffer, arguments);
input = checkInput(arguments);
if (input){
redirectInput(input,arguments);
arguments[input]=NULL;
}
output = checkOutput(arguments);
if (output){
redirectOutput(output,arguments);
arguments[output]=NULL;
}
//First we copy a /bin/ to prog
strcpy(prog, path);
//Then we concancate the program name to /bin/
//If the program name is ls, then it'll be /bin/ls
strcat(prog, arguments[0]);
//pass the prepared arguments to execv and we're done!
int rv=execv(prog, arguments);
if (rv == -1) {
perror("Command can't execute");
exit(EXIT_FAILURE);
}
}
}
}
return 0;
}
This happens because wc is /usr/bin/wc, not /bin/wc.
You can use execvp instead of execv to automatically search through $PATH for your executable. In that case, you would not add /bin/ to the path.
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.