I am creating a shell program that has the ability to change directories and exit the shell. Everything is working as it should. I have a question about showing the current directory/path that I am in. When I compile & run my code. I am in my shell loop. My cursor represents ash > I want that cursor to represent the current path that the user is in.
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int ash_exit(char **args);
int ash_cd(char **args);
char const *builtin_str[] = {
"exit", "cd"};
int (*builtin_func[])(char **) = {
&ash_exit, &ash_cd};
int ash_num_builtins()
{
return sizeof(builtin_str) / sizeof(char *);
}
/**
Bultin command: change directory.
args List of args. args[0] is "cd". args[1] is the directory.
Always returns 1, to continue executing.
*/
int ash_cd(char **args)
{
if (args[1] == NULL)
{
fprintf(stderr, "ash: expected argument to \"cd\"\n");
}
else
{
if (chdir(args[1]) != 0)
{
perror("ash");
}
}
return 1;
}
int ash_exit(char **args)
{
return 0;
}
int ash_launch(char **args)
{
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
// Child process
if (execvp(args[0], args) == -1)
{
perror("ash");
}
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
// Error forking
perror("ash");
}
else
{
// Parent process
do
{
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
int ash_execute(char **args)
{
int i;
if (args[0] == NULL)
{
// An empty command was entered.
return 1;
}
for (i = 0; i < ash_num_builtins(); i++)
{
if (strcmp(args[0], builtin_str[i]) == 0)
{
return (*builtin_func[i])(args);
}
}
return ash_launch(args);
}
char *ash_read_line(void)
{
char *line = NULL;
ssize_t bufsize = 0; // have getline allocate a buffer for us
if (getline(&line, (unsigned long *)&bufsize, stdin) == -1)
{
if (feof(stdin))
{
exit(EXIT_SUCCESS); // We recieved an EOF
}
else
{
perror("readline");
exit(EXIT_FAILURE);
}
}
return line;
}
#define ASH_TOK_BUFSIZE 64
#define ASH_TOK_DELIM " \t\r\n\a"
/**
Split a line into tokens
line The line.
return Null-terminated array of tokens.
*/
char **ash_split_line(char *line)
{
int bufsize = ASH_TOK_BUFSIZE, position = 0;
char **tokens = (char **)malloc(bufsize * sizeof(char *));
char *token, **tokens_backup;
if (!tokens)
{
fprintf(stderr, "ash: allocation error\n");
exit(EXIT_FAILURE);
}
token = strtok(line, ASH_TOK_DELIM);
while (token != NULL)
{
tokens[position] = token;
position++;
if (position >= bufsize)
{
bufsize += ASH_TOK_BUFSIZE;
tokens_backup = tokens;
tokens = (char **)realloc(tokens, bufsize * sizeof(char *));
if (!tokens)
{
free(tokens_backup);
fprintf(stderr, "ash: allocation error\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, ASH_TOK_DELIM);
}
tokens[position] = NULL;
return tokens;
}
void ash_loop(void)
{
char *line;
char **args;
int status;
do
{
printf("ash > ");
line = ash_read_line();
args = ash_split_line(line);
status = ash_execute(args);
free(line);
free(args);
} while (status);
}
int main(int argc, char *argv[])
{
ash_loop();
return EXIT_SUCCESS;
}
getcwd(3) gives you the current path:
char buf[PATH_MAX] = {0};
getcwd(buf, sizeof buf);
printf("ash %s > ", buf);
Need to include <linux/limits.h> for PATH_MAX.
Related
#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;
}
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.
I have written a function for the purposes of redirecting output from some command to a file. Something like this
ls > ls.txt
Here is the function that does output redirection:
int redirect_output(char* cmdline, char **output_filename) {
int i;
char* args[MAX_ARGS];
// int nargs = get_args(cmdline, args);
for(i = 0; args[i] != NULL; i++) {
// Look for the >
if(!strcmp(args[i], ">")) {
// Get the filename
if(args[i+1] != NULL) {
*output_filename = args[i+1];
} else {
return -1; //syntax error
}
return 1; //there is an >
}
}
return 0; //no redirect
}
The function successfully redirects output from some command to a file, but for some reason it causes other commands to stop working. So for example, my program with redirect_output works for something like ls but it does not work for something like cat ls.txt, if I do not call the function it works for any command. And, by not work it mean it gets stuck. Here are the other important functions.
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 execute(int output, char *outputefile, char* cmdline)
{
int pid, 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;
pid = fork();
if(pid == 0) { /* child process */
//if(output)
// freopen(outputefile, "w+", stdout);
execvp(args[0], args);
/* return only when exec fails */
perror("exec failed");
exit(-1);
} else if(pid > 0) { /* parent process */
if(!async) waitpid(pid, NULL, 0);
else printf("this is an async call\n");
} else { /* error occurred */
perror("fork failed");
exit(1);
}
}
And here is my main:
int main (int argc, char* argv [])
{
char cmdline[BUFSIZ];
char *output_filename;
char **args;
int output;
for(;;) {
printf("COP4338$ ");
if(fgets(cmdline, BUFSIZ, stdin) == NULL) {
perror("fgets failed");
exit(1);
}
output = redirect_output(cmdline, &output_filename);
switch(0) {
case -1:
printf("Syntax error!\n");
break;
case 0:
break;
case 1:
printf("Redirecting output to: %s\n", output_filename);
break;
}
execute (output, output_filename, cmdline);
}
return 0;
}
Any help will be much appreciated! Thanks!
I am currently writing a shell for school purposes and I have the problem that if I write the command "history", the "exit" command afterwards doesn't work. If you could help me I would be very happy.
Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define MAX_INPUT 256
static char cwd[100];
void myprintf(char *text) {
text != NULL ?
printf("%s#rash:%s: %s\n", getenv("USER"), getcwd(cwd, sizeof(cwd)), text) :
printf("%s#rash:%s: ", getenv("USER"), getcwd(cwd, sizeof(cwd)));
}
void errprintf(char *text) {
myprintf(NULL);
printf("Couldn't run %s\n", text);
}
static char *argv[256];
static int argc;
static int pid = 0;
FILE *fhistory;
void parseInput(char *input) {
input = strtok(input, "\n");
for (argc = 0; argc < MAX_INPUT; argc++) {
argv[argc] = NULL;
}
argc = 0;
fprintf(fhistory, "%s\n", input);
char *param = strtok(input, " ");
while (param) {
argv[argc++] = param;
param = strtok(NULL, " ");
}
}
void signalHandler(int sign) {
switch (sign) {
case SIGINT: {
if (pid != 0) {
kill(pid, SIGKILL);
pid = 0;
}
break;
}
case SIGCHLD: {
if (pid != 0) {
kill(pid, SIGKILL);
pid = 0;
}
break;
}
}
}
int programs() {
if (!strcmp(argv[0], "exit")) {
return -1;
}
if (!strcmp(argv[0], "cd")) {
chdir(argv[1] == NULL ? getenv("HOME") : argv[1]);
} else {
pid = fork();
switch (pid) {
case -1: {
myprintf("Erectile Dysfunction!");
break;
}
case 0: {
if (!strcmp(argv[0], "history")) {
system("cat .rash_history.txt");
} else {
execvp(argv[0], argv);
errprintf(argv[0]);
}
break;
}
default: {
waitpid(pid, NULL, 0);
signal(SIGCHLD, signalHandler);
return 0;
}
}
}
return 1;
}
int main(void) {
signal(SIGINT, signalHandler);
char *input = NULL;
myprintf("Welcome to rash!");
fhistory = fopen(".rash_history.txt", "a");
while (input != NULL ? strncmp(input, "exit", strlen(input)) != 0 : 1) {
myprintf(NULL);
input = (char *) malloc(sizeof(char *) * MAX_INPUT);
int j;
for (j = 0; j < MAX_INPUT; j++) {
input[j] = '\0';
}
fgets(input, MAX_INPUT, stdin);
parseInput(input);
if (*(input) != '\n') {
programs();
}
}
fclose(fhistory);
return 0;
}
I think here while (input != NULL ? strncmp(input, "exit", strlen(input)) != 0 : 1) {
it would be strlen("exit")
The "input" variable take value inside your while loop only:
fgets(input, MAX_INPUT, stdin);
parseInput(input);
The parseInput uses strtok function couple times. The strtok modifies the argument string, so it not clear what value it will contains at time you will compare it with "exit".
Try to duplicate "input" string before calling strtok. Try to add debug string at the end of loop to make clear what value is into input string.
I'm writing a small shell as an exercis to learn C. Now I can execute custom commands like ls and date but when I try to run firefox it doesn't start. Why? My session is
$ ./a.out
miniShell>> ls
ls
a.out digenv2.c~ digenv.c.orig miniShell.c README.md test
digenv digenv.c digenv.old.c miniShell.c~ smallshell.c
digenv2.c digenv.c~ LICENSE miniShell.c.orig smallshell.c.orig
miniShell>> date
date
Tue May 12 09:38:27 CEST 2015
miniShell>> firefox
firefox
miniShell>>
My program is
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_LEN 1024
#define BUFFERSIZE 1024
pid_t foreground = -1;
int mystrcmp(char const *, char const *);
struct command
{
char * const *argv;
};
void err_syserr(char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
int StartsWith(const char *a, const char *b)
{
if(strncmp(a, b, strlen(b)) == 0) return 1;
return 0;
}
/* Helper function that spawns processes */
/*static int spawn_proc(int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork()) == 0)
{
if (in != 0)
{
if (dup2(in, 0) < 0)
err_syserr("dup2() failed on stdin for %s: ", cmd->argv[0]);
;
close(in);
}
if (out != 1)
{
if (dup2(out, 1) < 0)
err_syserr("dup2() failed on stdout for %s: ", cmd->argv[0]);
close(out);
}
fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd->argv[0]);
execvp(cmd->argv[0], cmd->argv);
err_syserr("failed to execute %s: ", cmd->argv[0]);
}
else if (pid < 0) {
err_syserr("fork failed: ");
}
return pid;
}*/
/* Helper function that forks pipes */
/*static void fork_pipes(int n, struct command *cmd)
{
int i;
int in = 0;
int fd[2];
for (i = 0; i < n - 1; ++i)
{
pipe(fd);
spawn_proc(in, fd[1], cmd + i);
close(fd[1]);
in = fd[0];
}
if (dup2(in, 0) < 0) {
err_syserr("dup2() failed on stdin for %s: ", cmd[i].argv[0]);
}
fprintf(stderr, "%d: executing %s\n", (int)getpid(), cmd[i].argv[0]);
execvp(cmd[i].argv[0], cmd[i].argv);
err_syserr("failed to execute %s: ", cmd[i].argv[0]);
}*/
/*Remove zoombie processes*/
/*Return if background process terminated*/
/*
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
*/
void Janitor(int status) {
if(status==SIGCHLD) { /*Child process terminated, stopped, or continued*/
int a = 1;
while(a) {
pid_t pid_my1 = waitpid(-1, &status, WNOHANG);
/*WNOHANG = return immediately if no child has exited*/
/*Wait*/
/*http://linux.die.net/man/2/waitpid*/
if(0<pid_my1) { /*Still things to clean up*/
if(pid_my1!=foreground) { /*Don't stop me since it's the foregound process*/
/*http://linux.die.net/man/3/wait*/
if(WIFEXITED(status)) { /*Child process terminated*/
printf("%d terminated", pid_my1);
}
}
}
else { /*All work done, for now*/
a = 0;
}
}
}
}
int main() {
char line[BUFFER_LEN];
char* argv[100];
char* path= "/bin/";
char progpath[20];
int argc;
size_t length;
char *token;
int i=0;
int pid;
char *tokenstr;
char *search = " ";
int isSignal = 0;
int isBackground = 0;
#ifdef SIGDET
#if SIGDET == 1
isSignal = 1; /*Termination detected by signals*/
#endif
#endif
while(1) {
i = 0;
int built_in_command = 0;
printf("miniShell>> ");
if(!fgets(line, BUFFER_LEN, stdin)) {
break;
}
length = strlen(line);
if (line[length - 1] == '\n') {
line[length - 1] = '\0';
}
if(strcmp(line, "exit")==0) {
break;
}
if(StartsWith(line, "cd")) {
built_in_command=1;
printf("change directory\n");
tokenstr = strtok(line, search);
tokenstr = strtok(NULL, search);
chdir(tokenstr);
/*TODO maybe have a check whether extra argument exist, if not go to home directory*/
}
token = strtok(line," ");
while(token!=NULL) {
argv[i]=token;
token = strtok(NULL," ");
i++;
}
if(StartsWith(line, "checkEnv")) {
built_in_command=1;
if (0==i) {
char *printenv[] = { "printenv", 0};
char *sort[] = { "sort", 0 };
char *less[] = { "less", 0 };
struct command cmd[] = { {printenv}, {sort}, {less} };
fork();
}
else {
char *tmp;
int len = 1;
for (i = 1; i < argc; i++)
{
len += strlen(argv[i]) + 2;
}
tmp = (char *) malloc(len);
tmp[0] = '\0';
int pos = 0;
for (i = 1; i < argc; i++)
{
pos += sprintf(tmp + pos, "%s%s", (i == 1 ? "" : "|"), argv[i]);
}
char *printenv[] = { "printenv", 0};
char *grep[] = { "grep", "-E", tmp, NULL};
char *sort[] = { "sort", 0 };
char *less[] = { "less", 0 };
struct command cmd[] = { {printenv}, {grep}, {sort}, {less} };
fork();
free(tmp);
}
}
if(0==built_in_command) { /*Not a built in command, so let execute it*/
argv[i]=NULL;
argc=i;
for(i=0; i<argc; i++) {
printf("%s\n", argv[i]);
}
strcpy(progpath, path);
strcat(progpath, argv[0]);
for(i=0; i<strlen(progpath); i++) {
if(progpath[i]=='\n') {
progpath[i]='\0';
}
}
isBackground = 0;
sigset_t my_sig;
pid_t pid_temp;
int lastElem = (sizeof(line)/sizeof(line[0]))-1; /*Last input argument index*/
/*TODO check if background process*/
/*TODO store the time forground process started*/
int fd[2];
if (isBackground == 1) { //If backgroundprocess
pipe(fd); /*(two new file descriptors)*/
/*FIXME pid_temp = fork_pipes(2, .....);*/
pid_temp = fork();
}
else if (isBackground == 0) { //If foreground process
int isSignal = 0; /*FIXME*/
if (1 == isSignal) { /*If using signaldetection*/
sigemptyset(&my_sig); /*empty and initialising a signal set*/
sigaddset(&my_sig, SIGCHLD); /*Adds signal to a signal set (my_sig)*/
/*http://pubs.opengroup.org/onlinepubs/7908799/xsh/sigprocmask.html*/
sigprocmask(SIG_BLOCK, &my_sig, NULL);
}
/*FIXME pid_temp = fork_pipes(2, .....);*/
pid_temp = fork();
foreground = pid_temp; /*Set pid for foreground process*/
}
if (0<pid_temp) {
/*Parent process*/
}
else if (0>pid_temp) {
/*Error*/
}
else {
/*Child process*/
if (1 == isBackground) { //Backgroundprocess
dup2(fd[STDIN_FILENO], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
}
execvp(progpath,argv);
/*TODO execute command here, some is working*/
}
if (0 == isBackground) { //Foregroundprocess
/*Write here, Emil*/
int status = 0;
waitpid(pid_temp, &status, 0);
/*Foregroundprocess terminated*/
/*TODO How long time was the total execution time*/
int isSignal = 0; /*FIXME*/
if (1 == isSignal) { /*If using signaldetection*/
int a = sigprocmask(SIG_UNBLOCK, &my_sig, NULL);
/*http://man7.org/linux/man-pages/man2/sigprocmask.2.html*/
if (0 == a) {
/*Sigprocmask was successfull*/
}
else {
/*Sigprocmask was not successfull, return=-1*/
}
Janitor(SIGCHLD);
}
}
else if (1==isBackground) {
close(fd[0]);
close(fd[1]);
}
}
/* pid= fork();
if(pid==0) {
execvp(progpath,argv);
fprintf(stderr, "Child process could not do execvp\n");
} else {
wait(NULL);
printf("Child exited\n");
}*/
}
return (0);
}
int mystrcmp(char const *p, char const *q)
{
int i = 0;
for(i = 0; q[i]; i++)
{
if(p[i] != q[i])
return -1;
}
return 0;
}
int cd(char *pth) {
char path[BUFFERSIZE];
char cwd[BUFFERSIZE];
char * return_value;
int other_return;
strcpy(path,pth);
if(pth[0] != '/')
{
return_value = getcwd(cwd,sizeof(cwd));
strcat(cwd,"/");
strcat(cwd,path);
other_return = chdir(cwd);
} else {
other_return = chdir(pth);
}
printf("Spawned foreground process: %d\n", getpid());
return 0;
}
The issue is with the path. You always assume the command is in /bin/ whereas firefox is usually in /usr/bin/. So your child is going to exec /bin/firefox and fail. Checking the return code of execvp would have helped finding this. On success, execvp family functions don't return. So if they do, you know why.