I have been trying to implement an exit command on my C shell. I have tried the fork-exec method since it's a system call.
When I run the program, it prompts for the stdin input and when I type in "exit" it returns a "segmentation fault (core dumped)" error.
What am I doing wrong?
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#define ARGVMAX 100
#define LINESIZE 1024
#define EXITCMD "exit"
//makeargv - builds an argv vector from words in a string
int makeargv(char *s, char *argv[ARGVMAX]) {
int ntokens = 0;
if (s == NULL || argv == NULL || ARGVMAX == 0)
return -1;
argv[ntokens] = strtok(s, " \t\n");
while ((argv[ntokens] != NULL) && (ntokens < ARGVMAX)) {
ntokens++;
argv[ntokens] = strtok(NULL, " \t\n");
}
argv[ntokens] = NULL; // it must terminate with NULL
return ntokens;
}
void prompt() {
printf("sish> ");
fflush(stdout); //writes the prompt
}
/****** MAIN ******/
int main() {
char line[LINESIZE];
int wstatus;
while (1) {
prompt();
if (fgets(line, LINESIZE, stdin) == NULL)
break;
// TODO:
if(fgets(line, LINESIZE, strcmp(stdin, EXITCMD )) == 0)
return 0;
signal(SIGINT, SIG_DFL);
if (fork() == 0) exit(execvp(line[0], line));
{
signal(SIGINT, SIG_IGN);
}
wait(&wstatus);
if(WIFEXITED(wstatus))
printf("<%d>", WEXITSTATUS(wstatus));
}
return 0;
}
After reviewing and cleaning the code, I was finally able to implement the exit command.
Here goes the code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#define ARGVMAX 100
#define LINESIZE 1024
#define EXITCMD "exit"
//makeargv - build an argv vector from words in a string
int makeargv(char *s, char *argv[ARGVMAX]) {
int ntokens = 0;
if (s == NULL || argv == NULL || ARGVMAX == 0)
return -1;
argv[ntokens] = strtok(s, " \t\n");
while ((argv[ntokens] != NULL) && (ntokens < ARGVMAX)) {
ntokens++;
argv[ntokens] = strtok(NULL, " \t\n");
}
argv[ntokens] = NULL; // it must terminate with NULL
return ntokens;
}
void prompt() {
printf("C Shell >> ");
fflush(stdout); //writes the prompt
}
/****** MAIN ******/
int main() {
char line[LINESIZE];
while (1) {
prompt();
if (fgets(line, LINESIZE, stdin) == NULL)
break;
// TODO:
char *p = strchr(line, '\n');
if (p)
*p = 0;
if(strcmp(line, "exit") == 0)
break;
char *args[] = {line, (char*) 0};
int pid = fork();
if (pid == 0){
execvp(line, args);
perror("Command Error!");
exit(1);
} else {
wait(NULL);
}
return 0;
}
return 0;
}
Related
I am building a simple shell I want to handle the argument of exit. How can I handle it here?
I have already handle exit but I want to handle exit with argument.
Usage: exit status, where status is an integer used to exit the shell
For example
exit 98
exit 1
Present output
sh: No file or directory found
Expected Output
Exit the shell with the specific status code
shell.h
#ifndef SHELL_H
#define SHELL_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void print_prompt(void);
void tok_str(char *, char **);
int add_path(char **cmd);
void print_env(char **);
#endif
shell.c
#include "shell.h"
int main(int argc, char **argv, char **env)
{
char *buf = NULL;
size_t buflen = 0;
char *cmd[20];
do
{
fprintf(stderr, ":) ");
if (getline(&buf, &buflen, stdin) == -1)
{
if (feof(stdin))
{
free(buf);
buf = NULL;
exit(EXIT_SUCCESS);
}
perror("Error occurred");
free(buf);
buf = NULL;
exit(EXIT_FAILURE);
}
if (buf[0] == '\0' || strcmp(buf, "\n") == 0)
{
free(buf);
buf = NULL;
continue;
}
if (strcmp(buf, "exit\n") == 0)
{
free(buf);
buf = NULL;
exit(EXIT_SUCCESS);
}
if (strcmp(buf, "env\n") == 0)
{
print_env(env);
free(buf);
buf = NULL;
continue;
}
tok_str(buf, cmd);
add_path(cmd);
if (fork() == 0)
{
execve(cmd[0], cmd, env);
printf("%s: No such file or directory\n", argv[0]);
exit(EXIT_SUCCESS);
}else
{
wait(NULL);
free(buf);
buf = NULL;
}
} while (1);
exit(EXIT_SUCCESS);
}
utils.c
#include "shell.h"
void tok_str(char *buf, char **cmd)
{
char *ptr;
int i = 0;
ptr = strtok(buf, " \n");
while (ptr)
{
cmd[i] = ptr;
ptr = strtok(NULL, " \n");
i++;
}
cmd[i] = NULL;
}
int add_path(char **cmd)
{
const char *PATH[] = {"/usr/local/bin/ls", "/bin/", "/sbin/", "/usr/bin/", NULL};
unsigned int i = 0;
struct stat st;
while (PATH[i])
{
char s[120] = "";
strcat(s, PATH[i]);
strcat(s, cmd[0]);
if (stat(s, &st) == 0)
{
cmd[0] = s;
return (0);
}
i++;
}
return (-1);
}
void print_env(char **env)
{
int i = 0;
while (env[i])
printf("%s\n", env[i++]);
}
So I am trying to read from standard input and then get the input ready so that later on it can be used inside execvp().
What I am implementing here is basically a pipe for some terminal commands.
Here is how an example of my code goes.
input:
ls -s1
sort -n
output:
commands[0]="ls"
commands[1]="-s1"
commands2[0]="��""
commands2[1]="��""
sort: cannot read: t: No such file or directory
Here is my code
# include <stdlib.h>
# include <stdio.h>
# include <unistd.h>
# include <string.h>
# include <sys/wait.h>
# define BUF_SIZE 256
int main()
{
char buffer[BUF_SIZE];
char *commands[5];
char *commands2[5];
int argc = 0;
int argc2 = 0;
fgets(buffer, BUF_SIZE, stdin);
for ( commands[argc] = strtok(buffer, " \t\n");
commands[argc] != NULL;
commands[++argc] = strtok(NULL, " \t\n") ) {
printf("commands[%d]=\"%s\"\n", argc, commands[argc]);
}
commands[argc] = NULL;
fgets(buffer, BUF_SIZE, stdin);
for ( commands2[argc2] = strtok(buffer, " \t\n");
commands2[argc2] != NULL;
commands2[++argc2] = strtok(NULL, " \t\n") ) {
printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc]);
}
commands2[argc2] = NULL;
int my_pipe[2];
if (pipe(my_pipe) == -1)
{
perror("cannot create pipe\n");
}
pid_t my_pid;
my_pid = fork();
if (my_pid < 0)
{
perror("Failed fork\n");
}
if (my_pid > 0)
{
close(my_pipe[1]);
dup2(my_pipe[0], 0);
close(my_pipe[0]);
wait(NULL);
execvp(commands2[0],commands2);
}
else
{
close(my_pipe[0]);
dup2(my_pipe[1], 1);
close(my_pipe[1]);
execvp(commands[0],commands);
}
}
One major problem is that you read the second line over the first in buffer, and the commands[] array contains pointers into buffer too. That's not a recipe for happiness. The simplest fix is to define char buffer2[BUF_SIZE]; and use that in the second fgets() call and for loop.
Using argc in printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc]); is a copy'n'paste bug — it should reference argc2 twice. This helped hide the previous problem.
Note that perror() does not exit; your code blunders on if pipe() fails, or if fork() fails.
The wait() in if (my_pid > 0) is bad; remove it.
If execvp() fails, you should report an error and exit with a non-zero status.
Putting those changes together yields code such as:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define BUF_SIZE 256
int main(void)
{
char buffer[BUF_SIZE];
char buffer2[BUF_SIZE];
char *commands[5];
char *commands2[5];
int argc = 0;
int argc2 = 0;
fgets(buffer, BUF_SIZE, stdin);
for (commands[argc] = strtok(buffer, " \t\n");
commands[argc] != NULL;
commands[++argc] = strtok(NULL, " \t\n"))
{
printf("commands[%d]=\"%s\"\n", argc, commands[argc]);
}
commands[argc] = NULL;
fgets(buffer2, BUF_SIZE, stdin);
for (commands2[argc2] = strtok(buffer2, " \t\n");
commands2[argc2] != NULL;
commands2[++argc2] = strtok(NULL, " \t\n"))
{
printf("commands2[%d]=\"%s\"\n", argc2, commands2[argc2]);
}
commands2[argc2] = NULL;
int my_pipe[2];
if (pipe(my_pipe) == -1)
{
perror("cannot create pipe\n");
exit(EXIT_FAILURE);
}
pid_t my_pid = fork();
if (my_pid < 0)
{
perror("Failed fork\n");
exit(EXIT_FAILURE);
}
if (my_pid > 0)
{
close(my_pipe[1]);
dup2(my_pipe[0], 0);
close(my_pipe[0]);
execvp(commands2[0], commands2);
perror(commands2[0]);
exit(EXIT_FAILURE);
}
else
{
close(my_pipe[0]);
dup2(my_pipe[1], 1);
close(my_pipe[1]);
execvp(commands[0], commands);
perror(commands[0]);
exit(EXIT_FAILURE);
}
return EXIT_FAILURE;
}
When I run the program, it produces the appropriate output. Note that the return at the end of main() is actually never reached.
I've got a basic shell program that can change directories using cd and list the files with ls. I want to extend this further by adding optional flags to the ls command. In particular, I want to implement the ls -l (lowercase 'ell') command whereby it shows a total sum of all file sizes on the line before the long listing. I'm not sure how I can implement this. My code I have so far is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"
int main(int argc, char *argv[]) {
char *cmd;
char line[MAX_LENGTH];
while(1){
printf("> ");
if (!fgets(line, MAX_LENGTH, stdin)) break;
if ((cmd = strtok(line, DELIMS))) {
char *arg = strtok(0, DELIMS);
if (strcmp(cmd, "cd") == 0) {
if (!arg) fprintf(stderr, "cd missing argument.\n");
else {
chdir(arg);
}
}
else if (strcmp(cmd, "ls") == 0) {
struct dirent **namelist;
int n;
n = scandir(".",&namelist,NULL,alphasort);
if(n < 0)
{
perror("scandir");
exit(EXIT_FAILURE);
}
else
{
while (n--)
{
printf("%s\n",namelist[n]->d_name);
free(namelist[n]);
}
free(namelist);
}
}
else if (strcmp(cmd, "exit") == 0) {
break;
}
}
}
return 0;
}
Hello and thank you for attention. I am writing my own shell and I have small problem with redirect output to file. For example user wrote: ls -l >> output. If I find ">>", I should redirect first part of command, I mean call effect ls -l to file "output". I try to do it in case 1 but to file is redirected just one lane and the program is stopped, there is not appear "Shell -> " and nothing is going on. Can you give some advice to solve that problem? Thanks.
#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>
int *parse(char *linia, char **argv)
{
int counter = -1;
while (*linia != '\0')
{
while (*linia == ' ' || *linia == '\t' || *linia == '\n')
*linia++ = '\0';
*argv++ = linia;
counter++;
while (*linia != '\0' && *linia != ' ' && *linia != '\t' && *linia != '\n')
linia++;
}
*argv = '\0';
return counter;
}
void execute(char **argv)
{
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
{
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 **history = (char**)malloc(20*sizeof(char*));
int counter1=-1;
int counter2=0;
for(counter2 = 0; counter2<20; counter2++)
{
history[counter2]=(char*)malloc(100*sizeof(char));
}
int start = 0;
FILE *file;
file=fopen("his", "w");
if(!file)
printf("ERROR");
int i=0;
while (1)
{
printf("Shell -> ");
gets(command);
counter1++;
strcpy(history[counter1],command);
fopen("his", "w");
if(counter1<20)
for(i=0; i<=counter1; i++)
{
fprintf(file,"%s\n",history[i]);
}
else
for(i=counter1-20; i<counter1; i++)
{
fprintf(file,"%s\n",history[i]);
}
fflush(file);
printf("\n");
switch(specialChar(command))
{
case 1:
i = parse(command, argv);
int file1 = open(argv[i], O_APPEND | O_WRONLY);
dup2(file1,1) ;
if (strcmp(argv[0], "exit") == 0)
exit(0);
execute(argv);
close(file1);
break;
case 2:
break;
case 3:
break;
default:
parse(command, argv);
if (strcmp(argv[0], "exit") == 0)
exit(0);
execute(argv);
break;
}
fclose(file);
}
}
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
int show_dir_content(char *path);
int show_dir_content(char *path)
{
char readbuffer[80];
char buffer[150];
char uzun[9999];
int totalfound = 0;
DIR *d = opendir(path);
if(d == NULL)
return -3;
struct dirent *dir;
int piper[2];
pid_t typer = 0;
while((dir = readdir(d)) != NULL)
{
pipe(piper);
typer = fork();
if(typer == 0)
{
close(piper[0]);
if(dir->d_type != DT_DIR)
{ // if the type is not directory blue
// printf("%s\n",dir->d_name);
sprintf(buffer, "%s", dir->d_name);
write(piper[1], buffer, (strlen(buffer) + 1));
}
else if(dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 &&
strcmp(dir->d_name, "..") != 0) // if it is a directory
{
char d_path[255]; // here I am using sprintf which is safer than
// strcat
sprintf(d_path, "%s/%s", path, dir->d_name);
show_dir_content(d_path); // recall with the new path
}
exit(0);
close(piper[1]);
}
else if(typer > 0)
{
close(piper[1]);
read(piper[0], readbuffer, sizeof(readbuffer));
// strcat(uzun,readbuffer);
// close(piper[0]);
break;
}
}
while(wait(NULL) > 0)
;
closedir(d);
// printf("%s x_x\n",uzun);
return totalfound + 1; // finally close the directory
}
int main(int argc, char **argv)
{
show_dir_content(argv[1]);
return (0);
}
It doesn't execute on forked child, only on parent and only once.
I use Linux Mint 18 and gcc comes with it.
i don't what causes it, it doesn't seems to be Segmentation fault because it exits correctly. I would be more then happy if anyone can see my errors here
I'm debugging on CLion.