While I think I have the grasp on how fork(), exec(), wait() and pid work in C, I have yet to find a way how to run a personal program from within a program.
Here's my code:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h> /* for fork() */
#include<sys/types.h> /* for pid_t */
#include<sys/wait.h> /* fpr wait() */
int main(int argc, char* argv[])
{
char fileName[255];
pid_t pid;
switch (pid = fork()) {
case -1: //Did not fork properly
perror("fork");
break;
case 0: //child
execv(fileName[0],fileName);
puts("Oh my. If this prints, execv() must have failed");
exit(EXIT_FAILURE);
break;
default: //parent
//Infinite Loop
while (1) {
printf(" %s > ", argv[0]);
scanf("%s", fileName); // gets filename
if (fileName[0] == '\0') continue;
printf("\n Entered file: %s",fileName); // prints the fileName
waitpid(pid,0,0); /* wait for child to exit() */
break;
}
}
return 0;
}
My questions are the following:
I want to take a string as an input and I want to limit its scope to 255 characters. Is char fileName[255] and then scanf("%s", fileName); the way to go? Should I use getLine() or some other function instead?
Let's say that the input is taken correctly. How do I execute say an existing hello world program. Will the input be stored in *argv[] ? I found out that in a different program I could use
static char *argv[] = { "echo", "Foo is my name." , NULL };
execv("/bin/echo", argv);
in order to echo "Foo is my name.". Can I do something similar with a helloWorld program?
You're passing a single character as the command name, and the name string as the start of a list of arguments — as if the prototype for execv() were int execv(char cmd, char *args).
The actual prototype is: int execv(char *cmd, char **args), so you need:
char *args[2] = { fileName, 0 };
execv(args[0], args);
I assume you set fileName to a meaningful value somewhere — that isn't shown. For example, it might be "./local_program". It will be treated as the pathname of the executable.
If you want to read the name, then you can use fgets() or getline(), but you'll need to remove the newline:
if (fgets(fileName, sizeof(fileName), stdin) != 0)
{
fileName[strcspn(fileName, "\n")] = '\0';
…as before…
}
or:
char *fileName = 0;
size_t length = 0;
if (getline(&fileName, &length, stdin) != -1) /* Not EOF! */
{
fileName[strcspn(fileName, "\n")] = '\0';
…as before…
}
free(fileName);
The use of strcspn() avoids having to special case overly long command lines such that there isn't a newline in the fileName. Note that this does not attempt to split the input line into a command name and arguments at spaces or anything fancy like that. That's the next level of shell implementation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
char fileName[256];
if (fgets(fileName, sizeof(fileName), stdin) != 0)
{
fileName[strcspn(fileName, "\n")] = '\0';
char *args[129];
char **argv = args;
char *cmd = fileName;
const char *whisp = " \t\f\r\b\n";
/* Ugh — strtok()? OK while only handling white space separators */
char *token;
while ((token = strtok(cmd, whisp)) != 0)
{
*argv++ = token;
cmd = 0;
}
*argv = 0;
execv(args[0], args);
fprintf(stderr, "Oops!\n");
}
return 1;
}
I don't need to check for overflow of the args array because 256 characters of input, minus terminating null, cannot be split to produce more than 128 single-character arguments, each separated from the adjacent ones by a single white space character. Using strtok() is a temporary band-aid. As soon as you need to deal with real shell syntax (pipes, I/O redirections, quoted strings with spaces, etc), strtok() is woefully the wrong tool. (It — strtok() — is also the wrong function to use in any library function whatsoever. Use POSIX strtok_r() on Unix or Microsoft's strtok_s() on Windows if you must use a strtok()-like function in library code.)
As it stands, you'll need to fix the compilation errors from your code:
g++ -std=c++11 -g -Wall -Wextra -Wwrite-strings 36501711.cpp -o 36501711
36501711.cpp: In function ‘int main(int, char**)’:
36501711.cpp:18:25: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
execv(fileName[0],fileName);
^
36501711.cpp:18:35: error: cannot convert ‘char*’ to ‘char* const*’ for argument ‘2’ to ‘int execv(const char*, char* const*)’
execv(fileName[0],fileName);
^
36501711.cpp: At global scope:
36501711.cpp:7:5: warning: unused parameter ‘argc’ [-Wunused-parameter]
int main(int argc, char* argv[])
^
The warning (about unused argc) is harmless, but the errors are real, and need to be fixed.
You need to initialise fileName before you fork, otherwise the child won't have any valid data there:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h> /* for fork() */
#include<sys/types.h> /* for pid_t */
#include<sys/wait.h> /* fpr wait() */
int main(int argc, char* argv[])
{
char fileName[255];
pid_t pid;
//Infinite Loop
while (1) {
printf(" %s > ", argv[0]);
scanf("%s", fileName);
if (fileName[0] == '\0')
break;
printf("\n Entered file: %s\n", fileName);
pid = fork();
switch (pid) {
case -1: //Did not fork properly
perror("fork");
break;
case 0: //child
execl(fileName, fileName, 0);
perror("exec");
exit(EXIT_FAILURE);
break;
default: //parent
waitpid(pid,0,0); /* wait for child to exit() */
break;
}
}
return EXIT_SUCCESS;
}
Related
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
I am trying to make a simple shell running any command from PATH, lets say ls or pwd du gedit etc. I am having trouble with exec.I require that if i enter space nothing happens and if i type exit it terminates.Any help is appreciated
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#define BUFFER 1024
int main() {
char line[BUFFER];
char* args[100];
char* path = "";
char program[20];
while(1){
printf("$ ");
if(!fgets(line, BUFFER, stdin))
break;
size_t length = strlen(line);
if (line[length - 1] == '\n')
line[length - 1] = '\0';
if(strcmp(line, "exit")==0) break;
strcpy(program,path);
strcat(program,line);
int pid= fork(); //fork child
if(pid==0){ //Child
execlp(program,line,(char *)NULL);
}else{ //Parent
wait(NULL);
}
}
}
You have 2 fgets() call. Remove the first one fgets(line, BUFFER, stdin);.
fgets() will read in the newline if there's space in buffer. You need to remove it because when you input exit, you'll actually input exit\n and there's no command as /bin/exit\n.
The below code demonstrates removing newline character:
if(!fgets(line, BUFFER, stdin))
break;
char *p = strchr(line, '\n');
if (p) *p = 0;
Your usage is wrong. Check the manual of execl. You need to pass the arguments: execl(program, line, (char *)NULL);
Notice the cast of last argument of NULL. In case, NULL is defined as 0 then the cast becomes necessary because execl is a variadic function.
A modified example using execvp:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#define BUFFER 1024
int main(void) {
char line[BUFFER];
while(1) {
printf("$ ");
if(!fgets(line, BUFFER, stdin)) break;
char *p = strchr(line, '\n');
if (p) *p = 0;
if(strcmp(line, "exit")==0) break;
char *args[] = {line, (char*)0};
int pid= fork(); //fork child
if(pid==0) { //Child
execvp(line, args);
perror("exec");
exit(1);
} else { //Parent
wait(NULL);
}
}
return 0;
}
Hi See my correction below and see my comment as well.
int main() {
char line[BUFFER];
char* args[100];
char* path = "/bin/";
char program[20];
char command[50];
while(1){
printf("$ ");
if(!fgets(line, BUFFER, stdin))
break;
memset(command,0,sizeof(command));
if(strncmp(line, "exit", (strlen(line)-1))==0) break;
strncpy(command,line,(strlen(line)-1));
strcpy(program, path);
strcat(program,command);
int pid= fork(); //fork child
if(pid==0){ //Child
execl(program,command,NULL);
exit(0);// you must exit from the child because now you are inside while loop of child. Otherwise you have to type exit twice to exit from the application. Because your while loop also became the part of every child and from the child again it will call fork and create a child again
}else{
wait(NULL);
}
}
}
Also to support all command execution see the function execl how you need to pass the parameters in it. Accordingly you need to split your command and create the parameter list properly for execl.
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".
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.
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);
}
}