Segmentation fault C in writing a shell program - c

My code worked fine until I add the extra stuffs into it, like identifying and deal with cd, >, <, >> and |. Could you please check my code and see where did the error happens?
Btw the requirement of the assignment is only up to 1 pipe. And I think the problem starts somewhere from the for loop, cause I put printf right after it to check if it would print args[k], and it did but then the bug popped up and the program stopped.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
const int MAX_ARGS = 10;
char *token[MAX_ARGS + 1]; /*assume max number of token <=10*/
char *temp;
char line[256], command[MAX_ARGS];
char prompt[] = "sh2 % ";
pid_t pid;
int i=0, j,k, status;
printf("%s", prompt);
while (fgets(line, sizeof line, stdin) != NULL)
{
line[strlen(line)-1] = '\0'; /*get rid of \n*/
token[0] = strtok(line," ");
strcpy(command,token[0]);
temp = strtok(NULL," ");
while (temp != NULL)
{
i = i+1;
token[i] = temp;
temp = strtok(NULL," ");
}
char *args[i+2];
for (j = 0; j < (i+1); j++)
{
args[j] = token[j];
}
args[i+1] = NULL;
if (!strcmp("exit",command))
exit(0);
if (!strcmp("cd", command))
{
int success;
if (success = chdir(args[1]) <0)
{
printf("Failed to change dir.\n");
}
}
else
{
int piping = 0;
int fd;
for (k = 0; k < sizeof args; k++)
{
if (!strcmp(">",args[k]))
{
fd = open(args[k+1],O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
if (fd <0) { printf("Open file failed.\n");}
else
{
dup2(fd, 1);
args[k] = '\0';
fflush(stdout);
close(fd);
}
}
if (!strcmp("<", args[k]))
{
fd = open(args[k+1], O_RDONLY);
if (fd <0) {printf("Open file failed.\n");}
else
{
dup2(fd, 0);
args[k] = '\0';
close(fd);
}
}
if (!strcmp(">>", args[k]))
{
fd = open(args[k+1], O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
if (fd <0) {printf("Open file failed");}
else
{
dup2(fd,1);
args[k] = '\0';
fflush(stdout);
close(fd);
}
}
if (!strcmp("|", args[k]))
{
piping = 1;
}
} /*end for*/
if (!(piping))
{
pid = fork();
if (pid <0) {}
else if (pid == 0)
{
if ( (status = execvp(command, args)) < 0 )
{
printf("Command not found\n");
}
}
else /*parent*/
{
wait(&status);
} /*end parent*/
}/*end if not pipe*/
else /*its pipe*/
{
int pfd[2];
pipe(pfd);
fd = fork();
if (fd < 0) {}
else if (fd ==0)
{
close(pfd[1]);
dup2(pfd[0],0);
close(pfd[0]);
execvp(args[2],args[2]);
}
else /*parent*/
{
close(pfd[0]);
dup2(pfd[1],1);
close(pfd[1]);
execvp(args[0],args[0]);
}
}/*end pipe*/
} /*end outter else*/
printf("%s", prompt);
}/*end while*/
return 0;
}

for (k = 0; k < sizeof args; k++)
This is not how you iterate through args: this will go far beyond the end of the array. You want something like:
num = sizeof(args) / sizeof(*args);
for (k = 0; k < num; k++)
Alternatively, since you set the last element as NULL, you could do
for (char **arg = args; *arg; arg++)
Also note that you iterate with k until the end of the array and then use k + 1, which is very likely to cause problems.

Related

Custom shell in C: redirection I/O

I am currently trying to create my own (simple) shell - in C language - but I have a hard time with redirection I/O.
I compile and run in VMware(gcc ,and if i want to execute the command :
$cat < file_in.txt > file_out.txt
I get :
File errorcat: '<': No such file or directory
cat: file_in.txt: No such file or directory
Function in header file:
void fileRedirect(char **args, int k, int ioMode)
{
pid_t pid, wpid;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int fd, status = 0;
pid = fork();
if (pid == 0) {
// Child process
if(ioMode == 0) // Input mode
fd = open(args[k+1], O_RDONLY, mode);
else // Output mode
fd = open(args[k+1], O_WRONLY|O_CREAT|O_TRUNC, mode);
if(fd < 0)
fprintf(stderr, "File error");
else {
dup2(fd, ioMode); // Redirect input or output according to ioMode
close(fd); // Close the corresponding pointer so child process can use it
args[k] = NULL;
args[k+1] = NULL;
if (execvp(args[0], args) == -1) {
perror("SHELL");
}
exit(EXIT_FAILURE);
}
}
else if (pid < 0) { // Error forking process
perror("SHELL");
}
else {
// Parent process. Wait till it finishes execution
do {
wpid = waitpid(pid, &status, WUNTRACED);
}
while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
}
And .c code :
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "functions.h"
#define BUFSIZE 64 // Size of a single token
#define TOKEN_DELIM " \t\r\n\a" // Token Delimiters
void main(){
char *command = NULL, cwd[1024], *pwd;
char **args, *options[4] = {"<", ">"};
int k = 0, option, found;
pwd = NULL;
pwd = getenv("HOME");
do
{
ssize_t bsize = 0;
found = 0;
printf("$");
if(getcwd(cwd, sizeof(cwd)) != NULL)
{
printf("%s$", cwd);
}
getline(&command, &bsize, stdin);
if(endOfFile(command) == 0)
{
printf("\n");
break;
}
if(charLimit(command) == 0)
{
printf("\n");
break;
}
args = splitTokens(command);
if(args[0] == NULL)
{
free(command);
free(args);
continue;
}
if(strcmp(args[0],"exit") == 0)
{
break;
}
else if(strcmp(args[0], "cd") == 0)
{
if(args[1] == NULL)
{
if(pwd[0] != 0)
{
chdir(pwd);
}
}
else
{
chdir(args[1]);
}
}
else
{
k = 1;
while(args[k] != NULL) { // Check for any of the redirect or process operators <,<,|,&
for(option = 0; option < 2; option++) {
if(strcmp(args[k],options[option]) == 0)
break;
}
if(option < 2) {
found = 1;
if(args[k+1] == NULL) { // For IORedirect and Pipe, argument is necessary
fprintf(stderr, "SHELL: parameter missing\n");
break;
}
fileRedirect(args, k, option);
}
k++;
}
if(found == 0)
execute(args, 0); // Start a foreground process or command
}
free(command);
free(args);
} while(1);
}
I have seen many answers in similar question,but they made me more confused.
Thank you in advance .

shm_open segmentation fault and permission failed

I am new to Linux and i am trying to create a shared memory object which stores the collatz conjecture calculated in child process and prints it in parent process. I have already read the man pages for the commands.
When I create the object it prints permission denied and segmentation fault(core dumped). Only one time it somehow passed that step then I got the mapping failed error.I am using ubuntu 18.04 on a virtual machine
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>
#include<sys/stat.h>
int main(int argc,char** argv)
{
pid_t pid;
pid = fork();
int page_size = 4096;
char obj[] = "name";
int num = atoi(argv[1]);
if(num < 1)
{
printf("Please input a greater number\n");
return 0;
}
if(pid < 0)
{
fprintf(stdout,"Fork failed\n");
}
else if(pid == 0)
{
int fd1 = 0;
void *ptr1 = NULL;
fd1 = shm_open(obj,O_CREAT|O_TRUNC,S_IRWXU);
if (fd1 == -1)
{
perror("error:");
exit(0);
}
ftruncate(fd1,page_size);
ptr1 = mmap(0,page_size,PROT_WRITE,MAP_SHARED,fd1,0);
if(ptr1 == MAP_FAILED)
{
fprintf(stdout,"Mapping failed");
exit(0);
}
else
{
while(num != 1)
{
if(num%2 == 0)
num = num / 2;
else
num = (num * 3) + 1;
sprintf(ptr1,"%d, ",num);
}
}
}
else
{
wait(NULL);
int fd = 0;
void *ptr = NULL;
shm_open(obj,O_RDONLY,S_IRWXU);
ptr = mmap(0,page_size,PROT_READ,MAP_SHARED,fd,0);
char *pr = (char *)ptr;
fprintf(stdout,pr);
shm_unlink(obj);
}
return 0;
}
Your code needs a couple of changes, some for better programming practices and some for fixing the actual functionality:
#include <all.h>
int main(int argc, char **argv) {
pid_t pid;
pid = fork();
int page_size = 4096;
char obj[] = "name";
// check argc otherwise you'll segfault
if (argc < 2) {
printf("Please provide a second argument.\n");
// if the program errors it should not return 0
return 1;
}
int num = atoi(argv[1]);
if (num < 1) {
printf("Please input a greater number\n");
return 1;
}
if (pid < 0) {
// no need for fprintf(stdout
printf("Fork failed\n");
} else if (pid == 0) {
int fd1 = 0;
char *ptr1 = NULL;
// we need to open with O_RDWR otherwise mmap will fail
fd1 = shm_open(obj, O_CREAT | O_TRUNC | O_RDWR, 0666);
if (fd1 == -1) {
perror("shm_open error");
return 1;
}
ftruncate(fd1, page_size);
// cast it to char* directly, no need for void pointer stuff
ptr1 = (char *) mmap(0, page_size, PROT_WRITE, MAP_SHARED, fd1, 0);
if (ptr1 == MAP_FAILED) {
// perror for debugging
perror("mmap error");
printf("Mapping failed.\n");
return 1;
} else {
while (num != 1) {
if (num % 2 == 0)
num = num / 2;
else
num = (num * 3) + 1;
// we need to shift the pointer so it stops truncating
ptr1 += sprintf(ptr1, "%d, ", num);
}
}
} else {
wait(NULL);
// move these to one like and cast to char* directly
int fd = shm_open(obj, O_RDONLY, S_IRWXU);
char *ptr = (char*) mmap(0, page_size, PROT_READ, MAP_SHARED, fd, 0);
// no need for fprintf(stdout, and also use %s\n as a format to add a newline
printf("%s\n", ptr);
shm_unlink(obj);
}
return 0;
}

Piping user input into commands in C

So I have created a simple shell program which takes input from the user through cmd, and then creates a new task and executes the input as a command. For example:
ls -l /home/user/Downloads | sort –u > listing.txt
But my program can only handle one piping. Can I somehow make it work with no limitations to pipings?? For example:
ls -l /home/user/Downloads | sort –u | wc –l > count.txt
So far I have been unable to implement something like that.
Here is my code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
pid_t pid1,pid2,waitPid;
int status,n,j,k,in,out,pdes[2],current_out,current_in,returnValue;
char *token;
char array[256];
char *temp[256];
char *temp2[256];
current_out=dup(1);
current_in=dup(0);
for(;;)
{
if(dup2(current_out,1) < 0)
{
printf("Error restoring output");
}
if(dup2(current_in, 0) < 0)
{
printf("Error restoring input");
}
int i=0;
k=0;
printf("mysh4>");
int check = scanf("%d",&n);
if(check == EOF)
{
printf("You now exit.\n");
break;
}
else
{
fgets(array,sizeof(array),stdin);
array[strcspn(array,"\n")] = 0;
token = strtok(array, " ");
while(token != NULL)
{
temp[i] = token;
token = strtok(NULL, " ");
i++;
k++;
}
temp[i]= NULL;
int p=0;
int qq;
for(i=0;i<k;i++)
{
if(strcmp(temp[i],"|") == 0)
{
for(qq=i+1; qq<k; qq++)
{
temp2[p]=temp[qq];
temp[qq-1] = NULL;
printf("Temp2[%d] is: %s\n" , p , temp2[p]);
printf("Temp1[%d] is: %s\n" , p , temp[p]);
p++;
}
temp2[p]= NULL;
}
}
returnValue=pipe(pdes);
if (returnValue == -1)
{
printf("ERROR: Pipe command failed.\n");
return -1;
}
close(pdes[0]);
close(pdes[1]);
pid1 = fork();
if (pid1 < 0)
{
perror("ERROR: Fork failed.\n");
return -1;
}
//child process
if (pid1 == 0)
{
close(pdes[0]);
dup2(pdes[1],1);
printf("Array:%s", array);
execvp(array,temp);
printf("Invalid Code\n");
return -1;
}
pid2 = fork();
if(pid2 < 0)
{
perror("Error: Fork2 failed.\n");
return -1;
}
//child process 2
if(pid2 == 0)
{
close(pdes[1]);
dup2(pdes[0],0);
for(j=0;j<k;j++)
{
if(strcmp(temp[j],">") == 0)
{
out=j+1;
printf("Output is %s\n", temp[out]);
temp[j] = temp[out];
pdes[1]=open(temp[out],
O_APPEND | O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(dup2(pdes[1],1) < 0)
{
printf("error in dup2");
return -1;
}
}
}
execvp(temp2[0],temp2);
printf("Invalid Code 2\n");
return -1;
}
//father process
else
{
//father waits child process to finish
waitPid = wait(NULL);
if (waitPid == -1)
{
perror("ERROR: Waitpid failed.\n");
return -1;
}
}
}
}
return 0;
}

Creating a shell

When i run the program with the ls > filename command it prints the results in the file but after that it doesn't respond to other commands like ls etc . Here is the code :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
void execute(char **argv, char ** k, int *l)
{
pid_t pid;
int file_descriptor;
int status;
int file;
if ((pid = fork()) < 0)
{
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0)
{
if (l = 1)
{
file = open(k, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
file_descriptor = dup2(file, 1);
}
if (execvp(argv[0], argv) < 0)
{
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else
{
while (wait(&status) != pid)
;
}
}
void main(void)
{
char **name = (char**) malloc(256 * sizeof(char));
char line[1024];
char ** k;
int file;
int i;
int *l;
while (1)
{
l = 0;
i = 0;
printf("myShell3 -> ");
gets(line); // gets the input command from user
printf("\n");
name[i] = strtok(line, " ");
while (name[i] != NULL)
{
if (strcmp(name[i], ">") == 0)
{
i++;
l = 1;
k = strtok(NULL, " ");
name[i - 1] = name[i];
}
else if (!strcmp(name[i], "<"))
{
i++;
name[i] = strtok(NULL, " ");
name[i - 1] = name[i];
}
else
{
i++;
name[i] = strtok(NULL, " ");
}
}
execute(name, k, l);
}
}
I have spent many hours on how to fix this but didnt find solution . Any help would be very much appreciated

Implementing unlimited piping in shell using C

I'm trying to implement a C shell that allows for unlimited unidirectional pipes using the character '>'
So it can handle ls -A > tail > grep '.zip'
I understand that pipes are supposed to talk between processes, but I thought I came up with an idea that could use one pipe and multiple children.
This is what I have so far
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
/*#include <wait.h>*/
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int runCommand(char **arguments, int *fd, int pipeHasSomeData, int baseCase) {
pid_t pid;
int x = 0;
int status;
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);
if(baseCase) {
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal");
}
printf("\n \n");
}
return 1;
} else {
if(pipeHasSomeData == 1) {// read from the pipe
dup2(fd[0], 0);//read from pipe
}
if(baseCase == 0) {// not the base case
dup2(fd[1], 1);//write to pipe
} else {
close(fd[1]);//close write
}
exit(execvp(arguments[0], arguments));
return 0;
}
}
int execute_commands(char *arguments[1000][1000], int pd[2] = NULL) {
int current_count = args_count;
int iterator = 0;
int fd[2];
int useAPipeInCommand = 0;
pipe(fd);
while(iterator <= args_count) {//go through and execute all the commands
if(current_count == 0) {//base case
return runCommand(arguments[iterator], fd, useAPipeInCommand, 1);
} else {
runCommand(arguments[iterator], fd, useAPipeInCommand, 0);
useAPipeInCommand = 1;
}
iterator++;
current_count--;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}
It is running the single base case no problem but the shell freezes when I do a pipe. Any ideas on the issue?
Correct answer from Comments by #beau-bouchard and #rici:
Pipes have a (small) finite buffer; you cannot write more than a little bit to the pipe without blocking unless the other end of the pipe is being read.
For a correct implementation, check out "multiple pipes in C" Coding multiple pipe in C
--UPDATE:
Here is my final working code for anyone that is having a similar issue:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
int READ = 0;
int WRITE = 1;
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int execute_commands(char *arguments[1000][1000]) {
int pd[2];
int iterator = 0;
int fd[2];
int f_in = 0;
while(iterator <= args_count) {//go through and execute all the commands
pid_t pid;
int status;
pipe(fd);
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);//wait for child to exit
close(fd[WRITE]);//close the writing end
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[iterator][0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal %d", status);
}
f_in = fd[READ];//set the pipe to the in
if(iterator == args_count) {
printf("\n \n");
}
//return 1;
} else {
dup2(f_in, 0);
if(iterator != args_count) {//its not the main value
dup2(fd[WRITE], 1);//write to pipe
}
close(fd[READ]);
exit(execvp(arguments[iterator][0], arguments[iterator]));
return 0;
}
iterator++;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}

Resources