C-Execvp() always fails - c

i'm trying to make a shell in c and currently trying the ls command but the child always returns that execvp couldn't work. here is the function.first i get the path i'm in and the command i get from stdin using exec that is why i remove the newline after that i duplicate the command and using strtok(delimiter is " ") i put all the arguments in an array for execv and the "close it" with a null terminating string after that i print the elements of the array which give correct output ex(ls -l give ls "newline" -l) and then i print the path who also gives correctl output (/home/usr_name/Desktop). But the execv never works and it returns the fprintf
void ls(char* path,char* cmd){
int pid,elements,l,i=0;;
l=strlen(cmd);
if(cmd[l-1]=='\n'){
cmd[l-1]='\0';
}
char* ccmd=strdup(cmd);
char* t_cmd;
char* w_cmd[1024];
t_cmd=strtok(ccmd,DELIM);
while(t_cmd!=NULL){
w_cmd[i]=t_cmd;
t_cmd=strtok(NULL,DELIM);
i++;
}
w_cmd[i]='\0';
elements=i;
for(i=0;i<elements;i++){
printf("%s\n",w_cmd[i] );
}
printf("%s\n",path);
pid=fork();
if(pid==0){
execvp(path,w_cmd);
fprintf(stderr, "Child process could not do execvp\n");
}
else{
wait(NULL);
printf("Child exited\n");
}
}

You say that path is equal to "/home/usr_name/Desktop" (with your real user name of course)?
Then that is the problem. If you read an exec manual page it will tell you that the first argument is the command that you should execute.
So to execute ls -l with execvp you should do e.g.
char *w_cmd[] = { "ls", "-l", NULL };
execvp(w_cmd[0], w_cmd);

Related

Why is execv doesn't work in sort command

Hi I'm trying to use execv function in forked child process to execute the commands like: ls, grep, sort, awk and so on in terminal.
However, ls, grep works but sort, awk and other things are not working and execv returns -1 for failure.
Inputs for argv was "sort t.txt" by stdin and argv[0]=sort, argv[1]=t.txt. When I put in grep 5 t.txt for example, it printed out 3 numbers in t.txt that includes 5 and ls printed out every file in directory. Why do only ls and grep work and rest of commands don't?
char* command(char** argv) {
char* ptr = malloc(200);
if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "man") || !strcmp(argv[0], "grep")
|| !strcmp(argv[0], "sort") || !strcmp(argv[0], "awk") || !strcmp(argv[0], "bc")) {
sprintf(ptr, "/bin/%s", argv[0]);
}
return ptr;
}
//inside fork=
char* pos = command(argv);
if (execv(pos, argv) < 0) {
printf("%s\n", argv[0]);
if(argv[1]!=NULL)
printf("%s\n", argv[1]);
if(argv[2]!=NULL)
printf("%s\n", argv[2]);
perror("execv");
fprintf(stderr, "%s: Command not found.\n", argv[0]);
free(pos);
exit(0);
}
free(pos);
mini> sort t
sort
t
execv: No such file or directory
sort: Command not found.
mini> ls
Makefile minishell.c pa2 t
mini>
The problem is because you hard-code the commands to be found in /bin/, and some commands (like the sort command) isn't in that location (sort is typically in /usr/bin/).
To solve that problem, and to be able to use any command in the standard path, use execvp instead:
execvp(argv[0], argv);
The difference from execv is that execvp will use the environment variable PATH to search for the command.
Please read the exec family manual page for details about the different versions.

no such file or directory when using execv()

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};

How to use execv system call in linux?

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!

Why am I having difficulty making execvp in C work?

I need to implement a basic shell in C.
One of things I need is to implement a function that has a command and to execute it.
my code:
pID=fork();
if (pID == 0)
execvp(tmp[0], tmp);
else if (pID > 0)
{
printf("%d", pID);
wait(NULL);
}
else
printf("Failed to create proccess \n");
The problem is that no matter what is the command I put in tmp, the program shows me the prompt again, and do nothing except that.
For example if I write gedit (in order to open the gedit — a ntpad of Ubuntu), it doesn't open it, or if write ls -a it doesn't show me any output as the terminal of Ubuntu does.
execvp should work. As the others mentioned, you really need to show how you populate tmp. That said, I would guess that that's where the error is. tmp needs to be a null terminated array.
#include <stdio.h>
main( int argc, char * argv[] )
{
int pid = fork;
char * tmp[2];
memset( tmp, 0, sizeof(tmp) );
tmp[0] = argv[0];
if( 0 == pid )
{
if( -1 == execvp( tmp[0], tmp ) )
{
char errmsg[64];
snprintf( errmsg, sizeof(errmsg), "exec '%s' failed", tmp[0] );
perror( errmsg );
}
else if( 0 < pid )
{
printf("[%d] %s\n", pid, tmp[0]);
wait(NULL);
}
else
{
perror("fork failed");
}
}
Although you've failed to tell us what you're passing through the tmp variable to execvp, my psychic sense tells me that you forgot to null-terminate your argument list. A NULL argument tells execvp where the last argument is, and if you fail to put in a NULL, it will start reading random garbage off the stack.
If that random garbage points to large strings of non-zero data, it will run out of space to store the supposed arguments to the new process, which is typically a few hundred KB (see this page for some system-specific numbers, as well as various ways of getting your system's maximum arguments size).
When there's too much argument data, the system call execve(2) (called internally by execvp) fails with the error E2BIG.
So to see if this is what's happening to you, check the return value from execvp. If it even returns at all, it failed (if it succeeded, it wouldn't have returned since a new process would be executing!), so check the global value of errno to see why it failed:
if (pID == 0)
{
execvp(tmp[0], tmp);
printf("exec failed: %s\n", strerror(errno));
exit(1);
}
execvp() requires full path . If in tmp[0] isnt the full path of your executable file use execv()
execv(tmp[0], tmp);

Executing a separated command using execvp in C

I've separated a given command from the user into substrings , here's the code :
int i;
char *line = malloc(BUFFER);
char *origLine = line;
fgets(line, 128, stdin); // get a line from stdin
// get complete diagnostics on the given string
lineData info = runDiagnostics(line);
char command[20];
sscanf(line, "%20s ", command);
line = strchr(line, ' ');
printf("The Command is: %s\n", command);
int currentCount = 0; // number of elements in the line
int *argumentsCount = &currentCount; // pointer to that
// get the elements separated
char** arguments = separateLineGetElements(line,argumentsCount);
// here we call a method that would execute the commands
if (execvp(*arguments,*argumentsCount) < 0) // execute the command
{
printf("ERROR: exec failed\n");
exit(1);
}
When I execute the command in execvp(*arguments,*argumentsCount) , it fails .
What's wrong ?
Thanks .
EDIT :
The input from the user is : ls > a.out , hence I have 3 strings , which are :
ls , > , a.out , and it fails .
Shell redirection won't work if you aren't invoking a shell. You also won't have path searching to find the ls program. Some options
use system() instead, and exit when it returns
exec a shell and have it run your command
setup redirection as a shell would, then fork and execute each required child program.
Also your command doesn't make a lot of sense, you probably want ¦ instead of > and may need to specify the directory of a.out if it is not in your path. Consider giving it a meaningful name as well.
From man page of execvp command:
int execvp(const char *file, char *const argv[]);
The second argument is a list of null-terminated C-strings as arguments to the command to be executed by execvp. But in your code, you pass an int as the second argument which is wrong.
If you have list of arguments in the variable arguments then call execvp as:
execvp(arguments[0],arguments);
When you run ls > a.out at the command-line, > and a.out are not arguments passed to the application; they're interpreted by the shell to redirect stdout.
So in short, it is not possible to do what you want to do.1
1. Well, it is, but not this way. Your application would need to interpret the arguments, create the file, and set up a stream redirect.

Resources