Assume, I have one line bash script that executes everything it has in arguments
#!/bin/bash
$1
So, the command ./one_line_script.sh "ls -lh" works fine.
Also I have C code, that accepts arguments and send them to my bash script
int main (int argc, char* argv[])
{
char command[1000];
sprintf(command, "/path/to/one_line_script.sh %s", argv[1]);
system(command);
return 0;
}
And here I've got a problem, because ./c_program "ls -lh" returns only ls output. It doesn't understand few arguments. How do I need to modify my C code, so it could accept few arguments?
I would recommend to use fork and exec directly to avoid quoting issues altogether. Consider for example what happens if a ' is contained within an argument - when doing sprintf cmd-line butchering this leads to broken command lines.
int pid = fork();
if(pid == 0) {
execl("/bin/sh", "sh", "-c", arg1, arg2, arg3, 0);
} else {
int status=0;
waitpid(pid, &status, 0);
}
You need to quote it in your sprintf too, or bash will only receive one argument :)
sprintf(command, "/path/to/one_line_script.sh '%s'", argv[1]);
I've added quotes (') around the %s.
You can also use $# instead of $1 in your bash script, so it will take all arguments passed to it.
Try:
int i;
command[0] = '\0';
for (i = 1; i < argc; i++)
strcat (command, argv[i]);
system (command);
This should work, but please be aware that it has a lot of security hazards: first of all executing any possible command you get on the command line might allow users to do things they normally aren't allowed to (don't setuid your program!). Then the buffer might easily overflow and allow all kinds of stack smashing. So I'd say: only use this program as a learning tool, to learn manipulation of argc/argv and to begin thinking about security. Don't even compile it!
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 have made a small program that forks and executes another program. Basically it's supposed to work just like unix shell.
Here is my code:
int main(int argc, char *argv[]){
pid_t cpid;
char *shell[5];
shell[0]=argv[1];
shell[1]=argv[2];
shell[2]=argv[3];
shell[4]=NULL;
if(argc!=4){
printf("Program expects 4 arguments");
} else{
cpid=fork();
if(cpid==0){
execvp("/bin/sh",shell);
}//end child process
if (cpid != wait(NULL)) { /* parent code */
printf("Parent failed to wait");
return 1;
}
}//end else
}//end main
However, when I give the command
$ ./shell simple sml_prog1 A
it says sml_prog1 not found about 15 or 20 times.
The shell is supposed to run simple which takes sml_prog1 A as arguments.
The program does work on its own with the same arguments.
I changed the permissions of sml_prog1 to read/write/executable. Moreover sml_prog1 is a .txt file that contains data that the program simple uses
The main issue is how you're calling execvp. You don't want to execute /bin/sh, you want to execute the program the user passed in, i.e. argv[1].
Change the call to this, and add the following error checking:
execvp(shell[0],shell);
perror("exec failed"); // This line never gets called unless execvp fails
exit(1); // end the child process
Also, you never set shell[3] to anything. You probably want to set this to NULL instead of shell[4]:
shell[0]=argv[1];
shell[1]=argv[2];
shell[2]=argv[3];
shell[3]=NULL;
The simplest possible example of using execvp() I can think of is as follows:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
++argv;
if (*argv)
execvp(*argv, argv);
return 0;
}
Compile it with:
cc -Wextra -Wall some.c -o some
And run it like:
./some ls -la
Or to display that all the arguments are getting passed to execvp() more clearly:
./some ls -la -R /etc
To demonstrate it put to use with your exact scenario of invoking a shell compare the output of these two commands (be warned this it is pretty redundant to use /bin/sh directly when one is already using execvp()):
./some bash -c 'type history'
./some sh -c 'type history'
If you need to execute your program through /bin/sh, not directly, you must do it differently.
You must pass the program somehow this way:
char * shell[4];
shell[0] = "sh";
shell[1] = "-c";
shell[2] = "./simple sml_prog1 A";
shell[3] = NULL;
See ? with -c option, and the full program
So something like this should do the job for you:
char * shell[4];
shell[0] = "sh";
shell[1] = "-c";
char prog[100]; // be careful with this number
snprintf (prog, 100, "./%s %s %s",argv[1], argv[2], argv[3]);
shell[2] = prog;
shell[3] = 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'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 = ¤tCount; // 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.
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.