First, let me describe what happens if you execute a program on a Unix platform, be it via system or otherwise. Usually, the application calls fork(), which creates a new process. The new process inherits everything: the memory, the set of open files, the signal handlers; it even starts executing in the same location the original process will continue: directly after the call to fork. It is an identical copy of the original process. The entire operation is called forking, and the original and new process are usually referred to as parent and child, respectively.
It is worth noting that most resources, such as memory areas usually are not shared: if one process modifies a variable, for instance, the other process will continue to see the old value.3.10 The same is true of per-process variables like the real and effective uid. If the child process changes either of them, this will not affect the parent process.
After forking, the child process will invoke the exec system call, giving it the file name of the program that should be invoked, and a list of arguments. Roughly speaking, this replaces the currently running program with the one specified in the exec call. At the same time, however, a bunch of things are inherited across exec calls, such as the set of open files, and the real and effective uid. Note that the saved uid is not inherited but overwritten with the effective uid.
Let's take a look at some code, for a change! Here is how crontab invokes the editor on the temporary file containing the list of cron jobs:
int
edit_jobs(const char *file)
{
char *editor *argv[3];
int pid;
if ((editor = getenv("EDITOR")) == NULL)
editor = "vi";
if ((pid = fork()) < 0) {
perror("fork");
return;
}
if (pid) {
/* Parent: wait for subprocess to complete */
wait4(pid, NULL, 0);
return;
}
/* Child process: drop privilege and invoke editor */
if (setuid(real_uid) < 0) {
perror("failed to drop privs");
exit(1);
}
argv[0] = editor;
argv[1] = file;
argv[2] = NULL;
/* XXX: More stuff here! */
execvp(argv[0], argv);
perror(argv[0]);
exit(1);
}