Redirection/Piping in C Bugs - c

Ok, in this code I have 2 primary problems.
1: The parent doesn't wait for the child to print before returning to main and printing. I included a waitpid() but it doesn't seem to be working how I expected.
When I redirect, I create the file fine, but nano and vim don't "see" the file, Gedit however can (so I know my output is being sent correctly)
The flow of this function:
A string is input, the string is checked for a trailing & (if it exists, it is removed and the background flag is updated).
Then the string is tokenized on '|'
Each token is tokenized on '<' and '>'
Then each string from that is tokenized on ' '
The resulting char ** is executed with the output/input being redirected based off the results of the above tokenizations.
void execute_command(char * s)
{
printf("\n");
int fd[3][2]; // since we are guaranteed each type of redirection will be used only once, I only need 3 pipes: < > and |
//int fd[2];
int res;
pid_t pid;
int status;
int stdin_c = dup(0);
int stdout_c = dup(1);
int i;
int background = 0;
for(i = 0; s[i] != '\0'; i++);
if(s[i-1] == '&')
{
background = 1;
s[i-1] = '\0';
}
char ** piped = token_pipe(s);
char ** left_a;
char ** right_a;
int output = 0;
int input = 0;
for(i = 0; piped[i] != NULL; i++)
{
left_a = token_leftarrow(piped[i]);
right_a = token_rightarrow(piped[i]);
if(left_a[1] != NULL)
{
free(piped[i]);
piped[i] = calloc(strlen(left_a[0]) + 1, sizeof(char));
strcpy(piped[i], left_a[0]);
fd[0][0] = open(left_a[1], O_RDONLY);
input = i;
}
if(right_a[1] != NULL)
{
free(piped[i]);
piped[i] = calloc(strlen(left_a[0]) + 1, sizeof(char));
strcpy(piped[i], right_a[0]);
fd[1][1] = open(right_a[1], O_WRONLY | O_CREAT, 0666);
output = i;
}
}
char ** spaced = token_space(piped[0]);
char ** spaced2 = NULL;
if(piped[1] != NULL)
{
spaced2 = token_space(piped[1]);
res = pipe(fd[2]);
if(res < 0)
{
printf("Pipe Failure\n");
exit(-1);
}// end if
}
if(fork() == 0)
{
if(background == 1)
setpgid(0, 0);
if(piped[1] != NULL)
{
close(fd[2][1]);
close(0);
dup(fd[2][0]);
if(output == 1 && right_a[1] != NULL)
{
dup2(fd[1][1], 1);
close(fd[1][1]);
}
execvp(spaced2[0], spaced2);
perror("Invalid Command");
exit(-1);
}
else
{
if(input == 0 || output == 0)
{
if(right_a[1] != NULL)
{
dup2(fd[1][1], 1);
close(fd[1][1]);
}
if(left_a[1] != NULL)
{
dup2(fd[0][0], 0);
close(fd[0][0]);
}
}
execvp(spaced[0], spaced);
perror("Invalid command\n");
exit(-1);
}
}
else
{
if(piped[1] != NULL)
{
if((pid = fork()) == 0)
{
close(fd[2][0]);
close(1);
dup(fd[2][1]);
if(input == 0 && left_a[1] != NULL)
{
dup2(fd[0][0], 0);
close(fd[0][0]);
}
execvp(spaced[0], spaced);
perror("Invalid Command");
exit(-1);
}
else
if(background == 0)
waitpid(pid, &status, WNOHANG);
}
else
if(background == 0)
wait(NULL);
close(fd[2][0]);
close(fd[2][1]);
close(fd[0][0]);
close(fd[1][1]);
dup2(stdin_c, 0);
dup2(stdout_c, 1);
}
}

Related

exec failed with fork() and execvp()

I am currently working on a program in which I have to write a shell in C. I am having trouble getting the fork() section of my program to work. Here is my code:
void execute_func(char** tok)
{
pid_t pid = fork();
if (pid == -1)
{
printf("\nERROR: forking child process failed\n");
return;
}
else if (pid == 0)
{
if (execvp(tok[0], tok) < 0)
{
printf("ERROR: exec failed\n");
}
exit(0);
}
else
{
wait(NULL);
return;
}
}
for example, if I am to type in any sort of function such as "ls" or "wc" it gives me the "ERROR: exec failed" message, which means that the fork() is not running correctly. This could be a small issue in my understanding of fork() but I am completely stumped.
here is my whole program:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
char str[129];
enum {NOT_FOUND=0,FOUND};
enum {false=0,true};
static char *ptr;
const char *del;
int ReadLine(char *, int , FILE *);
char *mystrtok(char* string,const char *delim)
{
int j,flag=NOT_FOUND;
char *p;
if(string != NULL)
{
ptr=string;
p=string;
}
else
{
if(*ptr == '\0')
return NULL;
p=ptr;
}
while(*ptr != '\0')
{
del=delim;
while(*del != '\0')
{
if(*ptr == *del)
{
if(ptr == p)
{
p++;
ptr++;
}
else
{
*ptr='\0';
ptr++;
return p;
}
}
else
{
del++;
}
}
ptr++;
}
return p;
}
void execute_func(char** tok)
{
pid_t pid = fork();
if (pid == -1)
{
printf("\nERROR: forking child process failed\n");
return;
}
else if (pid == 0)
{
if (execvp(tok[0], tok) < 0)
{
printf("ERROR: exec failed\n");
}
exit(0);
}
else
{
wait(NULL);
return;
}
}
int main()
{
int i;
char *p_str,*token;
char delim[10];
delim[0] = ' ';
delim[1] = '\t';
delim[2] = '\n';
delim[3] = '\0';
char cwd[1024];
char *tok[129];
while(1)
{
tok[0] = NULL;
fflush(stdin);
fflush(stdout);
printf("\n Enter a string to tokenize: ");
// printf("\n before scan");
fflush(stdin);
// printf("\n fflush");
ReadLine(str, 128, stdin);
/* scanf("%[^\n]",str); */
printf("\n after scan");
for (i = 1, p_str = str; ; i++, p_str = NULL)
{
token = mystrtok(p_str,delim);
if (token == NULL)
break;
printf("%d: %s\n",i,token);
tok[i-1] = token;
printf("%s\n",tok[i-1]);
}
if(tok[0] != NULL)
{
if(strcmp(tok[0],"cd") == 0)
{
if (chdir(tok[1]) != 0)
perror("chdir() error()");
getcwd(cwd, sizeof(cwd));
printf("current working directory is: %s\n", cwd);
}
else if(strcmp(tok[0],"pwd") == 0)
if (getcwd(cwd, sizeof(cwd)) == NULL)
perror("getcwd() error");
else
printf("current working directory is: %s\n", cwd);
else if(strcmp(tok[0],"exit") == 0)
exit(3);
else
{
execute_func(tok);
}
}
}
}
int ReadLine(char *buff, int size, FILE *fp)
{
buff[0] = '\0';
buff[size - 1] = '\0'; /* mark end of buffer */
char *tmp;
if (fgets(buff, size, fp) == NULL)
{
*buff = '\0'; /* EOF */
return false;
}
else
{
/* remove newline */
if ((tmp = strrchr(buff, '\n')) != NULL)
{
*tmp = '\0';
}
}
return true;
}
Problem appears to be here:
if (token == NULL)
break;
printf("%d: %s\n",i,token);
tok[i-1] = token;
The trailing NULL never gets set in tok thus resulting in execve not finding the end of the list. Like this should fix it:
tok[i-1] = token;
if (token == NULL)
break;
printf("%d: %s\n",i,token);

Removing empty strings in an array of strings and other strange data in C

I'm creating a shell in C and have implemented piping. This involves dividing the argument on the left side and right side of the pipe into two separate array of strings to be executed separately with execvp. For some reason, the array of strings pcmd2 contains data other than the argument I entered in the shell.
For example, I enter ls | sort into the shell after running the program and it outputs the following: sort: open failed: : No such file or directory
Seeing this, I suspected that the array of strings pcmd2 containing the sort command may have empty strings and other things that are being processed.
I print out pcmd2 with a for-loop (with a new line after each string) and I find the following printed onto the screen:
sort
UJ
There clearly seems to be extra stuff in there. Seems like empty strings but I have no clue where the UJ is coming from (this gets consistenly printed out).
I've tried setting the index in pcmd2 that's right after where sort is stored in the array to NULL. Like so, pcmd2[1] = NULL and that seemed to "fix" it since the weird extra data would no longer be processed by the execvp function. However, that means that I can't pipe commands with multiple arguments like grep myshell as it would only be able to take in the grep part.
I will show the full code below because I'm unsure how much context is needed in this case.
I know there's some very broken piping implementation here (I've tried some weird solution to get it work with two pipes) but I'm working to just get one pipe to work. I'm not asking for help piping in particular.
The most relevant part, I believe, is in the splitPipeCommands function.
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define MAX_ARGS 20
#define MAX_PIPE_ARGS 3
#define BUFSIZE 1024
int input, output, append, usePipe;
char *inputFile;
char *outputFile;
FILE *fp1;
FILE *fp2;
int get_args(char* cmdline, char* args[])
{
int i = 0;
/* if no args */
if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
return 0;
while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
if(i >= MAX_ARGS) {
printf("Too many arguments!\n");
exit(1);
}
}
/* the last one is always NULL */
return i;
}
void checkIORedirect(char* args[])
{
input = 0;
output = 0;
append = 0;
int i;
for(i = 0; args[i] != NULL; i++)
{
if(!strcmp(args[i], ">"))
{
output = 1;
outputFile = args[i+1];
args[i] = NULL;
}
else if(!strcmp(args[i], "<"))
{
input = 1;
inputFile = args[i+1];
args[i] = NULL;
//printf("inputFile: %s\n", inputFile);
}
else if(!strcmp(args[i], ">>"))
{
append = 1;
outputFile = args[i+1];
args[i] = NULL;
}
}
}
int getPipeIndexes(char* args[], int* pIndex1, int* pIndex2)
{
*pIndex1 = -1;
*pIndex2 = -1;
int i = 0;
int pcount = 0;
usePipe = 0;
for(i = 0; args[i] != NULL; i++)
{
if(!strcmp(args[i], "|") && pcount == 0)
{
//printf("First pipe!\n");
*pIndex1 = i;
args[i] = NULL;
pcount++;
usePipe = 1;
}
else if(!strcmp(args[i], "|") && pcount == 1)
{
//printf("Second pipe!\n");
*pIndex2 = i;
args[i] = NULL;
pcount++;
}
else if(!strcmp(args[i], "|") && pcount > 1)
{
printf("Too many pipes, only accepting first two!\n");
args[i] = NULL;
}
}
return pcount;
}
void splitPipeCommands(char* args[], int* pIndex1, int* pIndex2, int pcount, char* pcmd1[], char* pcmd2[], char* pcmd3[])
{
if(pcount == 1)
{
int i;
int cmdIndex = 0;
for(i = 0; i < *pIndex1; i++)
{
pcmd1[cmdIndex] = args[i];
cmdIndex++;
}
cmdIndex = 0;
//printf("2nd half of args: \n");
for(i = *pIndex1 + 1; args[i] != NULL; i++)
{
pcmd2[cmdIndex] = args[i];
//printf("%s\n", args[i]);
cmdIndex++;
}
//pcmd2[1] = NULL;
//*pcmd1 = args[0];
//*pcmd2 = args[*pIndex1 + 1];
/*TODO: del when you finish debugging*/
//printf("pcmd1=%s,%s,%s\n", pcmd1[0], pcmd1[1], pcmd1[2]);
//printf("pcmd2=%s\n", pcmd2[0]);
}
else if(pcount == 2)
{
int i;
int cmdIndex = 0;
for(i = 0; i < *pIndex1; i++)
{
pcmd1[cmdIndex] = args[i];
cmdIndex++;
}
cmdIndex = 0;
for(i = *pIndex1 + 1; i < *pIndex2; i++)
{
pcmd2[cmdIndex] = args[i];
cmdIndex++;
}
//pcmd2[1] = NULL;
cmdIndex = 0;
for(i = *pIndex2 + 1; args[i] != NULL; i++)
{
pcmd3[cmdIndex] = args[i];
cmdIndex++;
}
/*
*pcmd1 = args[0];
*pcmd2 = args[*pIndex1 + 1];
*pcmd3 = args[*pIndex2 + 1];
*/
/*TODO: del when you finish debugging*/
//printf("pcmd1=%s,", *pcmd1);
//printf("pcdm2=%s,", *pcmd2);
//printf("pcmd3=%s\n", *pcmd3);
}
}
void closeFiles()
{
if(fp1 != NULL)
{
fclose(fp1);
}
if(fp2 != NULL)
{
fclose(fp2);
}
}
void execute(char* cmdline)
{
int pid, pid2, async;
char* args[MAX_ARGS];
int nargs = get_args(cmdline, args);
if(nargs <= 0) return;
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
exit(0);
}
/* check if async call */
if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; }
else async = 0;
/*Pipe related*/
int pIndex1, pIndex2;
char* pcmd1[30];
char* pcmd2[30];
char* pcmd3[30];
int pcount = getPipeIndexes(args, &pIndex1, &pIndex2);
if(usePipe)
{
splitPipeCommands(args, &pIndex1, &pIndex2, pcount, pcmd1, pcmd2, pcmd3);
if(pcount == 1)
{
checkIORedirect(pcmd1);
checkIORedirect(pcmd2);
}
else if(pcount == 2)
{
checkIORedirect(pcmd1);
checkIORedirect(pcmd2);
checkIORedirect(pcmd3);
}
}
else
{
checkIORedirect(args);
}
/*TODO: del when finished debugging*/
//printf("pIndex1=%d,pIndex2=%d,pcount=%d\n", pIndex1, pIndex2, pcount);
//checkIORedirect(args);
if(output)
{
if((fp1 = fopen(outputFile, "w")) == NULL)
{
printf("Output to file failed!\n");
}
}
else if(append)
{
if((fp1 = fopen(outputFile, "a")) == NULL)
{
printf("Append to file failed!\n");
}
}
if(input)
{
if((fp2 = fopen(inputFile, "r")) == NULL)
{
printf("Input from file failed!\n");
}
}
int fd[2];
int fd2[2];
if(usePipe)
{
if(pipe(fd) == -1)
{
perror("pipe failed");
exit(1);
}
}
pid = fork();
if(pid == 0) { /* child process */
if(input)
{
//printf("Input\n");
dup2(fileno(fp2), STDIN_FILENO);
fclose(fp2);
input = 0;
}
if(output || append)
{
dup2(fileno(fp1), STDOUT_FILENO);
fclose(fp1);
output = 0;
append = 0;
//printf("Output or append\n");
}
if(usePipe)
{
//TODO: Make case for one pipe and two pipes
if(pcount == 2)
{
if(pipe(fd2) == -1)
{
perror("pipe failed");
exit(1);
}
pid2 = fork();
if(pid2 == -1)
{
perror("fork 2 failed");
exit(1);
}
if(pid2 == 0) //Child 2
{
close(fd[1]);
close(fd[0]);
close(fd2[1]);
dup2(fd2[0], 0);
close(fd2[0]);
execvp(pcmd2[0], pcmd2);
perror("exec second child failed");
exit(-1);
}
else //Child 1
{
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
close(fd2[0]);
dup2(fd2[1], 1);
close(fd2[1]);
execvp(pcmd3[0], pcmd3);
perror("exec first child failed");
exit(-1);
}
}
else if(pcount == 1)
{
//TODO: pipe for one pipe
//close(fd[1]);
//dup2(fd[0], 0);
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
printf("pcmd1=%s\n", pcmd1[0]);
execvp(pcmd1[0], pcmd1);
perror("exec child failed");
exit(-1);
}
}
else
{
execvp(args[0], args);
/* return only when exec fails */
perror("exec failed");
exit(-1);
}
/*
execvp(args[0], args);
// return only when exec fails
perror("exec failed");
exit(-1);
*/
} else if(pid > 0) { /* parent process */
if(usePipe)
{
pid=fork();
if(pid==0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
close(fd[0]);
printf("pcmd2 contents: \n");
int j;
for(j = 0; pcmd2[j] != NULL; j++)
{
printf("%s\n", pcmd2[j]);
}
//FIXME: Something's wrong with pcmd2
execvp(pcmd2[0], pcmd2);
//execlp("sort", "sort", (char*)NULL);
perror("exec second command failed");
}
else
{
int status;
close(fd[0]);
close(fd[1]);
waitpid(pid, &status, 0);
}
//wait(NULL);
/*
close(fd[0]);
dup2(fd[1], 1);
printf("pcmd1=%s\n", pcmd1[0]);
execvp(pcmd1[0], pcmd1);
perror("exec parent failed");
*/
}
if(!async)
{
waitpid(pid, NULL, 0);
}
else
{
printf("this is an async call\n");
}
} else { /* error occurred */
perror("fork failed");
exit(1);
}
}
int main (int argc, char* argv [])
{
char cmdline[BUFSIZE];
for(;;) {
printf("COP4338$ ");
if(fgets(cmdline, BUFSIZE, stdin) == NULL) {
perror("fgets failed");
exit(1);
}
execute(cmdline);
usePipe = 0;
//closeFiles();
fflush(stdout);
}
return 0;
}

tail exec doesn't work well in pipe (No output until parent process die!)

#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
int pipFd[1000][2], hasPipe, forked, pipNum = 0, pNum = 0, bPipe = 0, bpipFd[2], stPipe, InpToChld = 0;
pid_t pid[1000];
void * outInp;
int builtin_command(char **argv)
{
if (!strcmp(argv[0], "quit")) /* quit command */
exit(0);
if (!strcmp(argv[0], "&")) /* Ignore singleton & */
return 1;
return 0; /* Not a builtin command */
}
int parsecmd(char *buf, char **argv)
{
char *delim; /* Points to first space delimiter */
int argc; /* Number of args */
int bg; /* Background job? */
buf[strlen(buf) - 1] = ' '; /* Replace trailing '\n' with space */
while (*buf && (*buf == ' ')) /* Ignore leading spaces */
buf++;
/* Build the argv list */
argc = 0;
while ((delim = strchr(buf, ' ')))
{
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ' || *buf == '<')) /* Ignore spaces */
buf++;
}
argv[argc] = NULL;
if (argc == 0) /* Ignore blank line */
return 1;
/* Should the job run in the background? */
if ((bg = (*argv[argc - 1] == '&')) != 0)
argv[--argc] = NULL;
return argc;
}
void myExec(char **argv, char *buf)
{
// if ((pid[pNum] = fork()) == 0)
// {
strcpy(buf, "/bin/");
buf[5] = 0;
strcat(buf, argv[0]);
//printf("%s\n", buf);
if (execv(buf, argv) < 0)
{
memset(buf, 0, 255);
strcpy(buf, "/usr/bin/");
strcat(buf, argv[0]);
if (execv(buf, argv) < 0)
{
printf("exec failed\n");
exit(-1);
}
}
exit(0);
// }
// else
// wait(NULL);
}
int splitPipe(char **cmdLine)
{
static char *svBuftok;
if (!hasPipe)
*cmdLine = strtok_r(*cmdLine, "|", &svBuftok);
else{
//printf("--------%s\n", svBuftok);
*cmdLine = strtok_r(svBuftok, "|", &svBuftok);
}
//printf(".......................%s %s\n", svBuftok, *cmdLine);
return strlen(svBuftok);
}
int isDigit(char * strings){
int i, tmp = strlen(strings);
for(i = 0; i < tmp; i++){
if(strings[i] < '0' || strings[i] > '9') return 0;
}
return 1;
}
void handler(int sig)
{
if (sig == SIGINT && forked) exit(0);
}
static char *getcmd()
{
static char buf[256];
//fputs("> ", stdout);
//printf("asdfasdfasdf\n");
fflush(stdin);
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
return NULL;
if (buf[strlen(buf)] == '\n')
buf[strlen(buf)] = 0;
return buf;
}
int main()
{
char *cmdline;
char *argv[12000];
char c, dir[256], buf[256], rdBuf[100000], pipBuf[100000];
int status, fd, rfd, dest, argc;
pid_t tmpPid;
getcwd(dir, 256);
signal(SIGINT, handler);
signal(SIGTSTP, handler);
signal(SIGCHLD, handler);
signal(30, handler);
//outInp = &&Outinp;
//printf("gd\n");
while (cmdline = getcmd())
{
ret:
do
{
rfd = 0;
hasPipe = splitPipe(&cmdline);
//printf(":::::::::::::::%s %d\n", cmdline, hasPipe);
if (strlen(cmdline) <= 1)
continue;
argc = parsecmd(cmdline, argv);
if (!builtin_command(argv))
{
if(!strcmp(argv[0], "exit")) exit(0);
{
if(hasPipe) pipe(pipFd[pNum]);
if(!bPipe) pipe(bpipFd);
fflush(NULL);
if((pid[pNum] = fork()) == 0){
int ofd, svStdout = dup(1), svStin = dup(0);
forked = 1;
close(pipFd[pNum][0]);
//printf("%s %d\n",argv[0], getpid());
//fflush(stdout);
//printf("\n");
if(bPipe) {
close(pipFd[pNum - 1][1]);
dup2(pipFd[pNum - 1][0], STDIN_FILENO);
}
else{
close(bpipFd[1]);
dup2(bpipFd[0], STDIN_FILENO);
}
//addArgv(pipBuf, &argc, argv);
if(hasPipe) dup2(pipFd[pNum][1], 1);
if(!strcmp(argv[argc - 2], ">")){
//printf("chked %s\n", argv[argc - 1]);
remove(argv[argc - 1]);
ofd = open(argv[argc - 1], O_WRONLY | O_CREAT, 0755);
dup2(ofd, 1);
argc -= 2;
argv[argc] = NULL;
}
else if(!strcmp(argv[argc - 2], ">>")){
//printf("chked %s\n", argv[argc - 1]);
ofd = open(argv[argc - 1], O_WRONLY);
dup2(ofd, 1);
argc -= 2;
argv[argc] = NULL;
}
fflush(stdout);
myExec(argv, buf);
close(pipFd[pNum][1]);
if(bPipe) {
close(pipFd[pNum - 1][0]);
}
else{
close(bpipFd[0]);
}
dup2(svStin, 0);
dup2(svStdout, 1);
if(!strcmp(argv[argc - 2], ">")) close(ofd);
exit(0);
}
else{
if(!bPipe) {
close(bpipFd[0]);
stPipe = pid[pNum];
InpToChld = 1;
}
pNum++;
}
}
bPipe = hasPipe;
}
}while (hasPipe);
while(InpToChld){
memset(rdBuf, 0, sizeof(rdBuf)); int i;
fflush(NULL);
//printf("Inp~~\n");
if(read(0, rdBuf, sizeof(rdBuf)) == 0){
write(bpipFd[1], "\0", 1);
InpToChld = 0;
break;
}
if(write(bpipFd[1], rdBuf, strlen(rdBuf)) < 0){
cmdline = rdBuf;
InpToChld = 0;
goto ret;
}
fflush(NULL);
//fflush(stdout);
}
}
}
At first, exec to tail works well.
But tail's output doesn't be printed until parent process die.
for example, cat < /proc/meminfo | head works well
but cat < /proc/meminfo | tail is printed after exit parent.
I guess this is about input / output problem, but I can't solve this problem.
The steps to code a tiny shell :
handle SIGCHLD
if necessary handle redirect
if necessary handle pipe
The following code could work, not handle builtin command:
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void signal_SIGCHLD_handle(int sig) {
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
int main()
{
char buf[256];
while (fgets(buf, sizeof(buf), stdin) != NULL) {
if (fork() > 0) { // parent
wait(NULL);
continue;
}
//child
// handle SIGCHLD
struct sigaction act;
act.sa_handler = signal_SIGCHLD_handle;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
sigaction(SIGCHLD, &act, NULL);
if (buf[strlen(buf)] == '\n')
buf[strlen(buf)] = '\0';
for (char* next_cmd = strtok(buf, "|"); next_cmd != NULL; ) {
char* current_cmd = next_cmd;
next_cmd = strtok(NULL, "|");
char* argv[10];
int argv_index = 0;
int new_argv = 1;
for (;;) {
if(*current_cmd == '\0') {
argv[argv_index] = NULL;
break;
}
if (isspace(*current_cmd)) {
*current_cmd++ = '\0';
new_argv = 1;
continue;
}
if (*current_cmd == '<') {
++current_cmd;
while (isspace(*current_cmd))
++current_cmd;
if (*current_cmd == '\0') {
printf("Please use cmd < file_name");
return -1;
}
char* filename = current_cmd;
while (!isspace(*current_cmd) && *current_cmd != '\0')
++current_cmd;
if (*current_cmd != '\0')
*current_cmd++ = '\0';
int fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("<");
return -1;
}
dup2(fd, 0);
close(fd);
continue;
}
if (*current_cmd == '>') {
int add = 0;
if (*++current_cmd == '>') {
add = 1;
++current_cmd;
}
while (isspace(*current_cmd))
++current_cmd;
if (*current_cmd == '\0') {
printf(add == 0 ? "Please use cmd > file_name" : "Please use cmd >> file_name");
return -1;
}
char* filename = current_cmd;
while (!isspace(*current_cmd) && *current_cmd != '\0')
++current_cmd;
if (*current_cmd != '\0')
*current_cmd++ = '\0';
int fd = open(filename, add == 0 ? (O_WRONLY|O_CREAT) : (O_WRONLY|O_CREAT|O_APPEND), 0644);
if (fd < 0) {
perror(add == 0 ? ">" : ">>");
return -1;
}
dup2(fd, 1);
close(fd);
continue;
}
if (new_argv == 1) {
new_argv = 0;
argv[argv_index++] = current_cmd;
}
++current_cmd;
}
if (argv_index == 0)
continue;
if (next_cmd != NULL) {
int pipe_fd[2];
pipe(pipe_fd);
if (fork() == 0) {
close(pipe_fd[0]);
dup2(pipe_fd[1], STDOUT_FILENO);
close(pipe_fd[1]);
execvp(argv[0], argv);
return -1;
}
close(pipe_fd[1]);
dup2(pipe_fd[0], STDIN_FILENO);
close(pipe_fd[1]);
continue;
}
execvp(argv[0], argv);
}
}
return 0;
}

error when passing arguments to execvp()

I have been struggling with this for hours, when I type in "ls" and pass the argument args to the execvp() function I get the error message "ls: cannot access '': No such file or directory". when I print the char arrays in args it all seems to be in correctly but when I manually type args[0] = "ls' and args[1] = "-l" everything works. Here is the code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*
*
*/
int main() {
char buffer[25];
char exit[5] = {'e', 'x', 'i', 't', '\0'};
while(strcmp(buffer, exit) != 0){
//save stdin to buffer
int i = 0, numwords = 0, k = 0, l = 0, j =0;
char ** args;
memset(buffer, 0, strlen(buffer));
printf("?: ");
while((buffer[i] = getchar()) != '\n'){
i++;
}
buffer[i] = '\0';
i = 0;
printf("%s", buffer);
//start parsing and save to a char[][] args
//get the number of words
while( buffer[i] != '\0'){
if(buffer[i] == ' '){
i++;
}
else if(buffer[i] != ' ' && buffer[i] != '\0'){
numwords++;
while(buffer[i] != ' ' && buffer[i] != '\0'){
i++;
}
}
else{
i++;
}
}
numwords += 1; //for the null char '\0'
args = malloc((sizeof(char*))*numwords);
i = 0;
while(i < numwords){
args[i] = malloc((sizeof(char))*(50));
i++;
}
i = 0;
printf("%d", numwords);
//get each word from buffer and save it to temp then save temp to the array
while( buffer[i] != '\0'){
char temp[15];
if(buffer[i] == ' '){
i++;
}
else if(buffer[i] != ' ' && buffer[i] != '\0'){
while(buffer[i] != ' ' && buffer[i] != '\0'){
temp[k] = buffer[i];
k++;
i++;
}
}
else{
i++;
}
if(k != 0){
int j = 0;
while(j < k){
(args)[l][j] = temp[j];
j++;
}
(args)[l][j] = '\0';
memset(temp, 0 , strlen(temp));
k = 0;
l++;
}
}
(args)[l][0] = '\0';
execvp(args[0], args);
//check printing!
// for(i = 0; i< numwords; i++){
// for(j = 0; j< numwords-1; j++){
// printf("%s", args[j]);
// }
// printf("%s", args[1]);
// printf("%s", args[0]);
}
printf("Logout Success\n");
return 0;
}
static void free_string_list(char** strings) {
if (!strings)
return;
for (char** p = strings; *p; ++p)
free(*p);
free(strings);
}
// Correctly handles leading spaces.
// Correctly handles trailing spaces.
// Correctly handles multiple spaces.
// Return NULL and sets errno on error.
// Free result with free_string_list otherwise.
static char** split_into_words(const char* s) {
size_t count = 0;
{
const char *src = s;
while (1) {
while (*src && *src == ' ')
++src;
if (!*src)
break;
while (*src && *src != ' ')
++src;
++count;
}
}
char** words = malloc(sizeof(char*) * (count+1));
if (!words)
goto ERROR;
{
const char *src = s;
char** dst = words;
while (1) {
while (*src && *src == ' ')
++src;
if (!*src)
break;
const char* src_s = src;
while (*src && *src != ' ')
++src;
const char* src_e = src;
size_t len = src_e - src_s;
char* dst_word = *dst = malloc(len+1);
if (!dst_word)
goto ERROR;
memcpy(dst_word, src_s, len);
dst_word[len] = 0;
++dst;
}
*dst = NULL;
}
return words;
ERROR:
free_string_list(words);
return NULL;
}
// Correctly handles exec failures (e.g. command not found and permission errors).
// Outputs error message on error.
// -1 = Error creating child or executing command.
// 0 = ok.
// 1 = Child killed or program returned an error.
int execute_command(char** cmd) {
int pipefd[2]; // r,w
pipefd[0] = -1;
pipefd[1] = -1;
if (pipe(pipefd) == -1) {
perror("pipe");
goto EXEC_ERROR;
}
{
int flags = fcntl(pipefd[1], F_GETFD, 0);
if (flags == -1) {
perror("fcntl F_GETFD");
goto EXEC_ERROR;
}
if (fcntl(pipefd[1], F_SETFD, flags | FD_CLOEXEC) == -1) {
perror("fcntl F_SETFD");
goto EXEC_ERROR;
}
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
goto EXEC_ERROR;
}
if (pid == 0) {
close(pipefd[0]);
execvp(cmd[0], cmd);
int exec_errno = errno;
perror("exec");
write(pipe, &exec_errno, sizeof(exec_errno));
_exit(1);
}
close(pipefd[1]);
pipefd[1] = -1;
{
int exec_errno;
ssize_t bytes_read = read(pipefd[0], &exec_errno, sizeof(exec_errno));
if (bytes_read == -1) {
perror("read");
goto EXEC_ERROR;
}
if (bytes_read != 0) {
errno = exec_errno;
perror("exec");
goto EXEC_ERROR;
}
}
close(pipefd[0]);
pipefd[0] = -1;
int status;
if (waitpid(pid, &status, 0) == -1) {
perror("waitpid");
goto EXEC_ERROR;
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Child killed by signal %d\n", WTERMSIG(status));
goto PROGRAM_ERROR;
}
if (WEXITSTATUS(status) > 0) {
fprintf(stderr, "Child exited with error %d\n", WEXITSTATUS(status));
goto PROGRAM_ERROR;
}
return 0;
EXEC_ERROR:
if (pipefd[0] != -1) close(pipefd[0]);
if (pipefd[1] != -1) close(pipefd[1]);
return -1;
PROGRAM_ERROR:
return 1;
}
int main(void) {
int rv = 1;
char* line = NULL;
size_t line_size = 0;
char** cmd = NULL;
while (1) {
ssize_t num_read = getline(&line, &line_size, stdin);
if (num_read == -1) {
if (errno) {
perror("getline");
goto ERROR;
}
break;
}
if (line[num_read-1] == '\n')
line[num_read-1] = 0;
char** cmd = split_into_words(line);
if (!cmd) {
perror("malloc");
goto ERROR;
}
if (strcmp(cmd[0], "exit") == 0)
break;
if (execute_command(cmd) == -1)
goto ERROR;
free_string_list(cmd);
cmd = NULL;
}
rv = 0;
ERROR:
free_string_list(cmd);
free(line);
return rv;
}
Untested.
Simple answer really, when I added the null character to the end of the list of arrays I put:
args[l][0] = '\0'.
What I needed to put was just
args[l] = '\0'.
I did this and it worked!

minishell malloc error with EXC_BAD_ACCESS

Hi I've recently started learning unix system programming.
I'm trying to create a minishell in c but when I run my code,
I always get:
EXC_BAD_ACCESS (code=EXC_I386_GPFLT
Don't really know what's wrong here. Searched online they say it's something wrong with malloc, but I don't see what's wrong.
Can someone help me with this problem?
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include "minishell.h"
char promptString[] = "mysh>";
struct command_t command;
int enviromentlength;
int commandlength;
char *pathv[MAX_PATHS];
//to display the prompt in the front of every line
void printPrompt()
{
printf("%s", promptString);
}
//get the user's command
void readCommand(char *buffer)
{
gets(buffer);
}
//get the environment variable and store in a pathEnvVar
int parsePath( char* dirs[] )
{
char* pathEnvVar;
char* thePath;
int i;
for(i = 0; i < MAX_ARGS; i++)
{
dirs[i] = NULL;
}
i = 0;
//use system call to get the environment variable
pathEnvVar = (char*) getenv("PATH");
//printf("%s\n", pathEnvVar);
thePath = (char*) malloc(strlen(pathEnvVar) + 1);
strcpy(thePath, pathEnvVar);
//splict the variable and store in the pathv
char *temp = strtok(thePath, ":");
dirs[i] = temp;
while(temp != NULL)
{
i++;
temp = strtok(NULL, ":");
if(temp == NULL)
{
break;
}
else
{
dirs[i] = temp;
}
}
dirs[i+1] = NULL;
return i;
}
//get the user's command and parameters
int parseCommand(char * commandline)
{
int i = 0;
char* temp;
temp = strtok(commandline, " ");
while(temp != NULL)
{
command.argv[i] = temp;
i++;
temp = strtok(NULL, " ");
}
command.argv[i] = NULL;
return i;
}
//input the user's command to
//fix the absolute path of the command
char* lookupPath(char* dir[], char* command[])
{
char* result = NULL;
int i;
//printf("%c\n", *command.argv[0]);
//if the command is already an absolute path
if(*command[0] == '/')
{
result = command[0];
//printf("test\n");
if( access(result, X_OK) == 0)
{
return result;
}
else
{
fprintf(stderr, "%s: command not found\n", result);
return NULL;
}
}
//if the command is not an absolute path
else
{
for(i = 0; i < enviromentlength; i++)
{
char *temp = (char *) malloc (30);
strcpy(temp, dir[i]);
strcat(temp, "/");
strcat(temp, command[0]);
result = temp;
if( access(result, X_OK) == 0)
{
return result;
}
}
fprintf(stderr, "%s: command not found\n", result);
return NULL;
}
}
//to change the directory and
//display the absolute path of the current directory
void do_cd(char* dir[])
{
char currentdirectory[MAX_PATHS];
if(dir[1] == NULL || (strcmp(dir[1], ".") == 0))
{
printf("director does not change\n");
//printf("The current directory is:%s", currentdirectory);
}
else
{
if(chdir(dir[1]) < 0)
{
printf("change director error\n");
}
else
{
printf("change director success\n");
}
}
getcwd(currentdirectory, MAX_PATHS);
printf("The current directory is:%s\n", currentdirectory);
}
//redirection the result to file
void redirection(char* command, char* commandcontent[], int position, pid_t thisChPID)
{
char* content[commandlength - 1];
char* filename = (char *) malloc(MAX_PATH_LEN);
FILE* fid;
int i = 0;
int stat;
strcpy(filename, commandcontent[position + 1]);
//printf("%s\n", commandcontent[position + 1]);
for(i = 0; i < position; i++)
{
content[i] = commandcontent[i];
//printf("content: %s\n", content[i]);
}
content[i + 1] = NULL;
for(i = 0; i< position + 1; i++)
{
printf("%s\n", content[i]);
}
printf("%s\n", command);
if((thisChPID=fork()) < 0)
{
fprintf(stderr, "fork failed\n");
}
else if(thisChPID == 0)
{
fid = open(filename, O_WRONLY || O_CREAT);
close(1);
dup(fid);
close(fid);
execve(command, content, pathv);
}
else
{
wait(&stat);
}
}
//use pipe to run the program
void piperun(char* command, char* commandcontent[], int position, pid_t thisChPID)
{
printf("%s\n%d\n", command, position);
char* firstcommand[position+1];
char* secondcommand[commandlength-position];
char* result = (char *) malloc(MAX_PATH_LEN);
pid_t child;
//the pipe name
int pipeID[2];
int j;
for(j = 0; j< position; j++)
{
firstcommand[j] = commandcontent[j];
printf("%s\n", firstcommand[j]);
}
firstcommand[j] = NULL;
printf("length: %d\n", commandlength-position);
for(j = 0; j < (commandlength-position); j++)
{
secondcommand[j] = commandcontent[position + 1 + j];
printf("second:%s\n",secondcommand[j]);
}
//secondcommand[j+1] = NULL;
result = lookupPath(pathv, secondcommand);
//printf("%s\n", secondcommand[0]);
printf("%s\n", result);
//create pipe "pipeID"
if(pipe(pipeID)==-1)
{
printf("Fail to creat pipe.\n");
}
if((thisChPID=fork())==-1)
{
printf("Fail to creat child process.\n");
}
if(thisChPID==0)
{
printf("in the child\n");
close(1);
dup(pipeID[1]);
close(pipeID[0]);
close(pipeID[1]);
if(execve(command, firstcommand, pathv)==-1)
{
printf("Child process can't exec command %s.\n",firstcommand[0]);
}
}
else
{
child = fork();
if((child=fork())==-1)
{
printf("Fail to creat child process.\n");
}
if(child==0)
{
close(0);
dup(pipeID[0]);
close(pipeID[1]);
close(pipeID[0]);
if(execve(result, secondcommand, pathv)==-1)
{
printf("Child process can't exec command %s.\n",secondcommand[0]);
}
}
else
{
wait(NULL);
}
}
}
int main()
{
char commandLine[LINE_LEN];
int child_pid; //child process id
int stat; //used by parent wait
pid_t thisChPID;
char *arg[MAX_ARGS];
//the flag of redirection, piping and background running
int redirectionsituation = 0;
int pipesituation = 0;
int background = 0;
char * tempchar;
//Command initialization
int i;
for(i = 0; i < MAX_ARGS; i++ )
{
command.argv[i] = (char *) malloc(MAX_ARG_LEN);
}
//get all directories from PATH env var
enviromentlength = parsePath(pathv);
//Main loop
while(TRUE)
{
redirectionsituation = 0;
pipesituation = 0;
background = 0;
//Read the command line
printPrompt();
readCommand(commandLine);
//input nothing
if(commandLine[0] == '\0')
{
continue;
}
//quit the shell?
if((strcmp(commandLine, "exit") == 0) || (strcmp(commandLine, "quit") == 0))
{
break;
}
//if it is background running
if(commandLine[strlen(commandLine) - 1] == '&')
{
printf("backgrond\n");
tempchar = strtok (commandLine, "&");
//strcpy(commandLine, tempchar);
printf("%s\n", tempchar);
background = 1;
}
//Parse the command line
commandlength = parseCommand(commandLine);
//if the command is "cd"
if(strcmp(command.argv[0], "cd") == 0)
{
do_cd(command.argv);
continue;
}
//Get the full path name
command.name = lookupPath(pathv, command.argv);
printf("command name %s\n", command.name);
//report error
if( command.name == NULL)
{
continue; //non-fatal
}
//if redirection is required
for(i = 0; i < commandlength; i++)
{
if(strcmp(command.argv[i], ">") == 0)
{
redirectionsituation = 1;
break;
}
}
if(redirectionsituation == 1)
{
redirection(command.name, command.argv, i, thisChPID);
continue;
}
//if pipe is required
for(i = 0; i < commandlength; i++)
{
if(strcmp(command.argv[i], "|") == 0)
{
pipesituation = 1;
break;
}
}
if(pipesituation == 1)
{ //run pipe
piperun(command.name, command.argv, i, thisChPID);
continue;
}
//normal running
if((thisChPID=fork()) < 0)
{
fprintf(stderr, "fork failed\n");
}
else if(thisChPID == 0)
{
//printf("run again\n");
execve(command.name, command.argv, pathv);
}
else
{
//do not put the process in the background, wait until the child process terminates
if(background == 0)
{
wait(&stat);
}
}
}
return 0;
}
Run it in a debugger and see where you are dereferencing a null.

Resources