How to Use of Execve in C
This tutorial will discuss the use of execve
to run both Linux standard commands and our executables in C.
First, we will discuss the exec
system call and the exec
’s family. Next, we will call our executables in C, and finally, we will discuss both standard Linux commands and our executables.
the exec
System Call in C
The exec
system call replaces the running process with some other executable process. The address space of the running process is replaced with the address space of the new process.
It is important to note that the new program is loaded into the same address space. The process id remains the same.
The new program will run independently; that is, the starting point will be the entry point of the new program. The exec
system call has many variants.
execl
execle
execlp
execv
execve
execvp
These functions use the same base exec
followed by one or multiple letters. The detail of the extra letters is below.
e
- Here,e
is for environment variables; this function has an array of pointers that points to environment variables. The list of environment variables is explicitly passed to the newly loaded program.l
- Here,l
is for command line arguments. We can give the list of command line arguments to the function.p
- Here,p
is for the environment variable path. In this function, the path variable helps to find the file, passed as an argument to the newly loaded process.v
- Here,v
is also for the command line arguments. However, in this function, command line arguments are passed to the newly loaded process as an array of pointers.
In the basic exec
system call, the current process address space is replaced with the newly loaded program’s address space; as a result, the currently running process is terminated. The freshly loaded process is passed as an argument in this system call.
The newly loaded process has the same process id, the same environment variables, and the same set of file descriptors. However, the CPU statistics and virtual memory are affected.
Syntax of exec
Calls
Here, we have six system calls’ syntax, variants of basic exec
system calls.
int execl(const char* path, const char* arg, ...) int execle(
const char* path, const char* arg, ...,
char* const envp
[]) int execlp(const char* file, const char* arg,
...) int execv(const char* path,
const char* argv
[]) int execve(const char* path,
const char* argv[],
char* const envp
[]) int execvp(const char*
file,
const char* argv
[]) int execvpe(const char*
file,
const char* argv
[],
char* const envp
[])
First, all functions’ return type is int
. However, in case of a successful operation (that is, the new program is loaded and replaced), nothing is returned because the current program is no more there to receive the return value.
In failure due to some error, the new program is not loaded, and -1
is returned to the existing program.
In the first argument, there is a difference between path and file. The p
, execlp
, execvp
, and execvpe
have a file instead of the path.
The path
specifies the file’s full path to be executed/loaded. The file
specifies the path name, which helps to locate the new program’s file.
In the second argument, the difference is that functions with v
have a two-dimensional array of type char
with multiple strings (including file name).
In contrast, other procedures have one or more one-dimensional arrays of type char
, where the first element of this list contains the file
name, the second element may include some parameters, etc.
Lastly, in the case of functions having e
, a third/last parameter has an environment variable as an array of pointers.
Coding Example of exec
System Call
It is better to see an example before further discussion. In this example, we are using the program’s source code as the program’s input.
Here, we have saved this program (with the name execl0.c
) in the directory of executable code. This means both source and executable code exist in the same directory.
#include <stdio.h>
#include <unistd.h>
int main(void) {
char binaryPath[] = "/bin/wc";
char arg1[] = "wc";
char arg2[] = "-w";
char arg3[] = "execl0.c";
printf("First line of current program\n");
execl(binaryPath, arg1, arg2, arg3, NULL);
printf("Last line of current program\n");
return 1;
}
The above code uses an execl
system call, having only a few simple variables of type char*
. The first variable contains the path and name of the new program (to be executed), and the second variable has a parameter wc
(again, the program’s name).
The third variable has a parameter -w
to run the command as wc -w
to count words in the source file.
It is also important to note two additional print
statements, first before the system call and second at the end of the program.
Output:
First line of current program
32 execl0.c
The output shows that our new program is successfully loaded and executed. However, note that the first print
statement is executed (see the first line of output ('First line of current program'
).
The last print
statement is not run because the current program automatically ends when a new program is successfully loaded.
The second output line shows the word count in the file execl0.c
.
the execve
System Call in C
Now, we will discuss the execve
call in detail.
Syntax:
int execve(const char* path, const char* argv[], char* const envp[])
Here, the first argument is the path
; as already discussed, the path
environment variable helps to find the program to be executed as a new program.
The second argument is a two-dimensional array of characters or a one-dimensional array of strings having a list of command line arguments.
The third argument is again a two-dimensional array of characters or a one-dimensional array of strings having a list of environment variables.
In the exec
family, execve
is a compelling command with three arguments path, a list of command line arguments, and a list of environment variables. Let’s see a code to execute the echo
command from the program.
#include <stdio.h>
#include <unistd.h>
int main(void) {
char *binaryPath = "/bin/bash";
char *args[] = {binaryPath, "-c",
"echo visit $HOSTNAME:Fun with your browser", "", NULL};
char *const env[] = {"HOSTNAME=www.delftstack.com", "port=8080", NULL};
execve(binaryPath, args, env);
return 1;
}
In the first line of the primary function, /bin/bash
is the path where the command exists. In the second line, the list of command line arguments contains three parameters before NULL
that terminates the argument.
Again, the first argument is the path, and the second parameter, -c
, stands for cmd
, which allows passing code as a string.
The third parameter is the command; like in the code, echo
is the command.
The code’s third line has two strings with two environment variables, HOSTNAME
and port
. Finally, the output of the code is:
visit www.delftstack.com : Fun with your browser
In this code, we have executed a Linux command from our program. Next, we will execute an external executable program inside the current program.
First of all, see this program:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *args[]) {
int i;
int count = atoi(args[1]);
for (i = 1; i <= count; i++) printf("[%d]", i);
printf("\n");
return 0;
}
This program is taking command line arguments. The command line argument (passed as a string) is converted to an integer in the second line of the main
function.
Next, we run a loop from one to count
and print counting in square brackets. See the output of this code.
$./ test 4 [1][2][3][4]
We have created the executable with the name test
. We have executed the test
file with parameter 4
from a command prompt.
We can see counting one to four in square brackets in the output.
Next, we have to run this program test
as an external command from another program. For this, we have to specify the path of the executable test
program.
Here, the complete path is /home/mateen/Documents/test
. Therefore, we will specify this path in our next program to locate the executable file.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *ar[]) {
printf("This is the first line\n");
char *binaryPath = "/bin/bash";
char name[80] = "/home/mateen/Documents/test ";
strcat(name, ar[1]);
char *args[] = {binaryPath, "-c", name, NULL};
char *env_args[] = {"/bin/bash", (char *)0};
execve(binaryPath, args, env_args);
printf("This is the last line\n");
return 1;
}
We have included another library to use the function to concatenate strings. In the third line of the main
function, we have a complete path and file name because this is not a Linux command; instead, this is user defined executable program (already discussed in detail).
In the following line, we are concatenating the command line argument passed to our current program with the new program’s name. Again, in the fifth line, we have command line arguments having a path, -c
.
The third parameter is the variable name having path + name of executable + argument passed to the current program.
Output:
$ ./a.out 5
This is the first line
[1][2][3][4][5]
We are running our current program with the command line parameter 5
. The first line of output has the first print
statement.
Next, you can see our test program is executed. The counting from 1
to 5
is written in square brackets.
Finally, the conclusion is we can run both Linux commands and our executable programs using execve
. In the case of the Linux command, we can pass the path to locate the Linux program.
In the case of some other/external executable, we can give a complete path with the file name; in this case, the program will be automatically located at the given path. In this case, the command will ignore the path variable in the third line of the main
function.