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};
Related
I'm making my own shell in C. The input window appears, but when I enter the command ls, it does not respond.
#define MAX 64
char buf[MAX];
void * get_next_command(void);
int main(int argc, char* argv[])
{
while(1){
char *cmd = get_next_command();
int child_pid = fork();
if(child_pid <0){
perror("error\n");
return -1;
}
else if(child_pid == 0){
execle(cmd,cmd,NULL);
exit(0);
}
else{
wait(NULL);
}
}
return 0;
}
void * get_next_command()
{
printf("Shell > ");
fgets(buf,MAX,stdin);
buf[strlen(buf)-1]='\n';
}
I wonder how to run commands in my own shell. Any reply will be thankful. Best regards.
While making a shell seems simple, you actually have to work a bit more on this to make it work. The thing is execle asks for the path of the file to execute. For example ls is actually /bin/ls so that's what you need to pass on as a first argument for your program to work. If you wanna go deeper into this and make a real custom shell, you have to get your environment through your main like this:
int main(int ac, char **av, char **env);
This will get your environment (you can type env in your terminal to see what it's like) then you'll be able to get the PATH variable to get all the paths separated by : for your binaries (like /bin which is used by /bin/ls).
As for execle, if you wanna pass on the arguments you have, you have to pass them like this:
For example if the command is ls -l -a
then you will run:
execle("/bin/ls", "ls", "-l", "-a", NULL);
But you can also do the same with execv which works with a char ** instead of strings for your arguments. AND if you go even deeper into this you can pass on your custom environment with execve (the third argument being your environment).
Here's how you should call your function (replacing the hardcoded values by variables of course):
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);
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!
pid_t childPid = fork ();
if (childPid == (pid_t) 0)//zero success
{
const char *path = "/usr/local/mysql/bin/mysql";
//doesn't work
//char * const parmList[] = {"--user=root", "test_db", NULL};
//does work
char * const parmList[] = {"", "--user=root", "test_db", NULL};
execv(path, parmList);
printf("ERROR:\tFork failed.\n");
}
else if (childPid < (pid_t) 0)// -1 failure
{
/* The fork failed. */
printf("ERROR:\tFork failed.\n");
return EXIT_FAILURE;
}
else
{
while (true) {
//stay alive
sleep(1);
}
}
printf("done");
exit(0);
I am having trouble importing a sql dump by using execv. You can see I wasn't able to login using the first paramList but the second one worked just fine. Anyways, if I add to the param list:
char * const parmList[] = {"", "--user=root", "test_db", "<", "/Users/joelsaltzman/Desktop/dump.sql", NULL};
The output shows the mysql help for the command line args like I typed something wrong.
Does anybody know how to get this to work?
The first paramList is incorrect, because the first element should be the filename of the program you are going to execute:
The argument argv is an array of character pointers to null-terminated strings. The application shall ensure that the last member of this array is a null pointer. These strings shall constitute the
argument list available to the new process image. The value in argv[0] should point to a filename that is associated with the process being started by one of the exec functions.
The input redirection with < does not work because this is not a feature of the kernel (which you invoke using execv), but of usual Unix shells. The system library call is what you are looking for. (It also just uses a call from the exec-family, but calls a shell with your command, which will then support <.)
Be sure to read the manpage system(3) and think about input validation if you are going to pass it a string that could be influenced by a malicious user.
The second one works better, because the first parameter should be the command name. Therefore, MySQL starts reading from the second parameter. You should use the command name (the path), not an empty string, but it normally doesn't matter.
You can't use redirection with execv, because this is a shell feature, and execv doesn't run the shell. You can execute /bin/sh, with parameters that tell it to run mysql, or you can use dup2 to change stdin to whatever you want.
Use popen() instead to start mysql, and then write the contents of the sql file into the process yourself.
I'm writing my own unix terminal and I'm running into a problem executing commands:
First I take the user input and store it into a buffer, then I separate the words and store them into my argv[] array.
i.e
command is "firefox" to launch firefox which is stored in argv[0]
How do I launch the command? This is what I'm trying to do, but I'm getting errors:
void launchProcess(char *command[], char *file){
pid_t pid;
pid = fork();
if (pid == -1) {
perror("Error when forking");
exit(EXIT_FAILURE);
}
if (pid == 0){
fprintf(stderr, "Child pid = %d\n", getpid());
execv(file, command);
}
When I call:
launchProcess(commandArgv, "STANDARD");
I get the following errors:
error: conflicting types for launchProcess
If you have a conflicting type error, you should make sure that the function you listed has the same type as its declaration.
Also, you probably already know, but execv requires a fully qualified path to the executable, so a value like "STANDARD" isn't going to work; use execvp if you want it to use the PATH variable to determine the location of the binary. You should also make sure the last value in the argv array is NULL. Finally, make sure to check the return value of execv; there is a definite possibility it can fail, e.g., if the user tries to execute a program that doesn't exist.
You need to prototype the function: add "void launchProcess(char *command[], char *file);" above your main function.
Your data types look correct, but based on the parameter names: "file" and "command", It looks like you might be using the function in the wrong way.
Here's an example of executing the ls function with no arguments.
char *args[] = { NULL };
execv("/bin/ls", args);
These notes on exec and wait might help some.