c programming using execvp - c

I'm working on a side project where I want to spawn 5 children processes then execvp into a second program called isEven which will tell if the number passed to it is even or not. However, the execvp call keeps failing as I ran it in gdb and it doesn't execute.
Args and load(args) are both called in another function.
Thanks in advance!!!
//Code to load binary
void load(char* args[])
{
execvp("./isEven", args);
printf("ERROR\n");
}
//args being passed in
char *args[] = {"./isEven", number, NULL};
load(args);
Here is a smaller program I wrote which does the same thing I'm trying to do right now. Gdb says I am getting a segmentation fault.
int main(int argc, char *argv[])
{
int returnCode = fork();
if(returnCode == 0)
{
char *args[] = {"./isEven", 12345, NULL};
execvp("./isEven", args);
printf(errno);
printf("ERROR: execvp failed\n");
exit(-1);
}
else if(returnCode < 0)
{
fprintf(stderr, "fork failed\n");
}
else
{
//i am the parent
printf("I am the parent");
}
return 1;
}

The main problem is with this line:
char *args[] = {"./isEven", 12345, NULL};
The args passed to execvp must be an array of char*. Having 12345 as an argument makes it interpret it as address and it's unlikely to be a valid address. In any case, that's not what you wanted to pass. So do:
char *args[] = {"./isEven", "12345", NULL};
You need to convert number into a string. For example:
int number = 12345;
char num_str[256];
snprintf(num_str, sizeof num_str, "%d", number);
char *args[] = {"./isEven", num_str, NULL};
execvp("./isEven", args);
perror("exec");
In your modified example, you are printing errno as:
printf(errno);
This will fail because printf expects a string as its first argument. You can use perror() to print the error as shown above.

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

Two way communication from the file that run with exec in child

I am creating a child process with a fork in program y. In that child, I run another program with exec, in which I want the function in that program (let's call it program x) to return something to me. Is there a way to pass this returned value to the parent?
I provided some sort of a pseudo-code that demonstrates what I want to do below.
program.x:
int main(int argc, char** argv)
{
if(argc != 2)
{
printf("argument count does not match\n");
return -1;
}
printf("task1!\n");
...
char *value = "want this"; // how to pass this to the parent in the program y?
...
}
program y:
int main(int argc, char *argv[])
{
int fd[2];
pipe(fd);
pid_t p;
p = fork();
if(p==-1)
{
printf("There is an error while calling fork()");
}
if(p==0)
{
printf("We are in the child process\n");
printf("Calling hello.c from child process\n");
char *args[] = {"Hello", "C", "Programming", NULL};
execv("./hello", args);
close(fd[0]);
write(fd[1], ???, ??);
close(fd[0]);
}
else
{
printf("We are in the parent process");
wait(NULL);
close(fd[1]);
read(fd[0], ???,???);
close(fd[0]);
}
return 0;
}
The only thing you can pass directly is the exit code of the child (via wait()).
To pass a string between two processes, you need an IPC data structure like pipes. See the pipe() function in unistd.h.
For the simple case where there is one-way communication from a child to a parent), you can use popen. It's high level, simple to use, and has little overhead (if any) over fork/exec
int main(...)
{
...
FILE *fp = popen("./hello 'Hello', 'C', 'Programming'", "r") ;
char resp[200] ;
if ( fgets(resp, sizeof(resp, fp) ) {
// Do something
}
int result = pclose(fp) ;
}
Note that the way to pass command line arguments follows shell rules - arguments may need to be quoted (usually, single quote) to pass any special characters.
The 'pclose' result is the exit code of the executed program.

Making simple unix shell using fork,execvp

printf(*arg);
execvp(*arg, arg);
Here printf() statement prints value= ls.But when running program execvp gives there is no such file or directory.
else if (pid == 0) {
printf(*arg);
execvp(*arg, arg);
char* error = strerror(errno);
printf("shell: %s: %s\n", arg[0], error);
return 0;
if(execvp(arg[0], arg)<0)
{
printf("***ERROR: execution failedn\n");
}
return 0;
}
In the following code are two examples of how to use execvp.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *args[] = {"ls", "-l", NULL};
/* an example with a decleared array containing the commande */
if (fork() == 0) {
execvp(args[0], args);
}
/* another example where the commande was passed to main */
printf("argv[0] is the programme/executable name!\nargv[0] = %s\n", argv[0]);
if (fork() == 0) {
execvp(argv[1], argv + 1);
}
return EXIT_SUCCESS;
}
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.

how to store execvp result in a char array as a string

If a user types "ls" execvp displays the result of "ls" to the screen. I would like to store this in a char array as a string. Can anyone help me?
THanks in advance.
int main () {
char response[1000];
char *buffer[100];
int pid, status;
printf("Please enter the shell command: ");
scanf("%s",&response);
pid = fork();
if (pid < 0) {
printf("Unable to create child process, exiting.\n");
exit(0);
}
if (pid == 0) {
printf("I'm the child.\n");
*buffer = response;
execvp(*buffer,buffer);
printf("execvp failed\n");
}
else{
wait(&status);
exit(0);
}
}
popen() is more appropriate for your purpose than execvp() since you want to read the output from the exec'ed command (See the linked manual for an example).
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen() returns FILE * using which you can read the output returned by the command.

Simple shell implementation with C in linux, fork causing infinite loop

I'm trying to implement a simple shell in C and my program is causing an infinite loop and creating a ton of new processes/printing Myshell> before the user has a chance to enter in any commands. I can't seem to find a way to prevent this and if anyone could help that would be great! (Didn't paste the #include headers at top
int main(int argc, char *argv[]){
char buffer[512];
int *status;
size_t nargs;
pid_t pid;
char delim[] = "\n";
while(1){
pid = fork();
if(pid){
printf("Myshell> ");
fgets(buffer, 512, stdin);
//parse(buffer, argv);
argv[0] = strtok(buffer, delim);
for(argc=1; argv[argc-1]; argc++){
argv[argc] = strtok(NULL, delim);
}
if(!strcmp(argv[0], "exit"))
exit(0);
printf("Waiting for child (%d)\n", pid);
pid = wait(status);
printf("Child (%d) finished\n", pid);
exit(0);
}else{
if(execvp(argv[0], argv)){
printf("error");
exit(1);
}else{
exit(0);
}
}
}
return 0;
}
Because you are reading command into buffer using fgets but not from command line argument So argv[argc] for argc > 1 is wrong - undefined behaviour.
When you don't pass any extra command line argument then argv[0] is your program name and argv[1] is NULL. indexing to argv[] for value more then 1 causes array out of index problem.
Instead of declare argv[] and argc as main function parameter declare within main as formal variable something like:
int argc;
char* argv[MAX]; // max number of argument can be pass to your shell
One more rectification in your code, change:
int *status;
as
int status;
and accordingly correct
pid = wait(status);
as
pid = wait(&status);

Resources