What's the difference between system() and execve() - c

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.

Related

Why isn't redirected input considered a command line argument?

I'm trying to read command line arguments that have been redirected from a file. The command I'm using is ./a.out < test.txt
And the contents of test.txt is: Hello world.
But the output of my program below isn't printing Hello
world. Instead it is only showing ./a.out. Why is this?
int main(int argc, char* argv[], char* envp[]) {
for (int i = 1; i < argc; i++) {
printf("%s\n", argv[i]);
}
}
The shell intercepts the redirection commands before preparing the command line for the program:
myProg <infile -t >outfile
will pass to the program
myProg -t
with stdin and stdout already rerouted before the pogram starts. So the program never sees the rediretion.
There a lot of cases, besides simple derirection:
dir > myfile.txt
Especially you can pipe output from one program to another:
dir | more
It will send output if dir command to more command. Since program launch handled by OS shell, it handles a redirection too.
Because the language is defined that way. Suppose what you say is true —
All the user input will have to come from command line arguments, but text redirected from a file can satisfy input required in different functions. This can be achieved if the input appears as command line arguments.
Consider this program:
#include <stdio.h>
int is_dict(char *word)
{
/* code to look up a dictionary */
int result = 1;
return result;
}
int main(int argc, char *argv[])
{
if(argc == 2 && is_dict(argv[1]))
printf("%s found", argv[1]);
return 0;
}
If the program is written that way to accommodate it, then the input would have to come from the command line arguments. How would you take input when it is not redirected? It would require more program overhead to detect the missing inputs.
Moreover, imagine a text file containing a million words: it is unfeasible to expect each word to arrive as an argv[n].
There are other objections too. Suppose the program prints a series of prompts for responses. The user would have to know in advance what the prompts are, to supply the answers before the prompts appear.
Lastly, if the program is run from a GUI, then all the program's input will have to be edited into its properties before it is run.

How many ways are there to execute system command in C program for windows

I am using MS visual studio 2008, for C coding.
I know we can use
"int system(const char *command)" to execute commands.
Is there any other method to execute system commands in C program.
Also I need to store output of executed command in a variable.
system() function execute command and send output to stdout , is there any way to read from stdout and store in variable.
So my ultimate goal is to execute system command in C program for windows (using visual studio) and store output of that command in a variable.
Any suggestions ?
Standard C libraries give you only one way to execute external command in OS, so use int system(const char *command).
You can save output of this command to text file, and then read this file from you program.
For example:
#include <stdio.h>
#include <stdlib.h>
#define TMP_FILE_NAME "TMP_FOLDER_CONTENT.txt"
int main(int argc, char *argv[])
{
system("dir C:\* > "TMP_FILE_NAME);
FILE * fdir = fopen(TMP_FILE_NAME, "r");
char buff[100];
if (fdir)
{
while (1) {
if (fgets(buff, 100, fdir) == NULL) break;
printf("%s", buff);
}
}
fclose(fdir);
remove(TMP_FILE_NAME);
return 0;
}
Where dir is a program to be executed, C:\* - argument of the program, and > - redirection of standard output for that command after which filename TMP_FOLDER_CONTENT.txt will be substituted.
Also you can check returned value, as:
int errorcode = system("dir C:\* > "TMP_FILE_NAME);
printf("Command executed and returned a value %d\n", errorcode);
or taking into account command you use, change the logic of your program, e.g.:
int errorcode = system("dir C:\* > "TMP_FILE_NAME);
if( errorcode )
{
return errorcode;
}
UPDATE:
Alternatively, you could use pipes in C++, for example as shown in the answer to question How to execute a command and get output of command within C++ using POSIX?
you can do as #VolAnd said or also if you don't care about/don't want the output of the command to be in stdout and you also don't want anything else to be printed to stdout you can use freopen to set stdout to a file of your choice.

execvp(): no such file or directory?

For some reason, execvp() doesn't find the commands (like ls, pwd, etc.) in my PATH file, which includes /bin. Since I have a customized terminal alias with ls, I'm using pwd, etc. to test (as well as a fresh Linux machine), but I keep getting this for the output:
gcc main.c
./a.out
What would you like to do?
ls
arg[0]: ls
arg[1]: (null)
arg[2]: (null)
arg[3]: (null)
arg[4]: (null)
arg[5]: (null)
arg[6]: (null)
arg[7]: (null)
arg[8]: (null)
arg[9]: (null)
before exec
after exec
ERROR: No such file or directory
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
/*
Write a c program that runs a command line program with exactly one command line argument. It should work as follows:
Prompts the user for a program to run via the command line.
Reads in that line (using fgets) and parses it to be used by one of the exec functions.
Runs the command using exec
You do not need to fork off a separate process and wait, the purpose of this assignment is only to parse a single line of input and run a program once.
*/
int main() {
printf("\nWhat would you like to do?\n");
char* input = malloc( 100 ); //100 character string for input
fgets(input, 100, stdin); //reads from stdin (terminal input "file")
//parses command in input (max of 8 flags)
int number_of_args = 10;
char *args[number_of_args];
//puts cmd && flags in args
int i = 0;
for(; i < number_of_args; i++) {
args[i] = strsep( &input, " ");
printf("arg[%i]: %s\n", i, args[i]);
}
args[number_of_args - 1] = 0; //last element for execvp must be NULL;
// printf("nullify final index -> args[%i] = %s\n", number_of_args - 1, args[number_of_args -1]);
printf("before exec\n");
int e = execvp( args[0], args);
printf("after exec\n");
if(e < 0)
printf("ERROR: %s\n", strerror(errno));
return 0;
}
EDIT: Thought it'd be good to include my PATH as well:
echo $PATH
/usr/local/bin:/usr/local/bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
fgets() reads in the newline character if there's space available in the buffer. So when you input ls, it's actually ls\n. Obivously, execvp() can't find such a command and it fails. So the solution is to remove the trailing newline, if any.
char *p = strchr(input, '\n');
if (p) *p = 0;
You should also use argc for argument processing (if you read in the commands and arguments via main()'s argument) rather than assuming some fixed numbers. Or simply break the loop when strsep() returns NULL the first time. Technically, your code invokes undefined behaviour when you print all those null strings.

How can I pass the redirection operator '>' as an argument for execv?

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 ">".

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