Goto sanos source index
//
// fork.c
//
// Process creation routines
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os.h>
#include <string.h>
#include <unistd.h>
#include <atomic.h>
#include <crtbase.h>
#include <sys/wait.h>
static int forkpid = 0;
//
// This implementation of vfork()/exec() has been inspired by Shaun
// Jackman's <sjackman@gmail.com> implementation for BusyBox. It uses
// setjmp()/longjmp() to emulate the behaviour of vfork()/exec().
//
static char **copyenv(char **env) {
int n;
char **newenv;
if (!env) return NULL;
for (n = 0; env[n]; n++);
newenv = (char **) malloc((n + 1) * sizeof(char *));
if (newenv) {
newenv[n] = NULL;
for (n = 0; env[n]; n++) newenv[n] = strdup(env[n]);
}
return newenv;
}
static void freeenv(char **env) {
int n;
if (!env) return;
for (n = 0; env[n]; n++) free(env[n]);
free(env);
}
static resume_fork(struct tib *tib, int pid) {
int i;
struct process *proc = tib->proc;
struct _forkctx *fc = (struct _forkctx *) tib->forkctx;
// Restore standard handles
for (i = 0; i < 3; i++) {
close(proc->iob[i]);
proc->iob[i] = fc->fd[i];
}
// Restore environment variables
freeenv(proc->env);
proc->env = fc->env;
// Unlink fork context from chain
tib->forkctx = fc->prev;
// Return control to the parent process path with pid handle
longjmp(fc->jmp, pid);
}
void fork_exit(int status) {
struct tib *tib = gettib();
// If no vfork() is in progress just return
struct _forkctx *fc = (struct _forkctx *) tib->forkctx;
if (!fc) return;
// Add a fake zombie for process
setchildstat(0x40000000 | fc->pid, status & 0xFF);
// Send a SIGCHLD to account for the virtual process exit
sendsig(self(), SIGCHLD);
// Return control to the parent process path with fork pid
resume_fork(tib, 0x40000000 | fc->pid);
}
struct _forkctx *_vfork(struct _forkctx *fc) {
int i;
struct tib *tib = gettib();
struct process *proc = tib->proc;
struct crtbase *crtbase = (struct crtbase *) proc->crtbase;
// Assign fork pid
fc->pid = atomic_increment(&forkpid);
// Install exit handler.
crtbase->fork_exit = fork_exit;
// Save and duplicate standard handles
for (i = 0; i < 3; i++) {
fc->fd[i] = proc->iob[i];
proc->iob[i] = dup(proc->iob[i]);
}
// Save and duplicate environment variables
fc->env = proc->env;
proc->env = copyenv(proc->env);
// Link fork context into chain
fc->prev = tib->forkctx;
tib->forkctx = fc;
return fc;
}
pid_t wait(int *stat_loc) {
struct process *proc = gettib()->proc;
while (1) {
int pid = getchildstat(-1, stat_loc);
if (pid != -1) return pid;
sigsuspend(NULL);
}
}
pid_t waitpid(pid_t pid, int *stat_loc, int options) {
struct process *proc = gettib()->proc;
int rc;
handle_t h;
// Use wait() if pid is -1 (TODO pid==0 meaning wait for any child in process group)
if (pid <= 0) {
if (options & WNOHANG) {
return getchildstat(-1, stat_loc);
} else {
return wait(stat_loc);
}
}
while (1) {
// Check for zombies
rc = getchildstat(pid, stat_loc);
if (rc != -1 || (options & WNOHANG)) return rc;
// Get handle for process from pid
h = getprochandle(pid);
if (h == NOHANDLE) {
errno = ECHILD;
return -1;
}
// Wait for main thread in process to terminate
rc = waitone(h, INFINITE);
close(h);
}
}
int execve(const char *path, char *argv[], char *env[]) {
struct tib *tib = gettib();
struct process *proc = tib->proc;
int pid;
handle_t child;
struct tib *ctib;
// If no vfork() is in progress return error
struct _forkctx *fc = (struct _forkctx *) tib->forkctx;
if (!fc) {
errno = EPERM;
return -1;
}
// Spawn new child process
child = spawnve(P_SUSPEND | P_CHILD, path, argv, env, &ctib);
if (child < 0) return -1;
pid = ctib->proc->id;
resume(child);
close(child);
// Return control to the parent process path with pid
resume_fork(tib, pid);
return 0;
}
int execv(const char *path, char *argv[]) {
return execve(path, argv, NULL);
}
int execl(const char *path, char *arg0, ...) {
return execve(path, &arg0, NULL);
}