I am trying to create a small shell using C. At the moment I am trying to figure out piping and external commands. I got stuck in them both even after looking at various youtube videos.
I referred to MAN and even Advanced Linux Programming
What can I change to improve and make the implementation work?
This a part of the checking of commands, args := tokenisation by whitespace, commLHS := will be used to store args before | and commRHS will be used to store args after | and indexT refers to the number of arguments inputted
else if((check4pipe(args, commLHS, commRHS, indexT) != 0))
{
return runPipeComm(commLHS, commRHS);
//fprintf(stderr, "%s: command not found\n", args[0]);
}
This will execute External Commands
void externalCommands(char **args)
{
// fork-plus-exec pattern
// https://www.percona.com/community-blog/2021/01/04/fork-exec-wait-and-exit/
/*
First we Fork
Then we Exec
Then we Wait
Then we Exit
*/
int status;
pid_t pip = fork();
if (pip == -1)
{
perror("Error - fork()");
}
else if (pip == 0)
{
//If PID is the child process
//Launches the process.
if (execvp(args[0], args) < 0)
{
perror("Error - execvp()");
}
}
else
{ //If PID is the parent process.
//Waits for the child process and returns exit code if waitpid() is successful.
if(waitpid(pip, &status, WUNTRACED) == -1)
{
perror("Error occured during waitpi");
}
else
{
//set_exitcode(status); //Sets the exitcode environment variable.
}
}
}
This is to check for | in args inputted by user after tokenisation.
int check4pipe(char **args, char **pipeLHS, char **pipeRHS, int indexT)
{
bool foundPipe = false;
for(int i = 0; i < indexT; i++)
{
if(strcmp(args[i], "|") == 0)
{
foundPipe = true;
memcpy(pipeLHS, args, (i+1) * sizeof(char*));
pipeLHS[i] = NULL;
memcpy(pipeLHS, args+(i+1), ((indexT-i)+1) * sizeof(char*));
pipeRHS[i]= NULL;
}
else
{
continue;
}
}
if(foundPipe == true)
{
return 1;
}
else
{
return 0;
}
}
This will run the pipe commands
int runPipeComm(char **commLHS, char **commRHS)
{
int userPipe[2];
pid_t pip1; // Pipe ID 1
pid_t pip2; // Pipe ID 2
if(pipe(userPipe) < 0)
{
perror("Error Occurred while piping: ");
}
// Start Process
pip1 = fork();
if(pip1 == -1)
{
perror("Error Occurred while forking: ");
}
else if(pip1 == 0)
{
dup2(userPipe[1], STDOUT_FILENO);
close(userPipe[1]);
//run
exit(0);
}
else
{
pip2 = fork();
if(pip2 == -1)
{
perror("Error Occurred while forking: ");
}
else if(pip2 == 0)
{
dup2(userPipe[0], STDOUT_FILENO);
close(userPipe[1]);
//run
exit(0);
}
else
{
close(userPipe[0]);
close(userPipe[1]);
wait(NULL);
wait(NULL);
}
}
return 1;
}
You forgot to change some things when copying snippets within your program.
wrong:
memcpy(pipeLHS, args+(i+1), ((indexT-i)+1) * sizeof(char*));
pipeRHS[i]= NULL;
right:
memcpy(pipeRHS, args+(i+1), (indexT-(i+1)) * sizeof (char*));
pipeRHS[indexT-(i+1)] = NULL;
wrong:
dup2(userPipe[0], STDOUT_FILENO);
close(userPipe[1]);
right:
dup2(userPipe[0], STDIN_FILENO);
close(userPipe[0]);
After the first //run line, add the missing
execvp(*commLHS, commLHS);
perror("Error - execvp()");
After the second //run line, add the missing
execvp(*commRHS, commRHS);
perror("Error - execvp()");
Finally, the write end of the pipe must be closed in the parent process, so move the close() there:
close(userPipe[1]);
pip2 = fork();
Related
I am have written a function in C that emulates command line piping. Now i am trying to handle redirection using '<' and '>'. I have managed to implement redirecting stdout correctly, however I am having problems wit stdin.
The function takes 3 arguments, the list of commands, and an input and output location. These can be NULL if there is no '<' or '>' used.
I am not sure why the input is not working. When I run "wc < input_file" it just executes wc.
The code looks like this:
void pipe_function(char *** cmd, char * input, char * output)
{
int p[2];
pid_t pid;
int fd_in = 0;
int count = 0;
/* If there are no input or output destinations specified */
if ((input == NULL) && (output == NULL))
{
while (* cmd != NULL)
{
pipe(p);
if ((pid = fork()) == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
dup2(fd_in, 0);
if (* (cmd + 1) != NULL)
{
dup2(p[1], 1);
}
close(p[0]);
execvp((* cmd)[0], * cmd);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
close(p[1]);
fd_in = p[0];
cmd++;
}
}
}
/* If there is no input destination specified but an output destination specified */
else if ((input == NULL) && (output != NULL))
{
while (* cmd != NULL)
{
pipe(p);
if ((pid = fork()) == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
dup2(fd_in, 0);
if (* (cmd + 1) != NULL)
{
dup2(p[1], 1);
}
else
{
int file = open(output, O_WRONLY | O_CREAT, 0777);
dup2(file, 1);
close(file);
}
close(p[0]);
execvp((* cmd)[0], * cmd);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
close(p[1]);
fd_in = p[0];
cmd++;
}
}
}
/* If there is an input destination and no output destination */
/* THIS IS WHERE TROUBLE IS OCCOURING*/
else if ((input != NULL) && (output == NULL))
{
while (* cmd != NULL)
{
pipe(p);
if ((pid = fork()) == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
if (count != 0)
{
dup2(fd_in, 0);
}
else
{
int file = open(input, O_RDONLY);
dup2(file, 0);
close(file);
}
if (* (cmd + 1) != NULL)
{
dup2(p[1], 1);
}
close(p[0]);
execvp((* cmd)[0], * cmd);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
close(p[1]);
fd_in = p[0];
cmd++;
}
count++;
}
}
/* If there is both an input and an output destination */
else
{
}
}
Any help would be greatly appreciated :)
It doesn't completely fix your program, but I think the main bug here is that you don't close STDIN_FILENO before calling dup2. Thus dup2 fails, and you remain with STDIN at the 0 fd.
This is actually incorrect, dup2() closes newfd for you!
Generally, the code looks pretty good, but it's best practice to always check the return value of library functions - this would obviously have caught the bug here.
I'm trying to create my own unix shell and I've hit a wall while trying to append a command such as ls to an existing file, for example.
ls >> myOutput
I was able to do a basic redirection using > to print to an output file and figured doing >> would be quite similar, but I guess I'm wrong.
This is my code:
int pid;
int in = 0, out = 0, append = 0, j;
int fd0, fd1, fda;
char* args[MAX_ARGS];
char inFileName[64], outFileName[64];
//used to get arguments for desired command
//i.e. ls -a -l -t
get_args(cmdline, args);
//Commands used to exit the shell.
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
{
exit(0);
}
pid = fork();
if(pid == 0)
{ /* child process */
for(j = 0; args[j] != '\0'; ++j)
{
if(strcmp(args[j], ">>") == 0)
{
args[j] = NULL;
strcpy(outFileName, args[j + 1]);
printf("You want to append data to existing file\n");
append = 2;
}
if(strcmp(args[j], "<") == 0)
{
args[j] = NULL;
strcpy(inFileName, args[j + 1]);
printf("input file name is %s\n", inFileName);
in = 2;
}
if(strcmp(args[j], ">") == 0)
{
args[j] = NULL;
strcpy(outFileName, args[j + 1]);
printf("output file name is %s\n", outFileName);
out = 2;
}
}
//printf("in is %d and out is %d\n", in, out);
if(!strcmp(args[0], "|"))
{
printf("You want to pipe info\n");
}
if(append)
{
**//here is where my issue occurs**
if((fda = open(outFileName, O_RDWR|O_APPEND)) < 0)
{
perror("Error appending data\n");
exit(0);
}
dup2(fda, STDOUT_FILENO);//1
close(fda);
}
if(in)
{
if((fd0 = open(inFileName, O_RDONLY, 0)) < 0)
{
perror("Couldn't read from input file\n");
exit(0);
}
//Changes where the command will read from STDIN to a input file.
dup2(fd0, 0);
close(fd0);
}
if(out)
{
if((fd1 = creat(outFileName , 0644)) < 0)
{
perror("Coudln't create output file\n");
exit(0);
}
dup2(fd1, STDOUT_FILENO);//1
close(fd1);
}
execvp(*args, args);
perror("exec failed");
exit(-1);
}
else if(pid > 0)
{ /* parent process */
waitpid(pid, NULL, 0);
}
else
{ /* error occurred */
perror("fork failed");
exit(1);
}
Please let me know if you would like further details. Any help would truly be appreciated.
I have the simple producer consumer program in C try to solve it with fork
I get error exactly when producer tries to write on pipe:
I have wrote another program with the same logic but this one does not give me any clue to know why?
Producer failed to write item on pipe: Bad file descriptor
Anyone have idea why I get this error?
Thanks
#define READ 0
#define WRITE 1
int mutex = 1, full = 0, empty = BUFFER_SIZE, x = 0;
void consumer();
void producer();
int wait_(int);
int signal_(int);
int pipefd[2];
int main() {
printf("Starting producer-consumer problem!\n");
//We intend to run the producer in parent process and the consumer in the child process
if (pipe(pipefd) == -1) { /* An error has occurred. */
fprintf(stderr, "%s", "The call to pipe() has failed.\n");
exit(EXIT_FAILURE);
}
for (int j = 0; j < sizeof(pipefd); j++) {
if (pipe(&pipefd[j]) < 0) { //Initialize each pipe appropriately
perror("Error in making pipe...");
}
}
pid_t pid = fork();
if (pid < 0) {
perror("**********Error in creating fork()!**************\n");
exit(STDERR_FILENO);
} else if (pid == 0) {
consumer();//We intend to run the consumer in child
} else {
producer();//We intend to run the producer in parent
}
return 0;
}
int wait_(int s) {
return (--s);
}
int signal_(int s) {
return (++s);
}
void producer() {
printf("Starting Producer\n");
//while (1) {
//sleep(1);
if (close(pipefd[READ]) != 0) {
perror("Error in closing reading pipe");
}
if (write(pipefd[WRITE], &full, 1) < 0) {
perror("Producer failed to write item on pipe");
}
if ((mutex == 1) && (empty != 0)) {
mutex = wait_(mutex);
full = signal_(full);
empty = wait_(empty);
x++;
printf("Producer produces the item %d\n", x);
mutex = signal_(mutex);
}
if (close(pipefd[WRITE]) != 0) {
perror("Error in closing writing pipe");
}
//}
}
void consumer() {
printf("Starting Consumer\n");
//while (1) {
//sleep(1);
int status = 0;
wait(&status); /* wait for all children to return back the result */
if (close(pipefd[WRITE]) != 0) {
perror("Error in closing reading pipe");
}
if (read(pipefd[READ], &full, 1) > 0) {
printf("Consumer\t%d\n", full);
}
if ((mutex == 1) && (full != 0)) {
mutex = wait_(mutex);
full = wait_(full);
empty = signal_(empty);
printf("Consumer consumes item %d\n", x);
x--;
mutex = signal_(mutex);
}
if (close(pipefd[READ]) != 0) {
perror("Error in closing reading pipe");
}
//}
}
The sizeof operator returns the size in bytes. So on a typical system where an int is four bytes, sizeof(pipefd) would result in the value 8. Which is not the correct number of elements for your loop.
Also, pipe(&pipefd[j]) is not correct either. The two pipes in pipefd are already initialized "appropriately". There's no need for any more initialization. Especially since in both this and the previous case you will have undefined behavior.
The following function successfully executes any command that doesn't contain pipes, so don't worry about the weird functions. These work. The problem I am having is that whenever I execute any command like the following:
cat file.txt | grep string
the command is successfully executed, but it remains idle, so somehow it gets stuck and no other command can execute. why is this happening?. I think it has something to do with the way I use pipe, dup and fork, so try to approach the problem from these functions. I know you may be arguing that this code doesn't work for other commands with pipes, but I just want to get this particular example to work and to do so I just redirect STDIN to the open file in the first iteration.
int myshell_execute(struct processNode* list, int a)
{
struct processNode* myList = list; // next node to be handled
int pipefd[2];
int in=0;
if (pipe(pipefd) == -1) {
perror("pipe");
myshell_exit(-1);
}
while(myList != NULL)
{
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
int *status;
int* stdout;
int stdout_num = 1;
stdout = &stdout_num;
int fileDescriptor;
pid_t pid;
if(strcmp(program,"cd") == 0)
{
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0)
{
return myshell_exit(0);
}
pid = fork();
if(pid == 0)
{
if(in == 1)
{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
}
if(sizeOfLine(redirection_string) != 0)
{
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
if(*stdout == 1 && myList->next !=NULL)
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO); // with this
close(pipefd[1]);
}
if(execvp(program,program_args) !=-1)
{
perror("myshell:");
myshell_exit(-1);
}
else{
myshell_exit(0);
}
}
else if (pid <0)
{
perror("myshell: ");
myshell_exit(-1);
}
else
{
wait(status);
}
in = 1;
myList = myList->next;
}
}
new solution:
int helper_execute(int in, int out, char* program, char ** program_args, char *redirection)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
redirectionHandler(redirection);
return execvp (program, program_args);
}
return pid;
}
int myshell_execute(struct processNode* list, int a)
{
int i;
pid_t pid;
int in, fd [2];
struct processNode*new_list = list;
char ** newProgram = new_list->program_arguments;
char ** redirection = new_list->redirection;
char * program = new_list->program;
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
while(new_list->next != NULL)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
helper_execute (in, fd [1],program,newProgram,redirection);
/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
new_list = new_list->next;
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
char* lastProgram = new_list->program;
char ** lastRedirection = new_list->redirection;
char * lastPrArguments = new_list->program_arguments;
redirectionHandler(redirection);
return execvp (lastProgram, lastPrArguments);
}
int main() {
int i=0;
char **input;
struct processNode* list;
int tracker = 0;
while ((input = getline()) != EOF) {
list = create_list(input);
myshell_execute(list,0);
}
return 0;
}
The only problem with this solution is that as soon as one command is executed, the main immediately detects the end of the file, so it exits the shell.
This is because
your parent process also holds the pipe open,
just after forking you call wait. If your first process (cat) fills its output pipe and there is not any process yet available to consume the read end of the pipe then the process stalls forever.
It would look like this:
#define MAX_PIPE_LEN 256
int myshell_execute(struct processNode* list, int a)
{
/* fd0 are the input and output descriptor for this command. fd1
are the input and output descriptor for the next command in the
pipeline. */
int fd0[2] = { STDIN_FILENO, STDOUT_FILENO },
fd1[2] = { -1, -1 };
pid_t pids[MAX_PIPE_LEN] = { 0 };
int pipe_len;
struct processNode* myList; // next node to be handled
int status;
int failed = 0;
for (pipe_len = 0, myList = list;
pipe_len < MAX_PIPE_LEN && myList != NULL;
pipe_len++, myList = myList->next) {
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
if(strcmp(program,"cd") == 0) {
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0) {
return myshell_exit(0);
}
if (myList->next != NULL) {
/* The output of this command is piped into the next one */
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
failed = 1;
break;
}
fd1[0] = pipefd[0];
fd1[1] = fd0[1];
fd0[1] = pipefd[1];
}
pids[pipe_len] = fork();
if (pids[pipe_len] < 0) {
perror("error: fork failed");
failed = 1;
break;
}
if (pids[pipe_len] == 0) {
if (fd0[0] != STDIN_FILENO) {
if (dup2(fd0[0], STDIN_FILENO) == -1) {
perror("error: dup2 input failed");
abort();
}
close(fd0[0]);
}
if (fd0[1] != STDOUT_FILENO) {
if (dup2(fd0[1], STDOUT_FILENO) == -1) {
perror("error: dup2 outut failed");
abort();
}
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0) {
close(fd1[1]);
}
if(sizeOfLine(redirection_string) != 0) {
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
execvp(program, program_args);
perror("error: execvp failed");
abort();
}
if (fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd1[1] != STDOUT_FILENO) {
close(fd0[1]);
}
fd0[0] = fd1[0];
fd0[1] = fd1[1];
fd1[0] = fd1[1] = -1;
}
if (myList->next) {
fprintf(stderr, "ERROR: MAX_PIPE_LEN (%d) is too small\n",
MAX_PIPE_LEN);
}
if (fd0[0] >= 0 && fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd0[1] >= 0 && fd0[1] != STDOUT_FILENO) {
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0 && fd1[1] != STDOUT_FILENO) {
close(fd1[1]);
}
/* Now wait for the commands to finish */
int i;
for (i = 0; i < pipe_len; i++) {
if (waitpid(pids[pipe_len - 1], &status, 0) == -1) {
perror("error: waitpid failed");
failed = 1;
}
}
if (failed)
status = -1;
myshell_exit(status);
}
I have looked all over the internet and die.net and can't see to make my code work. My problem is that I am able to redirect the output to a file, but have trouble bringing it back to standard out, I have tried using dup, dup2 and close, but maybe I am using them wrong. Any help would be appreciated, thank you
. My problem begins at the if(myargc >= 3) block when I am trying to redirect the output.
main()
{
int i, myargc =0, background, newfile, file, stdout2, read = 0, write = 0;
pid_t pid;
char input[512], *myargv[60];
while(1)
{
background = 1;
printf("Myshell>");
gets(input);
//scanf("%s", input);
myargc = parser(input, myargv);
if(strcmp(*myargv, "exit") == 0)
{
exit(0);
}
if(strcmp(myargv[myargc-1], "&") == 0)
{
background = 0;
myargv[myargc-1] = '\0';
myargc--;
}
if(myargc >= 3)
{
if(strcmp(myargv[myargc-2], ">") == 0)
{
write = 1;
file = creat(myargv[myargc-1], S_IWUSR);
myargv[myargc-2] = '\0';
if(file < 0)
{
printf("File could not be created.\n");
}
printf("Redirecting output to file %s.\n", myargv[myargc-1]);
fflush(stdout);
stdout2 = dup(STDOUT_FILENO);
//fclose(stdout); // fclose() for type FILE*
newfile = dup2(file, 1); // uses lowest number descriptor (1, since just
// closed stdout)
close(file); // closes old file descriptor duplicate, close() uses int
myargc = myargc-2;
}
}
if ((pid = fork()) == -1 )
{
// if fork fails, print error and exit
perror("Fork failed");
exit(-1);
}
else if (pid == 0) { // child process
if (read == 0 && write == 0)
{
printf("This is the child ready to execute: ");
fflush(stdout);
for (i =0; i < myargc; i++)
{
printf("%s ", myargv[i]);
fflush(stdout);
}
printf("\n");
}
if (execvp(*myargv,myargv) < 0);
{
printf("Execution failed.");
}/* error exit - exec returned */
close(newfile);
dup2(stdout2, STDIN_FILENO);
//close(file);
//if (close(file) == 0)
//{
//dup2(newfile, STDOUT_FILENO);
//close(newfile);
//close(stdout2);
// printf("Reopened stdout\n");
// }
perror("Exec returned");
exit(-1);
}
close(newfile);
if (background == 1) { /* this is the parent -- wait for child to terminate */
wait(pid,0,0);
printf("The parent is exiting now\n");
}else{
waitpid(pid, NULL, WNOHANG); //returns immediately, no wait
}
//test for correct parsing
printf("myargv:\n");
for (i = 0; i < myargc; i++)
{
printf("%s\n", myargv[i]);
}
printf("myargc: %d\n", myargc);
// clear out buffers
memset(&myargv[0], 0, sizeof(myargv));
memset(&input[0], 0, sizeof(input));
}
}