How can you run execvp with a "*.c". I can get it to work with a full name but not a wildcard. Any help would be greatly appreciated. This is what I have so far.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
printf("running\n");
char* args[] = { "find", "-name", "one.c", NULL};
char * envp[] ={NULL};
int pid = fork();
switch(pid){
case -1:
perror("fork() failed");
exit(1);
case 0: // child
execvp(args[0], args);
printf("after the exec\n");
default: // parent
//wait(NULL);
if(wait(NULL) == -1){
perror("wait() failed");
}
}
return 0;
}
You must do your own wildcard expansion. When you use the exec() family of functions, you are passing arguments almost directly to the new program.
If you want the replacing program to replace wildcards for you, you may well want to use a shell to do that (as system() does), but be careful, as you'll need to correctly quote for the shell.
Example:
char shell[] = "/bin/sh\0-c\0ls *.c";
char *args[] = { shell, shell+8, shell + 11, 0 };
execv("ls", args);
Note also that string literals are const char*, so shouldn't be used to populate a char*[].
However, in the case of find, you probably don't want to expand the wildcard. Here, there's no need to do anything special - just pass *.c as one of the arguments. The find command (specifically the -name argument) requires a pattern, not a list of filenames, so there's no expansion to do:
char shell[] = "/usr/bin/find\0.\0-name\0*.c";
char *args[] = { shell, shell+14, shell + 16, shell+22, 0 };
execv("find", args);
This is by design. Wildcard processing can be expensive because it requires to browse a folder. It is normally active by default in the shell, but not in API functions. One notable exception is system because it actually pass a command to the shell:
The system() function hands the argument string to the command inter-
preter sh(1)
The exec... family functions do not do that and assume that the path is a real path and do not special processing for wildcard characters. Simply, the exec[lv]p take all folder from the PATH environment variable and try to find a file with the exact name in one of them.
You must use the glob function for wildcard processing. BTW, the shell programs use it...
Related
Problem statement:-
How to pass arguments to a program for execution in a new Xterm/Gnome window which will be calling through execlp.
A little elaborate explanation:-(oxymoron eh?)
Consider the following program which will take a string as argument and display it
//output.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if(argc<2)
{
printf("insufficient parameters\n");
exit(1);
}
printf("%s",argv[1]);
sleep(10);
return 0;
}
And another program,client.cwhich, during the course of its execution is required to call output.c and make it display in a new xterm/gnome-terminal window.
//client.c
int main()
{
char buf[25]="Test String";//as argument for program to be called
int pid_child=fork();
if(pid_child==-1)
{
printf("Fork Failed. Exiting");
exit(1);
}
if(pid_child==0)
{
execlp("/usr/bin/xterm","-e","./output",buf,NULL);
}
int status=0;
while(wait(&status)!=-1);
}
The line of contention here is
execlp("/usr/bin/xterm","-e","./output",buf,NULL); //With string `buf` as argument for `output`.
Result:-Does not run
Error
-e: Explicit shell already was /~/cs60/directory/./output
-e: bad command line option "Test String"
execlp("/usr/bin/xterm","-e","./output",NULL);//Without passing variable `buf`
Result:- a) New Xterm window opens. b) output terminates with Insufficient parameters (as expected).
The manpage clearly states for Xterm:
-e program [ arguments ... ]
This option specifies the program (and its command line arguments) to be run in the xterm window.
It works perfectly fine when I run it from a terminal (as a script). But how can I achieve this through C.
Any help will be highly appreciated
You need to understand how execlp() works.
You need to add a second argument to execlp with the command name ("xterm").
execlp("/usr/bin/xterm", "xterm", "-e", "./output", buf, NULL);
Also, your output program may want to do a fflush (so you see the output), and you should exit or otherwise take proper evasive action if execl() fails. Note that when the command name ("/usr/bin/xterm") contains any slashes, execlp() behaves the same as execl().
In the linux terminal, I can type
echo hello! > /path/to/file
I thought I would be able to do the same thing using execv:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(void){
char *write_cmd[] = { "echo", "hello!", ">", "/path/to/file", NULL};
if (fork() == 0){
execv("/bin/echo", write_cmd);
}
else{
sleep(1);
}
return 0;
}
However, this code doesn't write 'hello!' to the file, which is what I want it to do. Is there another way to do this using execv and echo?
Edit:
I've tried using dup2 as a solution as well:
#include
#include
#include
int main(void){
char *write_cmd[] = { "echo", "hello!", NULL };
if (fork() == 0){
int tmpFd = open("/path/to/file", O_WRONLY);
dup2(tmpFd, 1);
execv("/bin/echo", write_cmd);
close(tmpFd);
exit(0);
}
else{
sleep(1);
}
return 0;
}
However, this doesn't give me the result I want either. This writes 'hello!' to the file, but it also overwrites everything else that was already written on the file. How can I guarantee that 'hello!' will be written to the END of the file?
You can, but only indirectly.
The > redirection operator is interpreted by the shell; /bin/echo doesn't recognize it, and treats it as just another argument to be printed.
If you want the shell to do the redirection, you need to invoke /bin/sh and pass the entire command to it as an argument.
Untested code follows:
char *write_cmd[] = { "/bin/sh", "-c", "echo hello! > /path/to/file", NULL };
// ...
execv("/bin/sh", write_cmd);
Or, more simply, you could use system().
First of all, redirection operators like > are interpreted by the shell, and mean nothing to execve(2) (or to echo, for that matter). You could try using system(3) instead, or you could set up the redirection yourself by opening the output file and setting standard out to the resulting file descriptor using dup2(2) (see this question).
Secondly, write_cmd is an array of char*, but '>' (note the single quotes) has type int. This effectively means that you are putting an integer in an array that otherwise contains pointers to strings. You probably meant to write ">".
I use linux and c.
First, I soft link bin/zsh to sh
Second, I login as root the run the following program.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *v[3];
if(argc < 2) {
printf("Please type a file name.\n");
return 1;
}
v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = 0;
/* Set q = 0 for system(), and q = 1 for execve */
int q = 0;
if (q == 0){
char *command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
sprintf(command, "%s %s", v[0], v[1]);
system(command);
}
else execve(v[0], v, 0);
return 0 ;
}
Third, I login as a normal user(not root).
Now, I can remove or rewrite a file which I don't have write privilege by using the execute file of this program.
Like this:
./a.out text;\`echo \”Not right\”>text\`”
Now I can write "Not right" into the file "text". I only have read privilege of this file
The read and write privilege of these files.
Fourth, I change q to 1. That means, this time I use execve instead.
And do the same thing as above.
But this time I cannot change the content of the file.
Why?
I google in the internet, but I can not find the different between system and execve.
system invokes a shell to parse the string and handle quoting and variable interpolations and stuff. execve does none of this. It replaces the program with the called program and passes the argument strings exactly as specified; ie. it will not interpret quotes.
You said you did chmod 4755 a.out. That means you're setting the setuid bit and the program will then always run with root privileges, and has write access to text. The string with backquote is passed to the shell which interprets it as a command to write to text.
The reason execve doesn't write to text is that it doesn't interpret its arguments as a shell command and ` doesn't have any special meaning.
I've been giving the task of writing a simple shell, I've managed to get some really basic functionality however for one of the stages it's asking me to get and set the environment. This is the details of the task.
Stage 4: Getting and setting the path – 10 marks
Keep original path
The reason this is necessary is because we would like to restore the path to what was originally on exiting the shell. This is important because any changes you do to the shell’s environment (i.e. setenv() function above), does not only affect the shell itself but also any other program that will be executed from the same terminal afterwards. For this reason, it is a good idea to put things back the way they were on exit.
A single string will be enough to keep the original path.
Saving the path should be the first thing your shell does when it starts up.
Print and change the path – built-in commands
From a C program we can access the environment using the getenv() function and we can change the environment using the setenv() function. If you look at the manual pages for setenv(), you will find how it works (i.e. parameters needed and return values) as well as what you need to include to use it.
getpath – print system path & setpath – set system path
These two commands are about the environment parameter PATH. The first just gets and prints its value, while the second takes a path (string of colon separated list of directories) as a parameter and makes it the value of PATH. You can getenv() and setenv() respectively for this purpose.
Restore path
You just change the PATH environment parameter to its original value (i.e. the one you saved at the start up of the shell).
Restoring the path should the last thing your shell does before it exits.
Stage 4: Testing
First, make sure that all the tests you carried out for stage 3 still work. Be careful though, as we are now changing the path this will affect the execution of external programs.
To check the additional functionality, you should start by checking that the save and restore of the path work. A good idea here is to print the path when you save it at the beginning of the execution of the shell and then again when you exit at the end. In both cases the printed path should be exactly the same!
Following that you should check that when getpath is called you print the current path, which should be the same as the original one.
Then you should focus on testing setpath. First, setpath the path to a new value and test that getpath prints it, then try also to see how changing the path really affects the execution of external commands (e.g. set the path to only ‘.’ and try ‘ls’ or try the shell itself, etc).
This is my code:
/* S.NO:201148541 Simple Shell Example
Completed upto Stage 3 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16
void parse_args(char *buffer, char** args,
size_t args_size, size_t *nargs)
{
char *buf_args[args_size];
char **cp;
char *wbuf;
size_t i, j;
wbuf=buffer;
buf_args[0]=buffer;
args[0] =buffer;
for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
break;
}
for (j=i=0; buf_args[i]!=NULL; i++){
if(strlen(buf_args[i])>0)
args[j++]=buf_args[i];
}
*nargs=j;
args[j]=NULL;
}
int main(int argc, char *argv[], char *envp[]){
char buffer[BUFFER_SIZE];
char *args[ARR_SIZE];
int *ret_status;
size_t nargs;
pid_t pid;
char curDir[100];
while(1){
getcwd(curDir, 100);
printf("%s->", curDir);
fgets(buffer, BUFFER_SIZE, stdin);
parse_args(buffer, args, ARR_SIZE, &nargs);
if (nargs==0) continue;
if(strcmp(args[0], "cd") == 0){
chdir(args[1]);
}
else if (!strcmp(args[0], "exit" )) exit(0);
pid = fork();
if (pid){
pid = wait(ret_status);
printf("finished\n", pid);
}
else {
if( execvp(args[0], args)) {
puts(strerror(errno));
exit(127);
}
}
}
}
return 0;
}
I'm really at a loss and any guidance would be helpful.
Given that we don't know what your previous steps are, and going by the advice
Then you should focus on testing setpath. First, setpath the path to a
new value and test that getpath prints it, then try also to see how
changing the path really affects the execution of external commands
(e.g. set the path to only ‘.’ and try ‘ls’ or try the shell itself,
etc).
You can do like this...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *path, *old, *tobeSet;
path = malloc(1024);
path = getenv("PATH");
old = malloc(strlen(path));
tobeSet = malloc(10); // just to be safe
strcpy(tobeSet,".");
if(setenv("PATH",tobeSet,1)!=0)
{
printf("Couldn't set path\n");
return 0;
}
printf("\nPATH::\t%s\n",path);
printf("\n\nNewPath::\t%s\n",tobeSet);
if(setenv("PATH",path,1)!=0)
{
printf("Couldn't restore path\n");
return 0;
}
printf("\n\nOld path ::\t%s\n",path);
free(path);
free(old);
free(tobeSet);
return 0;
}
I am trying to use execve to run the ls command. Currently I'm running it with the following arguments:
execve(args[0], args, env_args)
//args looks like {"ls", "-l", "-a", NULL}
//env_args looks like {"PATH=/bin", "USER=me", NULL}
What I expected this to do was run the ls command using my new env_args meaning that it would look up ls in my PATH. However, this code actually doesn't do anything and when I run the code it just returns to my command prompt without output.
Using the same args[] I was using execvp and ls worked and searched my current path.
Can you tell me what I am doing wrong?
What I am trying to do is write my own shell program where I can create and export my own environment and have exec use the environment I have defined in a char**. Essentially I am writing my own functions to operate on env_args to add and remove vars and when I call exec i want to be able to call exec on {"ls", "-l", NULL} and have it look down my new environments path variable for a valid program called ls. I hope this explains what I am doing a little better. I don't think the extern environ var will work for me in this case.
The execve() function does not look at PATH; for that, you need execvp(). Your program was failing to execute ls, and apparently you don't report failures to execute a program after the execve(). Note that members of the exec*() family of functions only return on error.
You'd get the result you expected (more or less) if you ran the program with /bin as your current directory (because ./ls - aka ls - would then exist).
You need to provide the pathname of the executable in the first argument to execve(), after finding it using an appropriate PATH setting.
Or continue to use execvp(), but set the variable environ to your new environment. Note that environ is unique among POSIX global variables in that is it not declared in any header.
extern char **environ;
environ = env_args;
execvp(args[0], &args[0]);
You don't need to save the old value and restore it; you're in the child process and switching its environment won't affect the main program (shell).
This seems to work as I'd expect - and demonstrates that the original code behaves as I'd expect.
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(void)
{
char *args[] = { "ls", "-l", "-a", NULL };
char *env_args[] = { "PATH=/bin", "USER=me", NULL };
execve(args[0], args, env_args);
fprintf(stderr, "Oops!\n");
environ = env_args;
execvp(args[0], &args[0]);
fprintf(stderr, "Oops again!\n");
return -1;
}
I get an 'Oops!' followed by the listing of my directory. When I create an executable ls in my current directory:
#!/bin/sh
echo "Haha!"
then I don't get the 'Oops!' and do get the 'Haha!'.