I have a problem with pipe implementation in C. When I run the following program and input "ls | more", the result is an error segmentation fault core dump.
This is the program:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <readline/readline.h>
#include <readline/history.h>
void execute(char **, int, char **);
void handle_signal(int);
int parse(char *, char **, char **, int *);
void chop(char *);
#define INPUT_STRING_SIZE 513
#define NORMAL 00
#define OUTPUT_REDIRECTION 11
#define INPUT_REDIRECTION 22
#define PIPELINE 33
#define BACKGROUND 44
#define OUTPUT_APP 55
int main(int argc, char *argv[])
{
int i, mode = NORMAL, cmdArgc;
size_t len = INPUT_STRING_SIZE;
char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
inputString = (char*)malloc(sizeof(char)*INPUT_STRING_SIZE+200);
char curDir[100];
char *temp;
while(1)
{
mode = NORMAL;
getcwd(curDir, 100);
inputString = readline("$ ");
if(strcmp(inputString, "exit\n") == 0)
exit(0);
cmdArgc = parse(inputString, cmdArgv, &supplement, &mode);
if(strcmp(*cmdArgv, "cd") == 0)
{
chdir(cmdArgv[1]);
}
else
execute(cmdArgv, mode, &supplement);
}
return 0;
}
int parse(char *inputString, char *cmdArgv[], char **supplementPtr, int *modePtr)
{
int cmdArgc = 0, terminate = 0;
char *srcPtr = inputString;
//printf("parse fun%sends", inputString);
while(*srcPtr != '\0' && terminate == 0)
{
*cmdArgv = srcPtr;
cmdArgc++;
//printf("parse fun2%sends", *cmdArgv);
while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\0' && *srcPtr != '\n' && terminate == 0)
{
switch(*srcPtr)
{
case '&':
*modePtr = BACKGROUND;
break;
case '|':
*modePtr = PIPELINE;
*cmdArgv = '\0';
srcPtr++;
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
//chop(*supplementPtr);
terminate = 1;
break;
}
srcPtr++;
}
while((*srcPtr == ' ' || *srcPtr == '\t' || *srcPtr == '\n') && terminate == 0)
{
*srcPtr = '\0';
srcPtr++;
}
cmdArgv++;
}
/*srcPtr++;
*srcPtr = '\0';
destPtr--;*/
*cmdArgv = '\0';
return cmdArgc;
}
void chop(char *srcPtr)
{
while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\n')
{
srcPtr++;
}
*srcPtr = '\0';
}
void execute(char **cmdArgv, int mode, char **supplementPtr)
{
pid_t pid, pid2;
FILE *fp;
int mode2 = NORMAL, cmdArgc, status1, status2;
char *cmdArgv2[INPUT_STRING_SIZE], *supplement2 = NULL;
int myPipe[2];
if(mode == PIPELINE)
{
if(pipe(myPipe)) //create pipe
{
fprintf(stderr, "Pipe failed!");
exit(-1);
}
parse(*supplementPtr, cmdArgv2, &supplement2, &mode2);
}
pid = fork();
if( pid < 0)
{
printf("Error occured");
exit(-1);
}
else if(pid == 0)
{
switch(mode)
{
case PIPELINE:
close(myPipe[0]); //close input of pipe
dup2(myPipe[1], fileno(stdout));
close(myPipe[1]);
break;
}
execvp(*cmdArgv, cmdArgv);
}
else
{
if(mode == BACKGROUND)
;
else if(mode == PIPELINE)
{
waitpid(pid, &status1, 0); //wait for process 1 to finish
pid2 = fork();
if(pid2 < 0)
{
printf("error in forking");
exit(-1);
}
else if(pid2 == 0)
{
close(myPipe[1]); //close output to pipe
dup2(myPipe[0], fileno(stdin));
close(myPipe[0]);
execvp(*cmdArgv2, cmdArgv2);
}
else
{
;//wait(NULL);
//waitpid(pid, &status1, 0);
//waitpid(pid2, &status2, 0);
close(myPipe[0]);
close(myPipe[1]);
}
}
else
waitpid(pid, &status1, 0);
//wait(NULL);
}
}
Standard commands like ls, cat, sort etc. works normally.
Related
I try to use two FIFOs, one for the child process and one for the parent.
First, the child should write a question in the parent FIFO.
Parent waits until question arrives, then reads it, answers it and writes the answer in the child's FIFO.
Child waits for the answer, then reads it and puts it out.
Problem is, somehow the child reads always an empty answer and I don't know why.
Code below:
MAIN:
int main() {
pid_t child, parent;
char quest[PIPE_BUF];
int n=0;
switch(fork()) {
case CHILD: {
while(n==0){
printf("Bitte Frage stellen\n");
n=scanf("%s", quest);
}
}
child = getpid();
question(quest, child);
break;
case ERROR:
perror("fork():");
break;
default:
printf("waiting for input\n");
parent = getpid();
oracle(parent);
}
return EXIT_SUCCESS;
}
CHILD FUNCTION
void question(const char *buf, pid_t pid ){
char filename[PIPE_BUF];
if (mkfifo(ANSW, 0666) == -1){
if(errno == EEXIST)
perror(" oracle mkfifo:");
else{
perror(" oracle mkfifo:");
exit(EXIT_FAILURE);
}
}
int fd, fd2;
fd = open(FIFO, O_WRONLY);
char quest[PIPE_BUF + 1];
char answer[PIPE_BUF + 1];
int n = 0;
sprintf(quest, "%d:", pid);
strcat(quest,buf);
write(fd,quest,strlen(quest));
sleep(2);
fd2= open(ANSW,O_RDONLY);
printf("%s\n", answer);
while(1) {
n=read(fd2, answer, strlen(answer));
if (n == -1) {
perror(" client read():");
break;
} else if (n > 0){
puts(answer);
break;
}
}
remove("/tmp/answer.me");
unlink(FIFO);
unlink(ANSW);
close(fd);
close(fd2);
}
PARENT FUNCTIONS:
int isVokal(const char* buf, int i){
if (buf[i] == '?' && ((buf[i-1] == 'a') || (buf[i-1] == 'e') ||(buf[i-1] == 'i') || (buf[i-1] == 'o') || (buf[i-1] == 'u')))
return 1;
else
return 0;
}
int answer(char * buf, pid_t pid){
int i = 0;
int fd = open(ANSW, O_WRONLY);
char answer[PIPE_BUF + 1];
size_t x = strlen(buf);
buf[x+1] = '\0';
do{
i++;
if(buf[i] == '\0'){
--i;
if(buf[i] != '?'){
sprintf(answer, "%s","Dies ist keine Frage.\n");
write(fd,answer, strlen(answer));
i++;
}else if(isVokal(buf,i)){
sprintf(answer, "%s","Yes!\n");
write(fd,answer, strlen(answer));
i++;
}else{
sprintf(answer, "%s","No!\n");
write(fd,answer, strlen(answer));
i++;
}
}
}while ( buf[i] != '\0');
close(fd);
return 0;
}
void oracle(pid_t pid) {
int i = 1;
if ((mkfifo(FIFO, 0666) == -1)){
if(errno == EEXIST)
perror(" oracle mkfifo:");
else{
perror(" oracle mkfifo:");
exit(EXIT_FAILURE);
}
}
int fd = open(FIFO, O_RDONLY);
if(fd == -1)
perror(" oracle open():");
char buf[PIPE_BUF + 1];
while (i) {
printf("waiting for input\n");
int n = read(fd, buf, PIPE_BUF);
if (n == -1)
perror(" oracle read():");
else if (n >= 0)
i=answer(buf, pid);
}
close(fd);
remove("/tmp/ask.me");
unlink(FIFO);
unlink(ANSW);
}
Hello and thank you for attention. I am trying to implement my own shell. I have a few questions about my code and about tasks ro resolv. Below I present my previous code and questions:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
void parse(char *line, char **argv, char **argv2)
{
while (*line != '\0')
{
while (*line == ' ' || *line == '\t' || *line == '\n')
{
*line++ = '\0';
}
if(*line == '>' && *(line+1) == '>')
{
while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n')
{
line++;
}
while (*line == ' ' || *line == '\t' || *line == '\n')
{
*line++ = '\0';
}
*argv2 = line;
break;
}
if(*line == '&')
{
break;
}
*argv++ = line;
while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n')
{
line++;
}
}
*argv = '\0';
}
void execute(char **argv, int option)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
else if (pid == 0)
{
if (execvp(*argv, argv) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
}
else if(option == 1)
{
while (wait(&status) != pid);
}
}
void execute2(char *command, char **argv, char **argv2)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
else if (pid == 0)
{
//close(1);
parse(command, argv, argv2);
int output = open(*argv2, O_APPEND | O_WRONLY);
dup2(output,1);
if (strcmp(argv[0], "exit") == 0)
exit(0);
if (execvp(*argv, argv) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
close(output);
}
else
{
while (wait(&status) != pid);
}
}
int specialChar(char *argv)
{
int i=0;
while(argv[i]!='\0')
{
if(argv[i]=='>' && argv[i+1]=='>')
return 1;
else if(argv[i]=='&')
return 2;
else if(argv[i]=='|')
return 3;
i++;
}
}
void main()
{
char command[20];
char *argv[64];
char *argv2[1];
char **history = (char**)malloc(20*sizeof(char*));
int counterHistory1=-1;
int counterHistory2=0;
int i;
for(counterHistory2 = 0; counterHistory2<20; counterHistory2++)
{
history[counterHistory2]=(char*)malloc(100*sizeof(char));
}
FILE *file;
file=fopen("history", "w");
if(!file)
printf("ERROR");
while (1)
{
printf("Shell -> ");
gets(command);
counterHistory1++;
strcpy(history[counterHistory1],command);
fopen("history", "w");
if(counterHistory1<20)
for(i=0; i<=counterHistory1; i++)
{
fprintf(file,"%s\n",history[i]);
}
else
for(i=counterHistory1-20; i<counterHistory1; i++)
{
fprintf(file,"%s\n",history[i]);
}
fflush(file);
printf("\n");
switch(specialChar(command))
{
case 1:
//close(1);
execute2(command,argv,argv2);
break;
case 2: //running program in background
parse(command, argv, argv2);
if (strcmp(argv[0], "exit") == 0)
exit(0);
execute(argv,0);
break;
case 3:
break;
default:
parse(command, argv, argv2);
if (strcmp(argv[0], "exit") == 0)
exit(0);
execute(argv,1);
break;
}
fclose(file);
}
}
1) When last sign gettin from user is '&' I need to run my program in background. I have read that I can do this if I don 't call wait. Did I do this good or I should change something?
2) If I find ">>" I should redirect output to file. For example I get ls >> output, all catalogs and files should be written to file "output". Unfortunetly, it works only once. After this my program is stopped. I think that proces has never finished and then I can 't write next command. I tried to kill that proces but it didn 't works or I did something wrong.
3) In my shell I should create pipes of any length with the | sign. I have no idea how to resolv that problem...
Thank you for answers and help.
Answer 1
In a shell, a process that is created is child of shell and needs control of terminal, by getting access to stdout, stdin and stderr
signal(SIGTTOU, SIG_IGN);
setpgid(getpid(),getpid());
if(background==false) //shell for foreground process
{
tcsetpgrp(0,getpgid(getpid())); //access to stdin
tcsetpgrp(1,getpgid(getpid())); //access to stdout
tcsetpgrp(2,getpgid(getpid())); //access to stderr
}
execvp(command,args);
I am trying to create a simple c shell. The only thing I can't get to work is the piping. I just need one pipe (i.e. "ls | wc -l"). I can't find anything where someone does this by reading the arguments from command line, any help would be greatly appreciated. I need help getting a second set of arguments that is after the "|" right now if I had the argument "ls | wc -l" I would get "ls" stored in args2 but I need "wc -l" to be in args3 so I can execvp() them separately.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define HISTORY_COUNT 20
char *prompt = "% ";
int
main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char **args3;
char cmd[100];
char *hp;
char *cp;
char *ifile;
char *ofile;
int check;
int pfds[2];
int i;
int j;
int current = 0;
int p = 0;
//int check;
char *hist[HISTORY_COUNT];
//char history[90];
//typedef void (*sighandler_t) (int);
args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));
while (1) {
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, 80, stdin) == NULL)
break;
// split up the line
i = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
break;
args[i++] = token;
/* build command array */
}
args[i] = NULL;
// assume no redirections
ofile = NULL;
ifile = NULL;
// split off the redirections
j = 0;
i = 0;
while (1) { //stackoverflow.com/questions/35569673
cp = args[i++];
if (cp == NULL)
break;
switch (*cp) {
case '<':
if (cp[1] == 0)
cp = args[i++];
else
++cp;
ifile = cp;
break;
case '>':
if (cp[1] == 0)
cp = args[i++];
else
++cp;
ofile = cp;
break;
case '|': //I need this case to put the commands following
the pipe into a variable
if(cp[1] ==0){
cp = args[i++];
printf("%s\n", cp);
if(pipe(pfds) == -1){
perror("Broken Pipe");
exit(1);
}
//args3[j++] = cp;
p = 1;
}
else{
++cp;
printf("DOES THIS HAPPEN\n");
//
}
break;
default:
args2[j++] = cp;
args3[cp++] = cp
break;
}
}
args2[j] = NULL;
if (j == 0)
continue;
switch (pid = fork()) {
case 0:
// open stdin
if (ifile != NULL) {
int fd = open(ifile, O_RDONLY);
if (dup2(fd, STDIN_FILENO) == -1) {
fprintf(stderr, "dup2 failed");
}
close(fd);
}
// open stdout
if (ofile != NULL) {
// args[1] = NULL;
int fd2;
if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
perror("couldn't open output file.");
exit(0);
}
// args+=2;
printf("okay");
dup2(fd2, STDOUT_FILENO);
close(fd2);
}
if(p == 1){ //from stackoverflow.com/questions/2784500
printf("Welcome to the party\n");
close(1);
dup(pfds[1]);
close(pfds[0]);
execvp(args2[0], args2);
break;
}
if(strcmp(args2[0], "cd") == 0){ //cd command
if(args2[1] == NULL){
fprintf(stderr, "Expected argument");
}
else{
//printf("Is this happening");
check = chdir(args2[1]);
//printf("What abuot this");
if(check != 0){
fprintf(stderr,"%s",prompt);
}
}
break;
}
if (strcmp(args2[0], "history") == 0){
int i = 0;
int hist_num = 1;
//printf("%d\n", current);
do {
if (hist[i]) {
printf("%4d %s\n", hist_num, hist[i]);
hist_num++;
}
i = (i + 1) % HISTORY_COUNT;
} while (i != current);
break;
}
execvp(args2[0], args2); /* child */
signal(SIGINT, SIG_DFL);
fprintf(stderr, "ERROR %s no such program\n", line);
exit(1);
break;
case -1:
/* unlikely but possible if hit a limit */
fprintf(stderr, "ERROR can't create child process!\n");
break;
default:
//printf("am I here");
if(p==1){
close(0);
dup(pfds[0]);
close(pfds[1]);
//execvp();
}
wait(NULL);
//waitpid(pid, 0, 0);
}
}
exit(0);
}
I have a problem with pipes. My program is a Shell program in C. I want to execute for example ls | wc, but what I get after running is:
ls: cannot access |: no such file or directory ls: cannot access wc: no such file or directory.
What am I doing wrong?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#define MAX_CMD_LENGTH 100
#define MAX_NUM_PARAMS 10
int parsecmd(char* cmd, char** params) { //split cmd into array of params
int i,n=-1;
for(i=0; i<MAX_NUM_PARAMS; i++) {
params[i] = strsep(&cmd, " ");
n++;
if(params[i] == NULL) break;
}
return(n);
};
int executecmd(char** params) {
pid_t pid = fork(); //fork process
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else if (pid == 0) { // child process
execvp(params[0], params); //exec cmd
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
};
int execpipe (char ** argv1, char ** argv2) {
int fds[2];
pipe(fds);
int i;
pid_t pid = fork();
for (i=0; i<2; i++) {
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else
if (pid == 0) {
if(i ==0){
close(fds[1]);
dup2(fds[0], 0);
close(fds[0]);
execvp(argv1[0], argv1);
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else if(i == 1) {
close(fds[0]);
dup2(fds[1], 1);
close(fds[1]);
execvp(argv2[0], argv2);
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
} // end for
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
char * argv1[MAX_NUM_PARAMS+1];
char * argv2[MAX_NUM_PARAMS+1];
int k, y, x;
int f = 1;
while(1) {
printf("$"); //prompt
if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit
if(cmd[strlen(cmd)-1] == '\n') { //remove newline char
cmd[strlen(cmd)-1] = '\0';
}
int j=parsecmd(cmd, params); //split cmd into array of params
if (strcmp(params[0], "exit") == 0) break; //exit
for (k=0; k <j; k++) { //elegxos gia uparksi pipes
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
}
}
if (f==0) {
for (x=0; x<k; x++) {
argv1[x]=params[x];
}
int z = 0;
for (x=k+1; x< j; x++) {
argv2[z]=params[x];
z++;
}
if (execpipe(argv1, argv2) == 0) break;
} else if (f==1) {
if (executecmd(params) == 0) break;
}
} // end while
return 0;
}
Updated your code with following corrections.
Removed for() loop that iterated two times after fork() call.
Removed incorrect close of pipe FDs after dup2 calls for both parent and child processes.
Aligned the command that needed to be run as per the file descriptors that were duplicated in dup2() calls for parent and child. Basically I needed to swap execvp(argv2[0], argv2) and execvp(argv1[0], argv1) calls.
Added a break; statement in the for loop that searched for pipe character.
The updated code is as below.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_CMD_LENGTH 100
#define MAX_NUM_PARAMS 10
int parsecmd(char* cmd, char** params) { //split cmd into array of params
int i,n=-1;
for(i=0; i<MAX_NUM_PARAMS; i++) {
params[i] = strsep(&cmd, " ");
n++;
if(params[i] == NULL) break;
}
return(n);
};
int executecmd(char** params) {
pid_t pid = fork(); //fork process
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
} else if (pid == 0) { // child process
execvp(params[0], params); //exec cmd
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
int childstatus;
waitpid(pid, &childstatus, 0);
return 1;
}
};
int execpipe (char ** argv1, char ** argv2) {
int fds[2];
pipe(fds);
int i;
pid_t pid = fork();
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
}
if (pid == 0) { // child process
close(fds[1]);
dup2(fds[0], 0);
//close(fds[0]);
execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
} else { // parent process
close(fds[0]);
dup2(fds[1], 1);
//close(fds[1]);
execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
char * argv1[MAX_NUM_PARAMS+1] = {0};
char * argv2[MAX_NUM_PARAMS+1] = {0};
int k, y, x;
int f = 1;
while(1) {
printf("$"); //prompt
if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit
if(cmd[strlen(cmd)-1] == '\n') { //remove newline char
cmd[strlen(cmd)-1] = '\0';
}
int j=parsecmd(cmd, params); //split cmd into array of params
if (strcmp(params[0], "exit") == 0) break; //exit
for (k=0; k <j; k++) { //elegxos gia uparksi pipes
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
break;
}
}
if (f==0) {
for (x=0; x<k; x++) {
argv1[x]=params[x];
}
int z = 0;
for (x=k+1; x< j; x++) {
argv2[z]=params[x];
z++;
}
if (execpipe(argv1, argv2) == 0) break;
} else if (f==1) {
if (executecmd(params) == 0) break;
}
} // end while
return 0;
}
If you are interested only in changes I made, here is the diff between your code and the above updated code:
--- original.c
+++ updated.c
## -4,6 +4,7 ##
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/wait.h>
#define MAX_CMD_LENGTH 100
## -43,44 +44,36 ##
pipe(fds);
int i;
pid_t pid = fork();
- for (i=0; i<2; i++) {
if (pid == -1) { //error
char *error = strerror(errno);
printf("error fork!!\n");
return 1;
- } else
- if (pid == 0) {
- if(i ==0){
+ }
+ if (pid == 0) { // child process
close(fds[1]);
dup2(fds[0], 0);
- close(fds[0]);
- execvp(argv1[0], argv1);
+ //close(fds[0]);
+ execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
- } else if(i == 1) {
+ } else { // parent process
close(fds[0]);
dup2(fds[1], 1);
- close(fds[1]);
- execvp(argv2[0], argv2);
+ //close(fds[1]);
+ execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
char *error = strerror(errno);
printf("unknown command\n");
return 0;
}
- } else { // parent process
- int childstatus;
- waitpid(pid, &childstatus, 0);
- return 1;
- }
- } // end for
};
int main() {
char cmd[MAX_CMD_LENGTH+1];
char * params[MAX_NUM_PARAMS+1];
- char * argv1[MAX_NUM_PARAMS+1];
- char * argv2[MAX_NUM_PARAMS+1];
+ char * argv1[MAX_NUM_PARAMS+1] = {0};
+ char * argv2[MAX_NUM_PARAMS+1] = {0};
int k, y, x;
int f = 1;
while(1) {
## -95,6 +88,7 ##
if (strcmp(params[k], "|") == 0) {
f = 0; y = k;
printf("pipe found\n");
+ break;
}
}
if (f==0) {
execv* procedure doesn't interpret shell script string. It merely starts an executable file and passes an array of arguments to it. Thus, it cannot organize a pipeline.
If you need "normal" shell command execution, you may want to use system(char*) procedure instead of execvp.
Otherwise, if you need to do the pipes yourself, you may want to parse the string with '|' special characters and use pipe(), fork() and I/O redirection. Like here How to run a command using pipe?
So, I thought I was on the right track with trying to imitate the bash shell, but I'm having issues piping. I am getting an error executing the second command. I was wondering if someone could explain to me how to fix this and why it's going wrong.
I'm very new to C & Linux commands so any supplemental information that could help me along the way would be also be appreciated.
Thank you so much for your time. My code is below, but there is a lot of it. My issue is occurring in the exec_pipe function. I would normally include what I have used for input and what I am getting for output, but my sample input is actually executable files my professor gave us for testing. Unfortunately, mine is not working like it does in the shell. I am just getting my error print out:
Inside Case 5
Inside Exec_Pipe
Error in Pipe EXECVP cmd2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/wait.h>
#define BUFSIZE 1024
#define CSTRSIZE 100
#define CMDSIZE 30
#define DEBUG 1
//I referenced our blackboard source code files to create the fork functions and to deal with file descriptors
void exec_cmd(char** cmd1){
pid_t pid;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
}
}
void exec_cmd_in(char** cmd1, char* infile){
pid_t pid;
int fdi;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
fdi = open(infile, O_RDONLY);
if(fdi == -1){
printf("No Infile");
}
}
}
void exec_cmd_opt_in_append(char** cmd1, char* infile, char* outfile){
/* pid_t pid;
int fdi, fdo;
if((pid = fork()) < 0){
printf("Child Process Failed\n");
}else if(pid == 0){
fdo = open(outfile, O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if(fdo == -1){
printf("No Outfile");
}
if(dup2(fdi, 0) == -1){
printf("Infile not updated");
}
if(dup2(fdo, 1) == -1){
printf("Outfile not updated");
}
close(fdi);
close(fdo);
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
} */
}
void exec_cmd_opt_in_write(char** cmd1, char* infile, char* outfile){
/* pid_t pid;
int fdi, fdo;
if((pid = fork()) < 0 ){
printf("Fork Error");
exit(1);
}else if(pid == 0 ){
fdo = open(outfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(fdo == -1){
printf("No Outfile");
}
if(dup2(fdi, 0) == -1){
printf("Infile not updated");
}
if(dup2(fdo, 1) == -1){
printf("Outfile not updated");
}
close(fdi);
close(fdo);
if(execvp(cmd1[0], cmd1) < 0){
printf("Execution Failed\n");
exit(1);
}
}else{
wait(NULL);
}
*/
}
void exec_pipe(char** cmd1, char** cmd2){
pid_t pid;
int pipefd[2];
// pipe[1] is the write end of the pipe
// pipe[0] is the read end of the pipe
// making a pipe
printf("Inside Exec_Pipe\n");
pid = fork();
switch(pid){
case -1:
//error in fork
printf("Fork Error\n");
//Exit
exit(1);
case 0:
//child
break;
default:
//parent
wait(NULL);
}
//This will be executed by child process
if(pipe(pipefd) < 0 ) {
//error condition
printf("Pipe Error");
exit(1);
}
pid = fork();
switch(pid){
case -1:
//error in fork
printf("Fork Error\n");
//Exit
case 0:
//child
close(STDIN_FILENO);
//direct STDOUT to the pipe
dup2(pipefd[1], STDOUT_FILENO);
//Close descriptors
close(pipefd[0]);
close(pipefd[1]);
//Execute Command1
execvp(cmd1[0], cmd1);
//execvp should not return, so if it does
//there is an error!
printf("Error in EXECVP cmd1");
exit(1);
default:
//parent
close(STDIN_FILENO);
//direct input to the pipe
dup2(pipefd[0],STDIN_FILENO);
//close descriptors
close(pipefd[0]);
close(pipefd[1]);
//execute command 2
execvp(cmd2[0],cmd2);
//if execvp makes it back, error condition
printf("Error in Pipe EXECVP cmd2");
exit(1);
}
}
void exec_pipe_opt_in_append(char** cmd1, char** cmd2, char* infile, char* outfile){
}
void exec_pipe_opt_in_write(char** cmd1, char** cmd2, char* infile, char* outfile){
}
int parse_command(char* line, char** cmd1, char** cmd2, char* infile, char* outfile){
/*
(1)Create a bunch of flags to compare for the right return value
(2)Loop over the entire line and set the flags
(3)Add a bunch of if statements to compare flags
(4)If there is more than one flag for pipe, we can't handle it. Regurn 9.
(5)If there is &, we can't handle.
(6)Return the right value
*/
int pipe_found = 0;
int input_found = 0;
int redirection = 0;
int i = 0;
int spaces = 0;
int append = 0;
int special = 0;
while(line[i] != '\0'){
if(line[i] == '|'){
pipe_found++;
}
if(line[i] == '<'){
input_found = 1;
}
if((line[i] == '&') || (line[i] == '*') || (line[i] == '^') || (line[i] == '%') || (line[i] == '#') || (line[i] == '!') || (line[i] == '#') || (line[i] == '(') || (line[i] == ')')){
special = 1;
}
if(line[i] == '>'){
redirection = 1;
if(line[i+1] == '>'){
append = 1;
}
}
if(line[i] == ' '){
spaces++;
}
i++;
}
if((strlen(line) >=4) && (line[0] == 'q') && (line[1] == 'u') && (line[2] == 'i') && (line[3] == 't')){
return 0;
}
if((pipe_found == 0) && (special == 0)){
if((redirection == 0) && (input_found == 0)){
return 1;
}else if((redirection == 0) && (input_found == 1)){
return 2;
}else if(append == 1){
return 3;
}else if(redirection == 1){
return 4;
}
}else if((pipe_found == 1) && (special == 0)){
if((redirection == 0) && (input_found == 0)){
return 5;
}else if((redirection == 0) && (input_found == 1)){
return 6;
}else if(append == 1){
return 7;
}else if(redirection == 1){
return 8;
}
}
return 9;
}
//I referenced StackOverflow and some online libraries to get this tokenize function
char ** tokenize(char *str, char *delim, unsigned int *number_tokens) {
char *pch = strtok(str, delim);
unsigned int ntok = 0;
if(pch != NULL) {
ntok = 1;
}else{
return NULL;
}
char **tokens = realloc(NULL, sizeof(char *)*ntok);
tokens[ntok-1] = pch;
while(pch != NULL) {
pch = strtok(NULL, delim);
ntok++;
tokens = realloc(tokens, sizeof(char *)*ntok);
tokens[ntok-1] = pch;
}
if(number_tokens) {
*number_tokens = ntok;
}
return tokens;
}
//I referenced StackOverflow.com for this trim function
char *trim(char *str) {
char *end;
if(str == NULL){
return NULL;
}
while(isspace(*str)){
str++;
}
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) {
end--;
}
*(end+1) = 0;
return str;
}
int main(int argc, char *argv[]){
int returnValue = 0;
char *infile = NULL;
char *outfile = NULL;
char **cmd = NULL;
char **cmd1_tokens = NULL;
char **cmd2_tokens = NULL;
char *input;
int current_cmd = 0;
/*
(1)If the user does not enter a command line argument, get one after typing "myshell-%"
(2)Call parse_command on the user input to get the right return value
(3)Begin parsing the user input within main
*/
if(argc == 1){
printf("myshell-%%\n");
fgets (input, 20, stdin);
returnValue = parse_command(input, cmd1_tokens, cmd2_tokens, infile, outfile);
cmd = tokenize(input, "|", NULL);
}else{
returnValue = parse_command(argv[1], cmd1_tokens, cmd2_tokens, infile, outfile);
cmd = tokenize(argv[1], "|", NULL);
}
int infileIt = 0;
while(cmd[current_cmd] != NULL) {
unsigned int number_tokens = 0;
char **infile_token = tokenize(cmd[current_cmd], "<", &number_tokens);
if(number_tokens > 1){
while(infile_token[infileIt] != NULL){
infileIt++;
}
}
if(infile_token[1] != NULL) {
number_tokens = 0;
char **infile_outfile_token = tokenize(infile_token[1], ">", &number_tokens);
if(number_tokens > 1){
infile = infile_outfile_token[0];
infile = infile_token[1];
}
}
number_tokens = 0;
char **outfile_token = tokenize(cmd[current_cmd], ">", &number_tokens);
if(number_tokens > 1){
outfile = outfile_token[1];
}
current_cmd++;
}
//Trim the in/outfiles
infile = trim(infile);
outfile = trim(outfile);
/*
Start breaking up cmd[0] and cmd[1] into smaller chunks and saving into the appropriate cmd
*/
cmd1_tokens = tokenize(cmd[0], " ", NULL);
if(cmd[1] != NULL){
cmd2_tokens = tokenize(cmd[1], " ", NULL);
}
int cmd1Args = 0;
while(cmd1_tokens[cmd1Args] != NULL){
cmd1Args++;
}
int cmd2Args= 0;
if(cmd2_tokens != NULL){
while(cmd2_tokens[cmd2Args] != NULL){
cmd2Args++;
}
}
int iterator = 0;
while((iterator < cmd1Args) && (cmd1Args != 0)){
printf("Cmd1: %s\n", cmd1_tokens[iterator]);
iterator++;
}
iterator = 0;
while((iterator < cmd2Args)&&(cmd2Args != 0)){
printf("Cmd2: %s\n", cmd2_tokens[iterator]);
iterator++;
}
if(infile != NULL){
printf("Infile: %s\n", infile);
}
if(outfile != NULL){
printf("Outfile: %s\n", outfile);
}
/*Use a switch statement to process all the return values (0 ot 9) of parse_command.
Our program should execute the “line” if the return code from parse_command
function is 0 to 8, that is the line is deemed “valid”. For return code 9,
our program simply output ”Not handled at this time!”.*/
switch(returnValue){
case 0 :
printf("Exiting Program.\n");
exit(1);
break;
case 1 :
printf("Inside Case 1\n");
exec_cmd(cmd1_tokens);
break;
case 2 :
printf("Inside Case 2\n");
exec_cmd_in(cmd1_tokens, infile);
break;
case 3 :
printf("Inside Case 3\n");
exec_cmd_opt_in_append(cmd1_tokens, infile, outfile);
break;
case 4 :
printf("Inside Case 4\n");
exec_cmd_opt_in_write(cmd1_tokens, infile, outfile);
break;
case 5 :
printf("Inside Case 5\n");
exec_pipe(cmd1_tokens, cmd2_tokens);
break;
case 6 :
printf("Inside Case 6\n");
//exec_pipe_in(cmd1_tokens, cmd2_tokens, infile);
break;
case 7 :
printf("Inside Case 7\n");
exec_pipe_opt_in_append(cmd1_tokens, cmd2_tokens, infile, outfile);
break;
case 8 :
printf("Inside Case 8\n");
exec_pipe_opt_in_write(cmd1_tokens, cmd2_tokens, infile, outfile);
break;
default :
printf("Inside Case 9\n");
printf("Not handled at this time!\n");
}
return 0;
}
Without having access to the input file that you're giving it, it's a little hard to say what's going on, but here are some tips for debugging it.
First, when something you don't understand is happening, it can be a good idea to strip it down to a minimal, working, self contained example that demonstrates the problem. Sometimes, just the process of cutting it down to that small example can help you to find the problem; but if not, it gives you a much smaller example to ask about.
Next, when putting in these print statements to debug what's going on, give yourself a little more context. Especially in the one that indicates an error; print out what the error is, and what the arguments were to the function that failed. Rather than just:
printf("Error in Pipe EXECVP cmd2");
You can use strerror to get a string representing the error number:
printf("Error %d in Pipe EXECVP cmd2: %s\n", errno, strerror(errno));
And you can also print out what the command and all of your arguments were:
for (char **arg = cmd2; *arg != NULL; ++arg) {
printf("cmd2[%ld] = %s", arg - cmd2, *arg);
}
Between printing out the actual error and printing out the command name and all of the arguments, that should help you to debug the problem.
If you could add that information to your question, and maybe cut your example down to a more minimal example as well as showing a minimal example of the input that causes a problem, we could probably help out a lot more.