staticpid_t startChild(ChildStuff *c) { #if START_CHILD_USE_CLONE #define START_CHILD_CLONE_STACK_SIZE (64 * 1024) /* * See clone(2). * Instead of worrying about which direction the stack grows, just * allocate twice as much and start the stack in the middle. */ if ((c->clone_stack = malloc(2 * START_CHILD_CLONE_STACK_SIZE)) == NULL) /* errno will be set to ENOMEM */ return-1; return clone(childProcess, c->clone_stack + START_CHILD_CLONE_STACK_SIZE, CLONE_VFORK | CLONE_VM | SIGCHLD, c); #else #if START_CHILD_USE_VFORK /* * We separate the call to vfork into a separate function to make * very sure to keep stack of child from corrupting stack of parent, * as suggested by the scary gcc warning: * warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork' */ volatilepid_t resultPid = vfork(); #else /* * From Solaris fork(2): In Solaris 10, a call to fork() is * identical to a call to fork1(); only the calling thread is * replicated in the child process. This is the POSIX-specified * behavior for fork(). */ pid_t resultPid = fork(); #endif // 子进程执行 if (resultPid == 0) childProcess(c); assert(resultPid != 0); /* childProcess never returns */ return resultPid; #endif/* ! START_CHILD_USE_CLONE */ }
WhyCantJohnnyExec: /* We used to go to an awful lot of trouble to predict whether the * child would fail, but there is no reliable way to predict the * success of an operation without *trying* it, and there's no way * to try a chdir or exec in the parent. Instead, all we need is a * way to communicate any failure back to the parent. Easy; we just * send the errno back to the parent over a pipe in case of failure. * The tricky thing is, how do we communicate the *success* of exec? * We use FD_CLOEXEC together with the fact that a read() on a pipe * yields EOF when the write ends (we have two of them!) are closed. */ { interrnum= errno; restartableWrite(FAIL_FILENO, &errnum, sizeof(errnum)); } closeInChild(FAIL_FILENO); _exit(-1); return0; /* Suppress warning "no return value from function" */ }
if (strchr(file, '/') != NULL) { execve_with_shell_fallback(file, argv, envp); } else { /* We must search PATH (parent's, not child's) */ char expanded_file[PATH_MAX]; int filelen = strlen(file); int sticky_errno = 0; constchar * const * dirs; for (dirs = parentPathv; *dirs; dirs++) { constchar * dir = *dirs; int dirlen = strlen(dir); if (filelen + dirlen + 1 >= PATH_MAX) { errno = ENAMETOOLONG; continue; } memcpy(expanded_file, dir, dirlen); memcpy(expanded_file + dirlen, file, filelen); expanded_file[dirlen + filelen] = '\0'; execve_with_shell_fallback(expanded_file, argv, envp); /* There are 3 responses to various classes of errno: * return immediately, continue (especially for ENOENT), * or continue with "sticky" errno. * * From exec(3): * * If permission is denied for a file (the attempted * execve returned EACCES), these functions will continue * searching the rest of the search path. If no other * file is found, however, they will return with the * global variable errno set to EACCES. */ switch (errno) { case EACCES: sticky_errno = errno; /* FALLTHRU */ case ENOENT: case ENOTDIR: #ifdef ELOOP case ELOOP: #endif #ifdef ESTALE case ESTALE: #endif #ifdef ENODEV case ENODEV: #endif #ifdef ETIMEDOUT case ETIMEDOUT: #endif break; /* Try other directories in PATH */ default: return; } } if (sticky_errno != 0) errno = sticky_errno; } }