create a new process to execute ls command - c

I want to write a program which will create a new process and in that child process, it should execute the command: ls. In the meanwhile, the parent should wait for the child to die. However, my code does not work.
Please help me thank you very much!
int main()
{
char * read;
size_t size;
getline(&read , &size , stdin);
read[strlen(read)-1] = '\0';
printf("%s\n" , read);
int status;
pid_t f;
if((f = fork()) == 0)
{
execvp(read , &read);
exit(0);
}
else
{
wait(&status);
}
}

From man execvp:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
You need to use an array of char* and set the last element to NULL.
I am unsure what the getline() is reading but I guess it is the directory to be lsd. The first argument to execvp() should be ls and the second argument the array of char*.

Consider the following:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *input_arg[2];
char *input_str = NULL;
size_t input_len = 0;
char **args;
ssize_t len;
size_t n;
pid_t child, p;
int status;
if (argc < 2) {
/* No command line parameters. Read command from stdin. */
len = getline(&input_str, &input_len, stdin);
/* Find length excluding the newline at end. */
if (len > (ssize_t)0)
n = strcspn(input_str, "\r\n");
else
n = 0;
if (n > (size_t)0) {
/* Terminate input command before the newline. */
input_str[n] = '\0';
} else {
fprintf(stderr, "No input, no command.\n");
return 1;
}
input_arg[0] = input_str;
input_arg[1] = NULL;
args = input_arg;
} else {
/* Use command line parameters */
argv[argc] = NULL;
args = argv + 1;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return 1;
}
if (!child) {
/* This is the child process. */
errno = ENOENT;
execvp(args[0], args);
fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
exit(127);
}
do {
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
return 127;
}
if (p != child) {
fprintf(stderr, "waitpid() library bug occurred.\n");
return 127;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
fprintf(stderr, "Command successful.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
return 126;
}
fprintf(stderr, "Command died from unknown causes.\n");
return 125;
}
The above uses the command line parameters if specified, otherwise it reads one from the standard input. Because the standard input is not tokenized, you can only supply the command name, no parameters. If you enlarge the input_arg[] array into
char *input_arg[4];
and modify the assignment into
input_arg[0] = "/bin/sh";
input_arg[1] = "-c";
input_arg[2] = input_str;
input_arg[3] = NULL;
args = input_arg;
then the input string will be processed using the /bin/sh shell, just like popen() does.
You can also use len = getdelim(&input_str, &input_len, '\0', stdin); and remove the input_str[n] = '\0'; assignment to allow multiline input; the shell should handle those fine, as long as it is short enough to fit in the command line argument buffer (maximum length depends on your OS).
The rules how shells split input into separate commands and parameters are rather complex, and you should not try to emulate them. Instead, find a simple way for the user to specify the parameters separately (like the command-line parameter case), or use the shell to do it for you. If you don't do any splitting, you will probably need to remove the newline at the end of the input line.
The point to note is that for execvp(file, args), args[0] is the name the application sees (as $0 or argv[0]), and args[1] is the first parameter. Each parameter is terminated by NUL (\0) just like strings are normally in C, and the args pointer array must end with a NULL pointer. If there are no parameters, then args[1] == NULL.

why dont you just use system command...
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
return 0;
}
Update:
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0) // this is child process
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
}
else // this is paraent process
{
int status=0
wait(&status);
printf ("Child process is returned with: %d.\n",status);
}
}

Related

execvp not giving output

I have to program a little shell for school but I am stuck at even executing a command.
execvp worked when I executed it in the wait for input function, but in the execute command function it doesnt e.g I don't get any output to stdout for commands like ls.
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
int executecommand(char commandarray[]){
char * command = &commandarray[0];
pid_t pid = fork();
if (pid < 0){
perror("failed");
return errno;
} else if (0 == pid) {
execvp(command, &commandarray);
return 0;
} else {
int waitstatus;
waitpid(pid, &waitstatus, 0);
return 0;
}
}
int waitforinput(){
printf("$ ");
char cmd[256];
fgets(cmd, 256, stdin);
executecommand(cmd);
return 0;
}
int main(int argc, char **argv) {
waitforinput();
return 0;
}
The argument to execvp() must be an array of strings ending with NULL, not a pointer to a single string. Normally you would parse the input line into an array containing the command followed by arguments, but if you don't care about arguments you can just create an array with 2 elements, the command followed by NULL.
You also need to remove the newline from the end of the input line returned by fgets(). See Removing trailing newline character from fgets() input for many ways to do this.
If you want to return errno, you need to save it before calling perror(), because that may change errno.
int executecommand(char *commandarray[]){
char * command = commandarray[0];
pid_t pid = fork();
if (pid < 0){
int saved_errno = errno;
perror("fork");
return saved_errno;
} else if (0 == pid) {
execvp(command, commandarray);
int saved_errno = errno;
perror("execvp");
return saved_errno;
} else {
int waitstatus;
waitpid(pid, &waitstatus, 0);
return 0;
}
}
int waitforinput(){
printf("$ ");
char cmd[256];
char *cmd_array[2];
fgets(cmd, 256, stdin);
cmd[strcspn(cmd, "\n")] = 0; // remove trailing newline
cmd_array[0] = cmd;
cmd_array[1] = NULL;
return executecommand(cmd_array);
}
Invalid arguments to execvp :
From the man page:
int execvp(const char *file, char *const argv[])
The execv(), execvp(), and execvpe() functions provide an array of
pointers to null-terminated strings that represent the argument list
available to the new program. The first argument, by convention,
should point to the filename associated with the file being executed.
> The array of pointers must be terminated by a NULL pointer.
The execvp function expects an array of strings terminated by a NULL pointer, not a pointer to one string.
Ignoring the return value of exec* and others:
The exec() functions only return if an error has occurred. The return
value is -1, and errno is set to indicate the error.
Add a check for that.
Likewise, waitpid() may also fail, check its return value.
You also declared waitforinput() and executecommand() to return an int, but ignore the values returned. Either make use of them, or change the return type to void.
Trailing newline:
fgets(cmd, 256, stdin)
fgets will retain the newline in the buffer. Here's a one-liner that I use to remove it¹:
cmd [strcspn (cmd, "\n\r")] = `\0`;
Aside: Change
int main (int argc, char **argv)
to
int main (void)
as you never make use of the arguments.
[1] — Here are some others: https://codereview.stackexchange.com/q/67608/265278

How to execute multiple commands from argv with an specific delimiter

Recently I had an assignment where I had to make a program that takes from command line two different commands, separated with a '+', for example:
ps -lu myUsername + ls -la
The objective of the program was to run any two orders simultaneously, with any number of parameters per order, by using fork() and exec(). This was my solution to this problem:
Note: the original problem was meant to be run on a machine with Solaris 5.10 and c89 or c99 standard (gcc 3.4.3)
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
int main (int argc, char* argv[]) {
char delimiter = '+';
char* auxp; //auxiliar pointer
int i = 1;
int position;
while(i < argc){
if (strcmp("+", argv[i]) == 0) {
argv[i] = NULL;
position = i;
}
i++;
}
if (fork() == 0) {
execvp(argv[1], &argv[1]);
exit(1);
}
if (fork() == 0) {
execvp(argv[position+1], &argv[position+1]);
exit(1);
}
wait(NULL);
wait(NULL);
exit(0);
}
This was enough for the assignment but I wanted to make it work with N arguments instead of only 2. I can't reach a systematic way to find the all the addresses. Any help is appreciated, thanks in advice.
For the general case of N commands, need to keep track of where each command starts and ends, and fork a new child whenever the end of a command is found. That could be at a + separator argument, or at the end of the original argument list.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char* argv[]) {
/*
* Note: argc could be in range 0 to INT_MAX.
* Using unsigned int values to avoid arithmetic overflow.
*/
unsigned int start = 1;
unsigned int end = 1;
unsigned int child_count = 0;
/* Note: argv[argc] is a terminating null pointer. */
while(end <= (unsigned int)argc){
if(end == (unsigned int)argc || strcmp("+", argv[end]) == 0){
/* Reached the terminating null pointer or a command separator. */
argv[end] = NULL;
if(start != end){
/*
* Command is not empty.
* Fork a child process to execute the command.
*/
pid_t child = fork();
if(child > 0){
/* Parent forked child successfully. */
child_count++;
}else if(child == 0){
/* This is the child process. Execute command. */
execvp(argv[start], &argv[start]);
exit(1);
}
}
/* Next command starts after this one. */
start = end + 1;
}
/* Looking for terminating null pointer or command separator. */
end++;
}
/* Wait for the child processes to terminate. */
while(child_count){
wait(NULL);
child_count--;
}
exit(0);
}
Note: the argv[end] = NULL; line could be moved into the if(child == 0){ } block (but before the call to execvp) to leave the parent's original argument list intact.
Just move the forking into the loop:
int main (int argc, char* argv[])
{
char** start = ++argv;
unsigned int n = 0;
for(; *argv; ++argv) // profiting from argv being null terminated, too...
{
if (strcmp("+", *argv) == 0)
{
*argv = NULL;
if (fork() == 0)
{
execvp(*start, start);
exit(1);
}
start = argv + 1;
++n; // but need to count how many times we actually forked!
}
}
while(n--)
{
wait(NULL);
}
exit(0);
}
OK, I modified iterating a bit, too – pointers are just so much nicer (personal oppinion...).
Note: This is untested code, if you find a bug please fix yourself...

Shell program in C has odd fork behaviour

I am writing a C program to emulate a simple shell. This shell will basically evaluate commands like any other shell (ls, cat, etc.), as well as handle pipelining and redirection.
Currently, I am trying to start out by getting user input, tokenizing it, and executing the command provided (e.g. executing only "ls" and not "ls -l"). However, I am having a lot of difficulty with the forking. It seems that every time I fork, something goes wrong and hundreds of identical processes are created, leading to my computer freezing and me having to restart. The code appears to be correct, but I have no idea what is causing this behaviour. Below is the relevant portion of my code (main method and input tokenizer method).
int main() {
char inputLine[512]; //user input
char *args[10]; //arguments
char* pathVar = "/bin/";//path for argument
char programPath[512]; //pathVar + args[0]
int n; //count variable
//loop
while (1) {
//print prompt, get input
printf("input> ");
fgets(inputLine, 512, stdin);
n = tokenizer(inputLine, args);
//fork process
pid_t pid = fork();
if (pid != 0) { //if parent
wait(NULL);
} else { //if child
//format input for execution
strcpy(programPath, pathVar);
strcat(programPath, args[0]);
//execute user command
int returnVal = execv(programPath, args);
}
}
return 0;
}
int tokenizer(char *input, char *args[]) {
char *line; //current line
int i = 0; //count variable
line = input;
args[i] = strtok(line, " ");
do {
i++;
line = NULL;
args[i] = strtok(line, " ");
} while (args[i] != NULL);
return i;
}
Putting it all together:
You need to check fork and execv for failure.
You should exit after an execv failure (and perhaps after a fork failure).
And you need to add \n to the strtok delimiters (or remove the newline from the input line in some other way).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXARGS 10
#define PATH "/bin/"
int main() {
char inputLine[BUFSIZ];
char *args[MAXARGS];
char programPath[BUFSIZ + sizeof(PATH) + 10];
while (1) {
printf(":-> ");
if (fgets(inputLine, BUFSIZ, stdin) == NULL) /* ctrl-D entered */
break;
tokenize(inputLine, args);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid != 0) { /* parent */
wait(NULL);
} else { /* child */
strcpy(programPath, PATH);
strcat(programPath, args[0]);
execv(programPath, args); /* will not return unless it fails */
perror("execv");
exit(EXIT_FAILURE);
}
}
return 0;
}
int tokenize(char *input, char *args[]) {
int i = 0;
args[0] = strtok(input, " \n");
for (i = 0; args[i] && i < MAXARGS-1; ++i)
args[++i] = strtok(NULL, " \n");
return i;
}
You should check that execv doesn't fail and also be sure to exit() at the end of the child block.
//execute user command
int returnVal = execv(programPath, args);
// check return from execv
if (returnVal < 0) {
perror("execv");
exit(1);
}
Also, beware using functions like strcpy in this context since they may lead to buffer overflows. If an untrusted attacker type is talking to your shell this type of security issue could let them break out of the "sandbox".

Use strtok() and execute UNIX command in background [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm trying to write a C program which creates a UNIX shell. In this shell when a UNIX command is typed, the shell should execute it in the foreground or background (background when & is specified). I'm getting the command to run in the foreground but I can't run it in the background.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"
int main(int argc, char *argv[])
{
char *cmd, *bg;
char line[MAX_LENGTH];
pid_t fpid,bpid;
int status;
while (1)
{
fpid=10;
bpid=10;
printf("myshell > ");
if (!fgets(line, MAX_LENGTH, stdin))
break;
int j=0;
if(cmd = strtok(line, DELIMS))
{
bg = strtok(line," ");
while(bg!=NULL)
{
printf("%s",bg);
bg = strtok(NULL, " ");
if(strcmp(bg, "&") == 0)
break;
}
printf("%s", bg);
if(strcmp(cmd,"exit")==0)
break;
else if(strcmp(bg,"&")==0)
{
bpid=fork();
//waitpid(bpid,&status,0);
system(line);
exit(0);
}
else
{
//fpid=fork();
//if(fpid==0)
//{
system(line);
// exit(0);
//}
//else
//{
// waitpid(fpid,&status,0);
//}
}
}
}
return(0);
}
This code is for my homework.
Read the manpage for fork(). The return code of 0 means that you are in the child, non-zero (non-negative) means you are the parent. You should have different logic based on that and use system() (or better exec*() in the child branch.
Here's the typical logic you should have:
tokenize(line)
if (last token is '&') {
rc = fork();
if (rc < 0)
handle error;
else if (rc > 0) { /* in parent, rc = child pid */
do whatever you planned to do in the parent process
}
else { /* in child */
use exec*() to start the child command
}
}
else { /* foreground execution */
use system() to run command
}
Here is code derived from the code in the question that emits a prompt, gets the line of input, splits it into tokens, detects that the last token is &, and detects that the first word is exit and exits the loop. It prints out what its found, carefully. And you now need to handle the fork, exec, wait etc code.
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"
int main(void)
{
char line[MAX_LENGTH];
char *ps1 = "toysh> ";
while (fputs(ps1, stdout) > 0 && fgets(line, sizeof(line), stdin) != NULL)
{
char *cmd[100];
char *bg = NULL;
int j = 0;
char *tokens = line;
while ((cmd[j++] = strtok(tokens, DELIMS)) != NULL)
tokens = NULL;
assert(j < 100);
/* The line has been tokenized into j-1 tokens */
/* Print the tokens found */
for (int i = 0; i < j; i++)
{
if (cmd[i] != 0)
printf("%d: <<%s>>\n", i, cmd[i]);
else
printf("%d: NULL pointer\n", i);
}
assert(j > 0);
if (j == 1)
continue; // No command
j--;
assert(j > 0);
if (strcmp(cmd[j-1], "&") == 0)
{
printf("== Found &\n");
bg = cmd[j-1];
cmd[--j] = 0;
if (j == 0)
{
puts("Syntax error: cannot have & on its own");
continue;
}
}
if (strcmp(cmd[0], "exit") == 0)
{
printf("== Found exit command\n");
if (bg != NULL)
{
puts("Can't run exit in background");
continue;
}
break;
}
/*
** Now you can do your fork, exec, waitpid work. Note that the
** command is already split into words with the null pointer at
** the end. This is what execv(), execve() and execvp() want
*/
}
putchar('\n');
return(0);
}
Note that the code does not prevent you from entering too many tokens on a single line. It eventually detects that you've done so, if it hasn't already crashed, via an assert. You'll need to make that bullet-proof at some point.
Request for further assistance
I'm very new to the fork and waitpid work. Can you help me here?
You've been given good advice in the other answer.
Add:
#include <sys/wait.h>
Add:
static void run_command(char **argv, int bg_flag);
Add:
/*
** Now you can do your fork, exec, waitpid work. Note that the
** command is already split into words with the null pointer at
** the end. This is what execv(), execve() and execvp() want
*/
run_command(cmd, (bg != NULL));
New function:
static void run_command(char **argv, int bg_flag)
{
pid_t pid;
fflush(0); // Flush pending output
if ((pid = fork()) < 0)
printf("Fork failed\n");
else if (pid > 0)
{
/* Parent shell */
if (bg_flag == 0)
{
int status;
int corpse;
while ((corpse = waitpid(-1, &status, WNOHANG)) >= 0)
{
if (corpse != 0)
printf("Process %d exited with status 0x%.4X\n",
corpse, status);
if (corpse == 0 || corpse == pid)
break;
}
}
else
printf("%d: %s running in background\n", pid, argv[0]);
}
else
{
/* Child process */
execvp(argv[0], argv);
fprintf(stderr, "%d: failed to execute %s (%d: %s)", (int)getpid(), argv[0], errno, strerror(errno));
exit(1);
}
}
You get to decide how verbose your shell should be, but while you're debugging it, more information is better than less.
Also, the error messages should all go to stderr; I've left a fair number going to stdout.

exit command does not work properly in my own shell

I wrote a shell for an assignment and it works correctly, but there is a small run time error which i can not figure out. When the user enter the command 'exit' through the shell it should come out of newly created shell. But the problem is I have to type the command 'exit' several times to quit the shell. If someone can help me it will be a great pleasure for me! Thanks all!
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
char* cmndtkn[256];
char buffer[256];
char* path=NULL;
char pwd[128];
int main(){
//setting path variable
char *env;
env=getenv("PATH");
putenv(env);
system("clear");
printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");
while(1){
fflush(stdin);
getcwd(pwd,128);
printf("[MOSH~%s]$",pwd);
fgets(buffer,sizeof(buffer),stdin);
buffer[sizeof(buffer)-1] = '\0';
//tokenize the input command line
char* tkn = strtok(buffer," \t\n");
int i=0;
int indictr=0;
// loop for every part of the command
while(tkn!=NULL)
{
if(strcoll(tkn,"exit")==0 ){
exit(0);
}
else if(strcoll(buffer,"cd")==0){
path = buffer;
chdir(path+=3);}
else if(strcoll(tkn,"|")==0){
indictr=i;}
cmndtkn[i++] = tkn;
tkn = strtok(NULL," \t\n");
}cmndtkn[i]='\0';
// execute when command has pipe. when | command is found indictr is greater than 0.
if(indictr>0){
char* leftcmnd[indictr+1];
char* rightcmnd[i-indictr];
int a,b;
for(b=0;b<indictr;b++)
leftcmnd[b]=cmndtkn[b];
leftcmnd[indictr]=NULL;
for(a=0;a<i-indictr-1;a++)
rightcmnd[a]=cmndtkn[a+indictr+1];
rightcmnd[i-indictr]=NULL;
if(!fork())
{
fflush(stdout);
int pfds[2];
pipe(pfds);
if(!fork()){
close(1);
dup(pfds[1]);
close(pfds[0]);
execvp(leftcmnd[0],leftcmnd);
}
else{
close(0);
dup(pfds[0]);
close(pfds[1]);
execvp(rightcmnd[0],rightcmnd);
}
}else wait(NULL);
//command not include pipe
}else{
if(!fork()){
fflush(stdout);
execvp(cmndtkn[0],cmndtkn);
}else wait(NULL);
}
}
}
Like the cd command, the exit command has to be interpreted by the shell as a built-in; it must exit the loop or call the exit() function directly. However, it also appears that should be happening. Note that using strcoll() is a little unusual; normally, strcmp() is sufficient.
You should report problems if execvp() returns — and you must make sure the sub-shell exits so that you don't have multiple shell processes reading the input simultaneously. I'm left wondering if this problem is occurring, and that's why you have to type exit multiple times.
You also need to check that fgets() did not report an error. It always null terminates its input; your code does not zap the newline (you'd need strlen(buffer)-1 instead of sizeof(buffer)-1).
The code that reads and sets PATH is wrong. getenv("PATH") returns a pointer to the first character after the PATH= part; you then use that to 'set' the environment. Fortunately for you, the average value for PATH does not contain anything that looks like VAR=value, so it is functionally a no-op (though the information is probably copied into the environment, where it makes a mess without causing any major harm).
Your code indentation scheme is rococo at best — mostly, it is just woefully inconsistent. Please be consistent! The spacing of the lines in the code was also extremely erratic. When you're adding code in SO, do not use tabs, do use 4 spaces per indent level, do highlight a block of code that is left justified and use the {} button above the edit box to indent it as code. This also means you don't need to add blank lines to the code.
You aren't closing enough file descriptors. When you use dup() (or dup2()) to duplicate a pipe to standard input or standard output, you have to close both of the file descriptors returned by pipe().
On Linux, using fflush(stdin) is undefined behaviour, AFAIK. It is defined on Windows, but not on POSIX systems.
You don't check that your chdir() system call works.
Trying your code, I did get one runaway prompt. Unfortunately, I couldn't remember or see what triggered the runaway. The code below is mostly sanitized and seems to behave. I've annotated some critical changes — and not others. One of the things you should be doing for your own benefit is including trace like the dump_cmd() function so you can see what your program is doing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
char *cmndtkn[256];
char buffer[256];
char *path = NULL;
char pwd[128];
static void dump_cmd(char **argv);
int main(void)
{
/*
//setting path variable
char *env;
env=getenv("PATH");
putenv(env);
system("clear");
*/
printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");
while (1)
{
//fflush(stdin);
getcwd(pwd, 128);
printf("[MOSH~%s]$", pwd);
if (fgets(buffer, sizeof(buffer), stdin) == 0)
{
putchar('\n');
break;
}
//buffer[sizeof(buffer)-1] = '\0';
buffer[strlen(buffer)-1] = '\0';
//tokenize the input command line
char *tkn = strtok(buffer, " \t\n");
int i = 0;
int indictr = 0;
// loop for every part of the command
while (tkn != NULL)
{
if (strcoll(tkn, "exit") == 0)
{
printf("Got: exit\n");
fflush(stdout);
exit(0);
}
else if (strcoll(tkn, "cd") == 0) // Was buffer, not tkn
{
printf("Got: cd (%s)\n", buffer + 3);
fflush(stdout);
path = buffer;
chdir(path += 3);
}
else if (strcoll(tkn, "|") == 0)
{
indictr = i;
}
cmndtkn[i++] = tkn;
tkn = strtok(NULL, " \t\n");
}
cmndtkn[i] = 0;
// execute when command has pipe. when | command is found indictr is greater than 0.
if (indictr > 0)
{
char *leftcmnd[indictr+1];
char *rightcmnd[i-indictr];
int a, b;
for (b = 0; b < indictr; b++)
leftcmnd[b] = cmndtkn[b];
leftcmnd[indictr] = NULL;
for (a = 0; a < i-indictr-1; a++)
rightcmnd[a] = cmndtkn[a+indictr+1];
rightcmnd[i-indictr-1] = NULL; // Did not include -1
if (!fork())
{
fflush(stdout);
int pfds[2];
pipe(pfds);
if (!fork())
{
dump_cmd(leftcmnd);
close(1);
dup(pfds[1]);
close(pfds[0]);
close(pfds[1]);
execvp(leftcmnd[0], leftcmnd);
fprintf(stderr, "failed to execvp() %s\n", leftcmnd[0]);
exit(1);
}
else
{
dump_cmd(rightcmnd);
close(0);
dup(pfds[0]);
close(pfds[0]);
close(pfds[1]);
execvp(rightcmnd[0], rightcmnd);
fprintf(stderr, "failed to execvp() %s\n", rightcmnd[0]);
exit(1);
}
}
else
wait(NULL);
}
else
{
//command does not include pipe
if (!fork())
{
dump_cmd(cmndtkn);
fflush(stdout);
execvp(cmndtkn[0], cmndtkn);
fprintf(stderr, "failed to execvp() %s\n", cmndtkn[0]);
exit(1);
}
else
wait(NULL);
}
}
return 0;
}
static void dump_cmd(char **argv)
{
int i = 0;
fprintf(stderr, "%d: Command:\n", (int)getpid());
while (*argv != 0)
fprintf(stderr, "%d: %d: [[%s]]\n", (int)getpid(), i++, *argv++);
}
I'm not keen on the code, but it does seem mostly sane.

Resources