On Unicies, or anywhere else you have execve
and it works like the man page specifies, you can just...kill me for using atoi
, because it's generally awful, except for this sort of case.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char** argv) {
(void) argc;
printf("arg: %s\n", argv[1]);
int count = atoi(argv[1]);
if ( getchar() == 'y' ) {
++count;
char buf[20];
sprintf(buf, "%d", count);
char* newargv[3];
newargv[0] = argv[0];
newargv[1] = buf;
newargv[2] = NULL;
execve(argv[0], newargv, NULL);
}
return count;
}
Example:
$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n
7 | $
(7 was the return code).
It neither recurses nor explicitly loops -- instead, it just calls itself, replacing its own memory space with a new version of itself.
In this way, the stack will never overflow, though all previous variables will be redeclared, just like with any reinvocation -- the getchar
call prevents 100% CPU utilisation.
In the case of a self-updating binary, since the entire binary (at least, on Unix-likes, I don't know about Windows) will be copied into memory at runtime, then if the file changes on disk before the execve(argv[0], ...
call, the new binary found on disk, not the same old one, will be run instead.
As @CarstenS and @bishop point out in the comments, due to the unique way in which Unix was designed, open file descriptors are kept across fork
/exec
, and as a result in order to avoid leaking open file descriptors across calls to execve
, you should either close them before execve
or open them with e
, FD_CLOEXEC
/ O_CLOEXEC
in the first place -- more information can be found on Dan Walsh's blog.