There are two basic ways to run another executable using C on linux: system()
and exec()
, although exec()
isn't really a function, it's a group of functions, execl()
, execv()
etc. They all have the same man page, so you can see the list via e.g. man execl
.
system()
is fine if what you want to do is launch another application and wait for it to finish, and you don't need to interact with or gather output from the other process.
If the context is that you don't want to wait for the other process to finish -- i.e., you want your program to go on running and RaspiStill to work in the background -- you need to combine exec()
and fork()
. The exec()
functions replace the current process with a new one. In other words, exec()
never returns; it's the last line of your code that will execute.
This is why you want to combine it with fork()
, which creates a child process. You then call exec()
in the child (note there is no main function here yet):
#include <stdio.h> // fprintf()
#include <unistd.h> // fork(), exec()
#include <string.h> // strerror()
#include <errno.h> // errno
#include <stdlib.h> // exit()
void forkAndExecute (const char *path, char *const args[]) {
int pid = fork();
if (pid == -1) {
fprintf ( stderr,
"fork() failed: %s",
strerror(errno)
);
return;
}
if (pid != 0) return;
// If pid == 0, this is the child process.
if (execvp(path, args) == -1) {
fprintf ( stderr,
"execvp(%s) failed: %s %s",
path, strerror(errno)
);
exit(-1);
}
}
If you haven't used fork()
before, you need to understand that because it literally forks the program into two, after fork()
you have TWO programs running, the parent and the child. In the parent program, fork()
will have returned the PID of the child process (if it returns -1, it failed, and there is no child; this shouldn't normally happen). In the child program, it will have returned 0. So you can use this to create two diverging branches of execution -- the parent does one thing while the child does another:
int main (int argc, const char *argv[]) {
char *const args[] = { "ls", "-l", "/" };
forkAndExecute("ls", args);
puts("Hello world");
return 0;
}
What will probably happen when you run this is something like:
me@homeĀ» ./a.out
Hello world
me@homeĀ» total 76
lrwxrwxrwx. 1 root root 7 May 22 2012 bin -> usr/bin
dr-xr-xr-x. 6 root root 4096 Sep 29 10:48 boot
drwxr-xr-x 18 root root 3300 Dec 3 14:26 dev
[...]
Notice "Hello world" appeared first, then the program seemed to end (which is why the prompt came back), then the output from ls
appeared. I said this is probably what will happen because it could have been the other way around -- those are two programs running simultaneously, so who does what first is indeterminate (but in this case the parent will probably be quicker to output).
Note it is generally not good to have the parent finish before a child does, if that is a concern see man 2 waitpid
.
Two things I want to point out about the use of exec()
;
The first arg to those functions is always the path to the executable. Since
ls
is probably in$PATH
, I just used"ls"
. The next arg (or in the case ofexecvp
, the first element in the 2nd arg array) should be the name of the executable. This is actually the same as the first arg in the arg array passed to a program'smain()
function -- it's always the name of the program executable itself. Most programs don't make any use of this, so it can probably be anything; I used"ls"
again:char *const args[] = { "ls", "-l", "/" }; forkAndExecute("ls", args);
So using, e.g., execl()
;
execl("ls", "ls", "-l", "/");
If the executable isn't in $PATH
, put the path in the first arg ("/bin/ls"
) but not in the second.
exec()
doens't return unless it fails, which can happen if it can't find the path you've specified. That's why there's anexit()
in the lastif
clause offorkAndExecute()
.
If you want to send data to the child process's standard input, or read from its output, you need to use fork()
, exec()
, and pipe()
to connect the child's file descriptors to something the parent can read from/write to.
BTW, general C questions are better off on Stack Overflow. fork()
and execute()
actually aren't standard C, they're POSIX (hence, unistd.h
), but any C programmer who didn't grow up in a cave is probably aware of them.