I attempted to use execve to run the command: ls -l /tmp, I'm familiar with the system call usage but I do not know how it runs commands.
I've tried this and i keep getting errors
char *argv[] = {"ls", "-l", NULL};
int exec;
exec = execve("/tmp", argv, NULL);
printf("%d\n", exec);
return (0);
You don't use execve correctly. The first argument has to be the executable path.
So your code should be
int main(void) {
char *argv[] = {"ls", "-l", "/tmp", NULL};
execve("/bin/ls", argv, NULL);
// Or "/usr/bin/ls", depending where
// is stored 'ls' on your system
// On success theses lines are not reached
perror("execve failed");
return 0;
}
Note that (from man execve):
RETURN VALUE
On success, execve() does not return, on error -1 is returned,
and errno is set to indicate the error.
If the execve call is a success, your program will be replaced by the launched one (ls here), so the two last lines won't be executed
The first argument to execve is the path to the program to execute.[1]
char *argv[] = { "ls", "-l", "/tmp", NULL };
execve( "/usr/bin/ls", argv, NULL );
Note that we had to provide the full path to the binary. To avoid this by searching the PATH when needed, we can use one of the "p" variants instead.
char *argv[] = { "ls", "-l", "/tmp", NULL };
execvpe( argv[0], argv, NULL ); # execvpe is a GNU extension.
Passing NULL for the envp parameter is not standard. On Linux, it causes the new program to have an empty environment, which is very unlikely to be what's desired here. Instead, use
char *argv[] = { "ls", "-l", "/tmp", NULL };
execvp( argv[0], argv );
Finally, the purpose of exec* is to change the program the current process executes. As such, it only returns on error, and it always returns -1 on error. It's not useful to print this. To print the error message, one can use perror.
To set the path to use as the current work directory, use chdir before exec*.
The first argument to execve must be the path to the program to execute:
exec = execve("/bin/ls", argv, NULL);
The second argument, argv, must be a list of all the arguments:
char *argv[] = {"ls", "-l", "/tmp", NULL};
Related
I am trying to implement a simple shell program that runs user input commands. I want the user to enter "ls" or "dir" and have the shell run /bin/ls or /bin/dir . For the execve argument what would be correct:
char *args[] ={"/bin/", "ls", NULL};
//Fork, pid, etc...then
execve(args[0], args+1, NULL);
Or would it be something different?? I've see some people using /bin/ls as the pathname and then no arguments or ls as the pathname and \bin for the environment? I tried what I had above and it didn't work so I want to know if it is the arguments I am sending it or whether I should be looking elsewhere in my code for the problem. Note I am not interested in other variations of execve such as execvp. I am on a Linux system. Thanks
PATHNAME
The pathname in execve() must be the full path to the executable, such as /bin/ls. If using execvpe(), you could use ls alone as the pathname, but as you already specified, you don’t want to use that.
ARGUMENTS
The arguments should be an array of strings, one for each space-separated argument specified on the command line. The last one should be NULL. The first argument should be the pathname itself. For example:
char* args[] = {"/bin/ls", "-la", "foo/bar", NULL};
ENVIRONMENT
The environment variables cannot be omitted when using execve(). In some implementations, NULL can be passed as the last argument to execve(), but this is not standard. Instead, you should pass a pointer to a null pointer; essentially an empty array of environment variables.
Putting it together
char *args[] ={"/bin/ls", "-foo", "bar", NULL};
//Fork, pid, etc...then
char* nullstr = NULL;
execve(args[0], args, &nullstr);
From execve [emphasis added]:
int execve(const char *pathname, char *const argv[],
char *const envp[]);
execve() executes the program referred to by pathname. This
causes the program that is currently being run by the calling
process to be replaced with a new program, with newly initialized
stack, heap, and (initialized and uninitialized) data segments.
pathname must be either a binary executable, or a script starting
with a line of the form:
#!interpreter [optional-arg]
For details of the latter case, see "Interpreter scripts" below.
argv is an array of pointers to strings passed to the new program
as its command-line arguments. By convention, the first of these
strings (i.e., argv[0]) should contain the filename associated
with the file being executed. The argv array must be terminated
by a NULL pointer. (Thus, in the new program, argv[argc] will be
NULL.)
In your case, the pathname should be "/bin/ls" and not "/bin/". If you want to pass any command line argument with the command, you can provide first argument them with argv vector index 1, second argument with index 2 and so on and terminate the argument vector with NULL.
A sample program which replace the current executable image with /bin/ls program and runs /bin/ls testfile:
#include <stdio.h>
#include <unistd.h>
int main (void) {
char *args[] = {"/bin/ls", "testfile", NULL};
// here you can call fork and then call execve in child process
execve(args[0], args, NULL);
return 0;
}
Output:
# ./a.out
testfile
I need to redirect stdout of a command invoked from kernel space using the linux usermode helper api. Using "echo" commmand as an example below. This does NOT work.
static int umh_helper() {
struct subprocess_info *sub_info;
char *argv[] = {"/bin/echo", "Hi", ">", "/tmp/command.out", NULL};
static char *envp[] = {
"HOME=/root",
"TERM=linux",
"PATH=/bin:/sbin:/bin:/usr/sbin:/usr/bin", NULL };
return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );
}
The command I need to run does NOT support a way to take file name as its input argument to write the output to file directly and hence I need to do this.
Im trying to write a basic shell that can interpret simple commands like date, ls in the language c.
I start by getting the PATH variable like this to later pass it on to the execv() function.
const char *name = "PATH";
char *value;
value = getenv(name)
and i print out the value and i get this:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
Note that i am using virutalbox to run Ubuntu. And this is the code that i am using to try a simple ls command. In the code below the variable line is the actual command that the user wrote, in our case it is "ls"
pid_t pid, wpid;
int status;
pid = fork();
if (pid == 0) {
// Child process
if (execv(value, line) == -1) {
perror("lsh");
}
exit(EXIT_FAILURE);
}
else if (pid < 0) {
// Error forking
perror("lsh");
}
else {
// Parent process
do {
wpid = waitpid(pid, &status, WUNTRACED);
}
while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
And the result i get is this:
lsh: no such file or directory
any ideas?
The execv() system call uses the name you specify in the first argument as the file name of the executable; it does not do a PATH-based search.
That means that if you specify "lsh" as the first argument, there must be an executable file lsh in the current directory.
If you want a PATH-based search, replace execv() with execvp(). Otherwise, specify the pathname — absolute or relative, but absolute is more normal — of the command in the first argument.
Note that if any of the exec*() functions returns, it has failed. There's no need to test the return value; it will always be -1.
The contents of value and line need to be along the lines of:
char *value = "ls";
char *line[] = { "ls", "-l", 0 };
execvp(value, line);
or, more conventionally:
execvp(line[0], line);
If you are analyzing the PATH yourself, you'll need to have line[0] pointing at the complete file name that you've created from PATH, and then you use execv() instead of execvp().
The first argument to execv is the command to run. This means you're trying to run /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games as a command.
Assuming the first value in the line array is the program you want to call, you should instead do this:
execv(line[0], line);
And if you want to perform a path-based search, use execvp instead (no need to manually extract the PATH variable):
execvp(line[0], line);
EDIT:
For example, suppose you wanted to run ls -l /usr/bin /var/log, your array would look like this:
char *line[] = { "ls", "-l", "/usr/bin", "/var/log", NULL};
I am writing a program using execl to execute my exe file which is testing and it's work very well and display the output in the Linux CLI. But I have not idea how to change the execl to execv, although I know both of the system call will give the same value. I am confused with the array argument for execv system call
This is my execl sample program
int main(void)
{
int childpid;
if((childpid = fork()) == -1 )
{
perror("can't fork");
exit(1);
}
else if(childpid == 0)
{
execl("./testing","","",(char *)0);
exit(0);
}
else
{
printf("finish");
exit(0);
}
}
can I know how to change the execl to execv. What I read from online, we must set the file path for my exe file and the argument of array . What type of argument need to set for the array in order to ask the program to execute the testing exe file ?
https://support.sas.com/documentation/onlinedoc/sasc/doc/lr2/execv.htmIs it the link consist of the thing I want ? But what I read from it ,the command is request the list the file,not execute the file. Correct me I make any mistake
In order to see the difference, here is a line of code executing a ls -l -R -a
with execl(3):
execl("/bin/ls", "ls", "-l", "-R", "-a", NULL);
with execv(3):
char* arr[] = {"ls", "-l", "-R", "-a", NULL};
execv("/bin/ls", arr);
The char(*)[] sent to execv will be passed to /bin/ls as argv (in int main(int argc, char **argv))
According to the man page the use of execv is quite simple. The first argument is the path as a string to the program you want to execute. The second is an array of string that will be used as the arguments of the program you want to execute. It is the kind of array you get if you get the argv array in your main function.
So the array you will pass as a parameter will be the array received in the main function of the program you execute with execv.
By convention, the first argument should be the program name (the one you try to execute) but it is not mandatory (but strongly recommended since it is the behaviour a lot of programs are expecting). Each other string in the array should be an individual argument.
And of course, the array should be terminated with a NULL pointer to mark the end.
Array example: ["prog_name", "arg1", "arg2", "arg3", NULL]
[] is your array, each string separated with a coma is a frame of your array and at the end you have the null frame.
I hope I am clear enough!
I'm working on a project that basically does the same thing as strace(1) using ptrace(). Basically we have a controller.c program that takes an executable as an argument and it outputs any system calls made by the executable (for example % controller ls -l)
We are using the execve() to run the executable file, but we are having a little bit of trouble. execve takes the following arguments
execve( const char *filename, char *const argv[], char *const envp[] )
where filename in this instance would be "ls", and argv[] is a list of the arguments for the given file name. So we have something that looks like this (in the C file)
int main(int argc, char *argv[],char *envp[]){
pid_t child;
child = fork;
if(/* CHILD */){
ptrace(PTRACE_TRACEME,0, NULL, NULL);
if(argc == 2) {
execve(argv[1],NULL,envp);
}
else {
execve( argv[1], /* ARGUMENT LIST */, envp);
}
} else /* PARENT */ {
//PARENT CODE
}
}
So if we get an executable, for example controller ls -l, where argv[0] = "controller", argv[1] = "ls", and argv[2] = "-l", how can we pass the correct parameter at "ARGUMENT LIST" (where the arguments in this case is just "-l" but it could be more)?
Basically, how do we initialize an array of type const char * such that the array has the argument values for the executable? Do we even need to worry about having extra values in the array and just past argv for ARGUMENT LIST?
Thanks for your help!
You can simply pass argv + 1 (to skip your program's name). argv + 1 is a pointer to an array starting from the second element of argv, which is the executed program's name. argv + 1 is equivalent to &argv[1], and you can use whatever style you prefer.
As mentioned in the comments, section 5.1.2.2.1 of the standard specified that argv is indeed null-terminated. The section below is retained for completeness.
In case C doesn't guarantee argv is null-terminated (and it's not clear to me whether it does or not), you can do:
char **new_argv = malloc((argc-1) * sizeof(char*));
for (int i = 0; i < argc - 1) new_argv[i] = argv[i+1];
and use new_argv for the argument list.
execve(argv[1], argv+1, envp);