How do calls to the execvp system call work? - c

I was doing a research into the contents of another StackOverflow question and I thought it was a good time to brush up my knowledge of unix system calls.
While experimenting with execvp (WITHOUT fork on purpose) I ran into something that confuses me
I wrote 4 test programs
Program 1
#include <stdio.h>
int main() {
//printf("Doge\n");
execvp("ls");
printf("Foo\n");
return 0;
}
The program works as expected, the contents of the directory are printed and the Foo print statement is not
Program 2
However when I uncomment the first print statement and have the program be this
#include <stdio.h>
int main() {
printf("Doge\n");
execvp("ls");
printf("Foo\n");
return 0;
}
execvp returns a -1 and both print statements are issued. why?
Program 3
I vaguely remember having to use unistd.h when experimenting with unix system calls from college.
So I included it, but not execvp has a different signature and it needed some more args than just the name of the program. So I did this
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Doge\n");
char *const parmList[] = {"ls", NULL};
execvp("ls", parmList);
printf("Foo\n");
return 0;
}
And this works. This has confused me. Why did exec work in the first program?
I also used This as a reference to the system calls.
Finally I wrote
Program 4
#include <stdio.h>
//#include <unistd.h>
int main() {
printf("Doge\n");
char *const parmList[] = {"ls", NULL};
execvp("ls", parmList);
printf("Foo\n");
return 0;
}
Which also works as expected.
Can someone explain what's going on?

With this snippet
#include <stdio.h>
int main() {
execvp("ls");
printf("Foo\n");
return 0;
}
you're invoking undefined behaviour. You're not providing the prototype for execvp which requires an argument list (null terminated) as a second parameter.
Using gcc without any warning option silently uses execvp as implicitly declared, and doesn't check parameters. It just calls the function. The function then looks for a second parameter and encounters... whatever is left of the call stack (or registers, depending on call conventions), that's why a previous printf call can change the behaviour.
Using gcc -Wall gives the following warning:
test.c:5:9: warning: implicit declaration of function 'execvp' [-Wimplicit-function-declaration]
execvp("ls");
Including the proper include (#include <unistd.h>) leads to:
test.c:6:9: error: too few arguments to function 'execvp'
execvp("ls");
^~~~~~
That's why you've got strange behaviour. Don't look further. Use execvp with 2 arguments, period. In your case "Program 3" is the way to go, and always set warning level to the maximum, if possible (gcc and clang: -Wall -Wextra -pedantic -Werror)

Related

Standard functions in C99 being called "Implicit declarations"

I have a C program that I am running on my MacOS terminal. All command line tools and GCC compiler have been installed. However for using functions like getpid() or execv() it gives the following error:
execv-test.c:7:35: error: implicit declaration of function 'getpid' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
printf("Pid before execv: %d\n", getpid());
^
execv-test.c:8:2: error: implicit declaration of function 'execv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
execv("print",NULL);
^
2 errors generated.
The code:
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
printf("The game is never over, John. But there may be some new players now.\n");
printf("Pid before execv: %d\n", getpid());
execv("print",NULL);
printf("Returned from execv call.\n");
return 0;
}
The following Stack Overflow exchange suggested that I write helper functions for the ones that were taken as implicit declarations. However, I am not sure you could do the same with getpid() or execv(). What should I do to make sure this doesn't happen?
PLEASE NOTE: "print" is just another helper file that is supposed to be run once execv() is called.
Note that you are using system calls that are defined in the unistd.h header file. Therefore calling them without including the std library #include <unistd.h> amounts to "implicit declaration" = "calling a function without defining it first".

Executing a command with execvpe in C

For a project, I'm supposed to pipe the output of a command to my C program (called execute), which will then execute that command.
For example, running this:
echo ls -lR /usr | ./execute, will take the output (ls -lR /usr) and pass it into my C program which will then execute ls -lR /usr.
According to the directions, I'm supposed to use execvpe() to do the actual execution of the program, however I can't find any documentation that makes sense, nor can I get it to work without getting these errors:
execute.c: In function ‘main’:
execute.c:98: warning: implicit declaration of function ‘getenv’
execute.c:98: warning: assignment makes pointer from integer without a cast
execute.c:106: warning: implicit declaration of function ‘execvpe’
My professor said that I have to #include <unistd.h>, and <stdio.h> which I did, parse the input to my program (which I did), and then do this:
int main(void) {
char *path;
path = getenv("PATH");
char *envp[] = {path, NULL};
// the initialized array below could change normally.
// below is just an example
char *tests = {"ls", "-lR", NULL};
int ret = execvpe("ls", tests, envp);
if(ret == -1) { printf("error\n"); }
return 0;
}
He then stated that execvpe should find the path correctly and execute everything. But no matter what I keep getting these warnings. Running the program and ignoring the warnings immediately seg faults. Does anyone know how execvpe works or how I can fix this?
This code should work, assuming your system has execvpe() at all:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void) {
char *path = getenv("PATH");
char pathenv[strlen(path) + sizeof("PATH=")];
sprintf(pathenv, "PATH=%s", path);
char *envp[] = {pathenv, NULL};
char *tests[] = {"ls", "-lR", NULL};
execvpe(tests[0], tests, envp);
fprintf(stderr, "failed to execute \"%s\"\n", tests[0]);
return 1;
}
Updated to format PATH=$PATH in the environment.
It fixes the compilation error on tests, uses tests[0] as the command name to execvpe(); it reports the error on standard error; it includes the name of the command that was not executed; it returns a failure status (non-zero) when exiting; it notes that execvpe() only returns if it fails so it isn't necessary to test what its return status is. It does not include the system error message in the error message, but you could modify the code to include <errno.h> and <string.h> and use errno and strerror() to report that information too.

How to use System(const char*) in TC++

Today , When i coding, met a question..my Code as follow:
#include<stdlib.h>
void main()
{
system("dir");
getch();
}
The question : The user Screen is nothing..Why ? where is my result?
If you want the output when using system, at least into something you can read in your application, you need to pipe the output:
system("dir > /tmp/output.txt");
FILE *f = fopen("/tmp/output.txt", "r");
char text[1024]; // max sizeof of 1 kb, any more and I'd consider using `malloc()` instead.
fread(text, 1, 1024, f);
printf("%s\n", text);
fclose(f);
There are some problems in your program, at least one of which has already been mentioned.
void main() should be int main(void).
As I recall, the Windows/DOS getch function is declared in <conio.h>; you should have a #include directive for it. Be aware that both <conio.h> and getch are non-standard.
Since main returns int, you should return an int result.
But none of these problems explain the problem you're seeing.
With these changes:
#include <stdlib.h>
#include <conio.h>
int main(void)
{
system("dir");
getch();
return 0;
}
This should work; it should show a directory listing of whatever directory your program runs in (which is determined by TC; I don't know the details).
It's possible that the program is running in an empty directory, which means the dir command wouldn't show any files, but it should still produce some output.
Try commenting out the system() call and adding a printf call (note the added #include <stdio.h>):
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main(void)
{
printf("Hello, world\n");
getch();
return 0;
}
This should open a console window, print "Hello, world" in it, and wait for you to type Enter.
If you still don't see any output (either no console window, or a console window with nothing in it), then you have a problem that's not related to the system() call. Most likely the problem has to do with the way you're using Turbo C (I presume that's what "TC" stands for).
The main function in every C program is supposed to return an int you are returning void
Change void to int:
#include<stdlib.h>
int main()
{
system("dir");
getch();
}
When I tested, the dir command ran in my console and printed to standard out.
May be he is the running the program directly in the Turbo C IDE and hence his output is not visible. If he runs the program directly from cmd line it works. I remember you need to run Alt - F5 or some other combination to see the output window in Turbo C++

Magic in C with malloc, fork and open

I have such a funny problem I thought I'd share with you.
I cornered it down to the most little program I could :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int cmd_left(char *name)
{
pid_t pid;
int f_d;
if ((pid = fork()) == -1)
{
perror("");
exit(1);
}
f_d = open(name);
printf("%d\n", f_d);
close(f_d);
}
int main(int ac, char **av, char **env)
{
char **dummy_env;
if (ac < 2)
return (0);
dummy_env = malloc(10);
cmd_left(av[1]);
}
Basically, if I remove the malloc, opening works just fine.
You just have to compile and give the program a (valid) file to see the magic.
open(2) takes at least two parameters. Since you are passing it only one argument, you are invoking Undefined Behavior. In this case, open() is just using some garbage as second argument.
You need #include <fcntl.h> to get a declaration for open() in scope, which would then tell you that you are not calling it with enough arguments:
int open(const char *filename, int flags, ...);
(The optional argument - singular - is the permissions for the file (mode_t perms) if you have O_CREAT amongst the options in the flags argument.)
The call to malloc() scribbles over enough stack to remove the zeroes on it initially, which leaves the 'extra arguments' to open() in a state where they are not zero and you run into problems.
Undefined behaviour - which you're invoking - can lead to any weird result.
Make sure you compile with at least 'gcc -Wall' and I recommend 'gcc -Wmissing-prototypes -Wstrict-prototypes -Wall -Wextra'.
The header file for open is missing and open expects at least a second parameter.
If you fix that it should be OK.

Use of exit() function

I want to know how and when can I use the exit() function like the program in my book:
#include<stdio.h>
void main()
{
int goals;
printf("enter number of goals scored");
scanf("%d",&goals);
if(goals<=5)
goto sos;
else
{
printf("hehe");
exit( );
}
sos:
printf("to err is human");
}
When I run it, it shows ERROR: call to undefined function exit().
Also, I want to know how I can create an option to close the window in which the program runs? For example, I made a menu-driven program which had several options and one of them was "exit the menu". How can I make this exit the program (i.e. close the window)?
Try using exit(0); instead. The exit function expects an integer parameter. And don't forget to #include <stdlib.h>.
The exit function is declared in the stdlib header, so you need to have
#include <stdlib.h>
at the top of your program to be able to use exit.
Note also that exit takes an integer argument, so you can't call it like exit(), you have to call as exit(0) or exit(42). 0 usually means your program completed successfully, and nonzero values are used as error codes.
There are also predefined macros EXIT_SUCCESS and EXIT_FAILURE, e.g. exit(EXIT_SUCCESS);
exit(int code); is declared in stdlib.h so you need an
#include <stdlib.h>
Also:
- You have no parameter for the exit(), it requires an int so provide one.
- Burn this book, it uses goto which is (for everyone but linux kernel hackers) bad, very, very, VERY bad.
Edit:
Oh, and
void main()
is bad, too, it's:
int main(int argc, char *argv[])
Try man exit.
Oh, and:
#include <stdlib.h>
int main(void) {
/* ... */
if (error_occured) {
return (EXIT_FAILURE);
}
/* ... */
return (EXIT_SUCCESS);
}
The exit() function is a type of function with a return type without an argument. It's defined by the stdlib header file.
You need to use ( exit(0) or exit(EXIT_SUCCESS)) or (exit(non-zero) or exit(EXIT_FAILURE) ).
The following example shows the usage of the exit() function.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("Start of the program....\n");
printf("Exiting the program....\n");
exit(0);
printf("End of the program....\n");
return 0;
}
Output
Start of the program....
Exiting the program....
You must add a line with #include <stdlib.h> to include that header file
and exit must return a value so assign some integer in exit(any_integer).
In addition to return an exit code to parent process -
In UNIX, an important aspect that I think has been left out is, that exit() at first calls (in reverse order) all those functions, which were registered by atexit() call.
Please refer to SUSv4 for details.
on unix like operating systems exit belongs to group of system calls. system calls are special calls which enable user code (your code) to call kernel code. so exit call makes some OS specific clean-up actions before returning control to OS, it terminates the program.
#include <stdlib.h>
// example 1
int main(int argc, char *argv){
exit(EXIT_SUCCESS);
}
// example 2
int main(int argc, char *argv){
return 0;
}
Some compilers will give you the same opcode from both of these examples but some won't. For example opcode from first function will not include any kind of stack positioning opcode which will be included in the second example like for any other function. You could compile both examples and disassemble them and you will see the difference.
You can use exit from any part of your code and be sure that process terminates. Don't forget to include integer parameter.
Write header file #include<process.h> and replace exit(); with exit(0);. This will definitely work in Turbo C; for other compilers I don't know.
Bad programming practice. Using a goto function is a complete no no in C programming.
Also include header file stdlib.h by writing #include <iostream.h>for using exit() function. Also remember that exit() function takes an integer argument . Use exit(0) if the program completed successfully and exit(-1) or exit function with any non zero value as the argument if the program has error.
Include stdlib.h in your header, and then call abort(); in any place you want to exit your program. Like this:
switch(varName)
{
case 1:
blah blah;
case 2:
blah blah;
case 3:
abort();
}
When the user enters the switch accepts this and give it to the case 3 where you call the abort function. It will exit your screen immediately after hitting enter key.
Use process.h instead of stdlib and iostream... It will work 100%.

Resources