/*
 * Copyright (c) 2011-2016, 2021-2024 Jonas 'Sortie' Termansen.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * process.cpp
 * A named collection of threads.
 */

#include <sys/ioctl.h>
#include <sys/wait.h>

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <msr.h>
#include <scram.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <sortix/clock.h>
#include <sortix/fcntl.h>
#include <sortix/fork.h>
#include <sortix/mman.h>
#include <sortix/resource.h>
#include <sortix/signal.h>
#include <sortix/stat.h>
#include <sortix/unistd.h>
#include <sortix/uthread.h>
#include <sortix/wait.h>

#include <sortix/kernel/addralloc.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/dtable.h>
#include <sortix/kernel/elf.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/mtable.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/ptable.h>
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/sortedlist.h>
#include <sortix/kernel/string.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h>
#include <sortix/kernel/time.h>
#include <sortix/kernel/worker.h>

#if defined(__i386__) || defined(__x86_64__)
#include "x86-family/float.h"
#include "x86-family/gdt.h"
#endif

namespace Sortix {

kthread_mutex_t process_family_lock = KTHREAD_MUTEX_INITIALIZER;

// The system is shutting down and creation of additional processes and threads
// should be prevented. Protected by process_family_lock.
Process::Process()
{
	program_image_path = NULL;
	addrspace = 0;
	pid = 0;

	nice_lock = KTHREAD_MUTEX_INITIALIZER;
	nice = 0;

	id_lock = KTHREAD_MUTEX_INITIALIZER;
	uid = euid = 0;
	gid = egid = 0;
	umask = 0022;

	ptr_lock = KTHREAD_MUTEX_INITIALIZER;
	// tty set to null reference in the member constructor.
	// root set to null reference in the member constructor.
	// cwd set to null reference in the member constructor.
	// mtable set to null reference in the member constructor.
	// dtable set to null reference in the member constructor.

	// ptable set to null reference in the member constructor.

	resource_limits_lock = KTHREAD_MUTEX_INITIALIZER;
	for ( size_t i = 0; i < RLIMIT_NUM_DECLARED; i++ )
	{
		resource_limits[i].rlim_cur = RLIM_INFINITY;
		resource_limits[i].rlim_max = RLIM_INFINITY;
	}

	signal_lock = KTHREAD_MUTEX_INITIALIZER;
	memset(&signal_actions, 0, sizeof(signal_actions));
	for ( int i = 0; i < SIG_MAX_NUM; i++ )
	{
		sigemptyset(&signal_actions[i].sa_mask);
		signal_actions[i].sa_handler = SIG_DFL;
		signal_actions[i].sa_cookie = NULL;
		signal_actions[i].sa_flags = 0;
	}
	sigemptyset(&signal_pending);
	sigreturn = NULL;

	parent = NULL;
	prev_sibling = NULL;
	next_sibling = NULL;
	first_child = NULL;
	zombie_child = NULL;
	group = NULL;
	group_prev = NULL;
	group_next = NULL;
	group_first = NULL;
	session = NULL;
	session_prev = NULL;
	session_next = NULL;
	session_first = NULL;
	init = NULL;
	init_prev = NULL;
	init_next = NULL;
	init_first = NULL;
	zombie_cond = KTHREAD_COND_INITIALIZER;
	is_zombie = false;
	no_zombify = false;
	limbo = false;
	is_init_exiting = false;
	exit_code = -1;

	first_thread = NULL;
	thread_lock = KTHREAD_MUTEX_INITIALIZER;
	threads_not_exiting_count = 0;
	threads_exiting = false;

	futex_lock = KTHREAD_MUTEX_INITIALIZER;
	futex_first_waiting = NULL;
	futex_last_waiting = NULL;

	segments = NULL;
	segments_used = 0;
	segments_length = 0;
	segment_write_lock = KTHREAD_MUTEX_INITIALIZER;
	segment_lock = KTHREAD_MUTEX_INITIALIZER;

	user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
	memset(&user_timers, 0, sizeof(user_timers));
	// alarm_timer initialized in member constructor.
	// execute_clock initialized in member constructor.
	// system_clock initialized in member constructor.
	// child_execute_clock initialized in member constructor.
	// child_system_clock initialized in member constructor.
	Time::InitializeProcessClocks(this);
	alarm_timer.Attach(Time::GetClock(CLOCK_MONOTONIC));
}

Process::~Process() // process_family_lock taken
{
	if ( alarm_timer.IsAttached() )
		alarm_timer.Detach();
	delete[] program_image_path;
	assert(!zombie_child);
	assert(!init);
	assert(!session);
	assert(!group);
	assert(!parent);
	assert(!init_first);
	assert(!session_first);
	assert(!group_first);
	assert(!first_child);
	assert(!addrspace);
	assert(!segments);
	assert(!dtable);
	assert(!mtable);
	assert(!cwd);
	assert(!root);
	assert(!threads_not_exiting_count);

	if ( ptable )
		ptable->Free(pid);
	ptable.Reset();
	tty.Reset();
}

void Process::BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtable)
{
	ScopedLock lock(&ptr_lock);
	assert(!this->dtable);
	assert(!this->mtable);
	this->dtable = dtable;
	this->mtable = mtable;
}

void Process::BootstrapDirectories(Ref<Descriptor> root)
{
	ScopedLock lock(&ptr_lock);
	assert(!this->root);
	assert(!this->cwd);
	this->root = root;
	this->cwd = root;
}

void Process::OnLastThreadExit()
{
	// Child processes can't be reparented away if we're init. The system is
	// about to shut down, so broadcast SIGKILL every process and wait for every
	// single process to exit. The operating system is finished when init has
	// exited.
	if ( init == this )
	{
		ScopedLock lock(&process_family_lock);
		// Forbid any more processes and threads from being created, so this
		// loop will always terminate.
		is_init_exiting = true;
		Process* process = first_child;
		while ( process )
		{
			if ( process->pid != 0 )
				process->DeliverSignal(SIGKILL);
			if ( process->init == process )
				process->is_init_exiting = true;
			if ( process->first_child )
				process = process->first_child;
			while ( process && process != this && !process->next_sibling )
				process = process->parent;
			if ( process == this )
				break;
			process = process->next_sibling;
		}
		// NotifyChildExit always signals zombie_cond for init when
		// is_init_exiting is true.
		while ( first_child )
			kthread_cond_wait(&zombie_cond, &process_family_lock);
	}
}

void Process::OnThreadDestruction(Thread* thread)
{
	assert(thread->process == this);
	kthread_mutex_lock(&thread_lock);
	if ( thread->prev_sibling )
		thread->prev_sibling->next_sibling = thread->next_sibling;
	if ( thread->next_sibling )
		thread->next_sibling->prev_sibling = thread->prev_sibling;
	if ( thread == first_thread )
		first_thread = thread->next_sibling;
	if ( first_thread )
		first_thread->prev_sibling = NULL;
	thread->prev_sibling = thread->next_sibling = NULL;
	bool threadsleft = first_thread;
	kthread_mutex_unlock(&thread_lock);

	// We are called from the threads destructor, let it finish before we
	// we handle the situation by killing ourselves.
	if ( !threadsleft )
		ScheduleDeath();
}

void Process__AfterLastThreadExit(void* user)
{
	return ((Process*) user)->AfterLastThreadExit();
}

void Process::ScheduleDeath()
{
	// All our threads must have exited at this point.
	assert(!first_thread);
	Worker::Schedule(Process__AfterLastThreadExit, this);
}

// Useful for killing a partially constructed process without waiting for
// it to die and garbage collect its zombie. It is not safe to access this
// process after this call as another thread may garbage collect it.
void Process::AbortConstruction()
{
	no_zombify = true;
	ScheduleDeath();
}

void Process::AfterLastThreadExit()
{
	LastPrayer();
}

void Process::DeleteTimers()
{
	for ( timer_t i = 0; i < PROCESS_TIMER_NUM_MAX; i++ )
	{
		if ( user_timers[i].timer.IsAttached() )
		{
			user_timers[i].timer.Cancel();
			user_timers[i].timer.Detach();
		}
	}
}

// This function runs in a worker thread and cannot block on another worker
// thread, or it may deadlock.
void Process::LastPrayer()
{
	assert(this);
	// This must never be called twice.
	assert(!is_zombie);

	// This must be called from a thread using another address space as the
	// address space of this process is about to be destroyed.
	Thread* curthread = CurrentThread();
	assert(curthread->process != this);

	// This can't be called if the process is still alive.
	assert(!first_thread);

	// Disarm and detach all the timers in the process.
	DeleteTimers();
	if ( alarm_timer.IsAttached() )
	{
		alarm_timer.Cancel();
		alarm_timer.Detach();
	}

	// We need to temporarily reload the correct addrese space of the dying
	// process such that we can unmap and free its memory.
	addr_t prevaddrspace = Memory::SwitchAddressSpace(addrspace);

	ResetAddressSpace();

	// tty is kept alive in session leader until no longer in limbo.
	if ( dtable ) dtable.Reset();
	if ( cwd ) cwd.Reset();
	if ( root ) root.Reset();
	if ( mtable ) mtable.Reset();

	// Destroy the address space and safely switch to the replacement
	// address space before things get dangerous.
	Memory::DestroyAddressSpace(prevaddrspace);
	addrspace = 0;

	ScopedLock family_lock(&process_family_lock);

	is_zombie = true;

	// Init is nice and will gladly raise our orphaned children and zombies.
	// Child processes can't be reparented away if we're init. OnLastThreadExit
	// must have already killed all the child processes and prevented more from
	// being created.
	assert(init);
	if ( init == this )
	{
		assert(is_init_exiting);
		assert(!first_child);
	}

	while ( first_child )
	{
		Process* process = first_child;
		first_child = process->next_sibling;
		process->parent = init;
		process->prev_sibling = NULL;
		process->next_sibling = init->first_child;
		if ( init->first_child )
			init->first_child->prev_sibling = process;
		init->first_child = process;
		process->no_zombify = true;
	}
	// Since we have no more children (they are with init now), we don't
	// have to worry about new zombie processes showing up, so just collect
	// those that are left. Then we satisfiy the invariant !zombie_child that
	// applies on process termination.
	while ( zombie_child )
	{
		Process* zombie = zombie_child;
		zombie_child = zombie->next_sibling;
		zombie->next_sibling = NULL;
		if ( zombie_child )
			zombie_child->prev_sibling = NULL;
		zombie->no_zombify = true;
		zombie->WaitedFor();
	}
	// Remove ourself from our process group.
	if ( group )
		group->GroupRemoveMember(this);
	// Remove ourself from our session.
	if ( session )
		session->SessionRemoveMember(this);
	// Remove ourself from our init.
	if ( init )
		init->InitRemoveMember(this);

	bool zombify = !no_zombify;

	// This class instance will be destroyed by our parent process when it
	// has received and acknowledged our death.
	if ( parent )
		parent->NotifyChildExit(this, zombify);

	// If nobody is waiting for us, then simply commit suicide.
	if ( !zombify )
		WaitedFor();
}

void Process::WaitedFor() // process_family_lock taken
{
	parent = NULL;
	limbo = false;
	if ( group_first || session_first || init_first )
		limbo = true;
	if ( !limbo )
		delete this;
}

void Process::ResetAddressSpace()
{
	ScopedLock lock1(&segment_write_lock);
	ScopedLock lock2(&segment_lock);

	assert(Memory::GetAddressSpace() == addrspace);

	for ( size_t i = 0; i < segments_used; i++ )
		Memory::UnmapRange(segments[i].addr, segments[i].size, PAGE_USAGE_USER_SPACE);

	Memory::Flush();

	segments_used = segments_length = 0;
	free(segments);
	segments = NULL;
}

void Process::GroupRemoveMember(Process* child) // process_family_lock taken
{
	assert(child->group == this);
	if ( child->group_prev )
		child->group_prev->group_next = child->group_next;
	else
		group_first = child->group_next;
	if ( child->group_next )
		child->group_next->group_prev = child->group_prev;
	child->group = NULL;
	if ( IsLimboDone() )
		delete this;
}

void Process::SessionRemoveMember(Process* child) // process_family_lock taken
{
	assert(child->session == this);
	if ( child->session_prev )
		child->session_prev->session_next = child->session_next;
	else
		session_first = child->session_next;
	if ( child->session_next )
		child->session_next->session_prev = child->session_prev;
	child->session = NULL;
	if ( !session_first )
	{
		// Remove reference to tty when session is empty.
		kthread_mutex_lock(&ptr_lock);
		Ref<Descriptor> session_tty = tty;
		kthread_mutex_unlock(&ptr_lock);
		kthread_mutex_unlock(&process_family_lock);
		ioctx_t ctx; SetupKernelIOCtx(&ctx);
		if ( session_tty )
			session_tty->ioctl(&ctx, TIOCUCTTY, 1);
		kthread_mutex_lock(&process_family_lock);
	}
	if ( IsLimboDone() )
		delete this;
}

void Process::InitRemoveMember(Process* child) // process_family_lock taken
{
	assert(child->init == this);
	if ( child->init_prev )
		child->init_prev->init_next = child->init_next;
	else
		init_first = child->init_next;
	if ( child->init_next )
		child->init_next->init_prev = child->init_prev;
	child->init = NULL;
	if ( IsLimboDone() )
		delete this;
}

bool Process::IsLimboDone() // process_family_lock taken
{
	return limbo && !group_first && !session_first && !init_first;
}

// process_family_lock taken
void Process::NotifyChildExit(Process* child, bool zombify)
{
	if ( child->prev_sibling )
		child->prev_sibling->next_sibling = child->next_sibling;
	if ( child->next_sibling )
		child->next_sibling->prev_sibling = child->prev_sibling;
	if ( first_child == child )
		first_child = child->next_sibling;
	if ( first_child )
		first_child->prev_sibling = NULL;

	if ( zombify )
	{
		if ( zombie_child )
			zombie_child->prev_sibling = child;
		child->prev_sibling = NULL;
		child->next_sibling = zombie_child;
		zombie_child = child;
	}

	// Notify this parent process about the child exiting if it's meant to
	// become a zombie process. Additionally, always notify init about children
	// when init is exiting, because OnLastThreadExit needs to be able to catch
	// every child exiting.
	DeliverSignal(SIGCHLD);
	if ( zombify || is_init_exiting )
		kthread_cond_broadcast(&zombie_cond);
}

pid_t Process::Wait(pid_t thepid, int* status_ptr, int options)
{
	// TODO: Process groups are not supported yet.
	if ( thepid < -1 || thepid == 0 )
		return errno = ENOSYS, -1;

	ScopedLock lock(&process_family_lock);

	// A process can only wait if it has children.
	if ( !first_child && !zombie_child )
		return errno = ECHILD, -1;

	// Processes can only wait for their own children to exit.
	if ( 0 < thepid )
	{
		// TODO: This is a slow but multithread safe way to verify that the
		// target process has the correct parent.
		bool found = false;
		for ( Process* p = first_child; !found && p; p = p->next_sibling )
			if ( p->pid == thepid && !p->no_zombify )
				found = true;
		for ( Process* p = zombie_child; !found && p; p = p->next_sibling )
			if ( p->pid == thepid && !p->no_zombify )
				found = true;
		if ( !found )
			return errno = ECHILD, -1;
	}

	Process* zombie = NULL;
	while ( !zombie )
	{
		for ( zombie = zombie_child; zombie; zombie = zombie->next_sibling )
			if ( (thepid == -1 || thepid == zombie->pid) && !zombie->no_zombify )
				break;
		if ( zombie )
			break;
		if ( options & WNOHANG )
			return 0;
		if ( !kthread_cond_wait_signal(&zombie_cond, &process_family_lock) )
			return errno = EINTR, -1;
	}

	// Remove from the list of zombies.
	if ( zombie->prev_sibling )
		zombie->prev_sibling->next_sibling = zombie->next_sibling;
	if ( zombie->next_sibling )
		zombie->next_sibling->prev_sibling = zombie->prev_sibling;
	if ( zombie_child == zombie )
		zombie_child = zombie->next_sibling;
	if ( zombie_child )
		zombie_child->prev_sibling = NULL;

	thepid = zombie->pid;

	// It is safe to access these clocks directly as the child process is no
	// longer running at this point and the values are nicely frozen.
	child_execute_clock.Advance(zombie->child_execute_clock.current_time);
	child_system_clock.Advance(zombie->child_system_clock.current_time);

	int status = zombie->exit_code;
	if ( status < 0 )
		status = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGKILL, SIGKILL);

	zombie->WaitedFor();

	if ( status_ptr )
		*status_ptr = status;

	return thepid;
}

pid_t sys_waitpid(pid_t pid, int* user_status, int options)
{
	int status = 0;
	pid_t ret = CurrentProcess()->Wait(pid, &status, options);
	if ( 0 < ret &&
	     user_status &&
	     !CopyToUser(user_status, &status, sizeof(status)) )
		return -1;
	return ret;
}

void Process::ExitThroughSignal(int signal)
{
	ExitWithCode(WCONSTRUCT(WNATURE_SIGNALED, 128 + signal, signal));
}

void Process::ExitWithCode(int requested_exit_code)
{
	ScopedLock lock(&thread_lock);
	if ( exit_code == -1 )
		exit_code = requested_exit_code;

	// Broadcast SIGKILL to all our threads which will begin our long path
	// of process termination. We simply can't stop the threads as they may
	// be running in kernel mode doing dangerous stuff. This thread will be
	// destroyed by SIGKILL once the system call returns.
	for ( Thread* t = first_thread; t; t = t->next_sibling )
		t->DeliverSignal(SIGKILL);
}

Ref<MountTable> Process::GetMTable()
{
	ScopedLock lock(&ptr_lock);
	assert(mtable);
	return mtable;
}

Ref<DescriptorTable> Process::GetDTable()
{
	ScopedLock lock(&ptr_lock);
	assert(dtable);
	return dtable;
}

Ref<ProcessTable> Process::GetPTable()
{
	ScopedLock lock(&ptr_lock);
	assert(ptable);
	return ptable;
}

Ref<Descriptor> Process::GetTTY()
{
	ScopedLock lock(&ptr_lock);
	return tty;
}

Ref<Descriptor> Process::GetRoot()
{
	ScopedLock lock(&ptr_lock);
	assert(root);
	return root;
}

Ref<Descriptor> Process::GetCWD()
{
	ScopedLock lock(&ptr_lock);
	assert(cwd);
	return cwd;
}

void Process::SetTTY(Ref<Descriptor> newtty)
{
	ScopedLock lock(&ptr_lock);
	tty = newtty;
}

void Process::SetRoot(Ref<Descriptor> newroot)
{
	ScopedLock lock(&ptr_lock);
	assert(newroot);
	root = newroot;
}

void Process::SetCWD(Ref<Descriptor> newcwd)
{
	ScopedLock lock(&ptr_lock);
	assert(newcwd);
	cwd = newcwd;
}

Ref<Descriptor> Process::GetDescriptor(int fd)
{
	ScopedLock lock(&ptr_lock);
	assert(dtable);
	return dtable->Get(fd);
}

Process* Process::Fork()
{
	assert(CurrentProcess() == this);

	Process* clone = new Process;
	if ( !clone )
		return NULL;

	struct segment* clone_segments = NULL;

	// Fork the segment list.
	if ( segments )
	{
		size_t segments_size = sizeof(struct segment) * segments_used;
		if ( !(clone_segments = (struct segment*) malloc(segments_size)) )
		{
			delete clone;
			return NULL;
		}
		memcpy(clone_segments, segments, segments_size);
	}

	// Fork address-space here and copy memory.
	clone->addrspace = Memory::Fork();
	if ( !clone->addrspace )
	{
		free(clone_segments);
		delete clone;
		return NULL;
	}

	// Now it's too late to clean up here, if anything goes wrong, we simply
	// ask the process to commit suicide before it goes live.
	clone->segments = clone_segments;
	clone->segments_used = segments_used;
	clone->segments_length = segments_used;

	kthread_mutex_lock(&process_family_lock);

	// Forbid the creation of new processes if init has exited.
	if ( init->is_init_exiting )
	{
		kthread_mutex_unlock(&process_family_lock);
		clone->AbortConstruction();
		return NULL;
	}

	// Register the new process by assigning a pid.
	if ( (clone->pid = (clone->ptable = ptable)->Allocate(clone)) < 0 )
	{
		kthread_mutex_unlock(&process_family_lock);
		clone->AbortConstruction();
		return NULL;
	}

	// Remember the relation to the child process.
	clone->parent = this;
	clone->next_sibling = first_child;
	clone->prev_sibling = NULL;
	if ( first_child )
		first_child->prev_sibling = clone;
	first_child = clone;

	// Add the new process to the current process group.
	clone->group = group;
	clone->group_prev = NULL;
	if ( (clone->group_next = group->group_first) )
		group->group_first->group_prev = clone;
	group->group_first = clone;

	// Add the new process to the current session.
	clone->session = session;
	clone->session_prev = NULL;
	if ( (clone->session_next = session->session_first) )
		session->session_first->session_prev = clone;
	session->session_first = clone;

	// Add the new process to the current init.
	clone->init = init;
	clone->init_prev = NULL;
	if ( (clone->init_next = init->init_first) )
		init->init_first->init_prev = clone;
	init->init_first = clone;

	kthread_mutex_unlock(&process_family_lock);

	// Initialize everything that is safe and can't fail.
	kthread_mutex_lock(&resource_limits_lock);
	for ( size_t i = 0; i < RLIMIT_NUM_DECLARED; i++ )
		clone->resource_limits[i] = resource_limits[i];
	kthread_mutex_unlock(&resource_limits_lock);

	kthread_mutex_lock(&nice_lock);
	clone->nice = nice;
	kthread_mutex_unlock(&nice_lock);

	kthread_mutex_lock(&ptr_lock);
	clone->root = root;
	clone->cwd = cwd;
	kthread_mutex_unlock(&ptr_lock);

	kthread_mutex_lock(&id_lock);
	clone->uid = uid;
	clone->gid = gid;
	clone->euid = euid;
	clone->egid = egid;
	clone->umask = umask;
	kthread_mutex_unlock(&id_lock);

	kthread_mutex_lock(&signal_lock);
	memcpy(&clone->signal_actions, &signal_actions, sizeof(signal_actions));
	sigemptyset(&clone->signal_pending);
	clone->sigreturn = sigreturn;
	kthread_mutex_unlock(&signal_lock);

	// Initialize things that can fail and abort if needed.
	bool failure = false;

	kthread_mutex_lock(&ptr_lock);
	if ( !(clone->dtable = dtable->Fork()) )
		failure = true;
	//if ( !(clone->mtable = mtable->Fork()) )
	//	failure = true;
	clone->mtable = mtable;
	kthread_mutex_unlock(&ptr_lock);

	if ( !(clone->program_image_path = String::Clone(program_image_path)) )
		failure = true;

	// If the proces creation failed, ask the process to commit suicide and
	// not become a zombie, as we don't wait for it to exit. It will clean
	// up all the above resources and delete itself.
	if ( failure )
	{
		clone->AbortConstruction();
		return NULL;
	}

	return clone;
}

void Process::ResetForExecute()
{
	DeleteTimers();

	for ( int i = 0; i < SIG_MAX_NUM; i++ )
	{
		signal_actions[i].sa_flags = 0;
		if ( signal_actions[i].sa_handler == SIG_DFL )
			continue;
		if ( signal_actions[i].sa_handler == SIG_IGN )
			continue;
		signal_actions[i].sa_handler = SIG_DFL;
	}

	sigreturn = NULL;
	stack_t* signal_stack = &CurrentThread()->signal_stack;
	memset(signal_stack, 0, sizeof(*signal_stack));
	signal_stack->ss_flags = SS_DISABLE;

	ResetAddressSpace();
}

bool Process::MapSegment(struct segment* result, void* hint, size_t size,
                         int flags, int prot)
{
	// process->segment_write_lock is held at this point.
	// process->segment_lock is held at this point.

	if ( !size )
		size = 1;

	if ( !PlaceSegment(result, this, hint, size, flags) )
		return false;
	if ( !Memory::MapMemory(this, result->addr, result->size, result->prot = prot) )
	{
		// The caller is expected to self-destruct in this case, so the
		// segment just created is not removed.
		return false;
	}
	Memory::Flush();
	return true;
}

int Process::Execute(const char* program_name, Ref<Descriptor> program,
                     int argc, const char* const* argv,
                     int envc, const char* const* envp,
                     struct thread_registers* regs)
{
	assert(argc != INT_MAX);
	assert(envc != INT_MAX);
	assert(CurrentProcess() == this);

	if ( argc == 0 )
		return errno = EINVAL, -1;

	char* program_name_clone = String::Clone(program_name);
	if ( !program_name_clone )
		return -1;

	ELF::Auxiliary aux;

	addr_t entry = ELF::Load(program, &aux);
	if ( !entry ) { delete[] program_name_clone; return -1; }

	delete[] program_image_path;
	program_image_path = program_name_clone;
	program_name_clone = NULL;

	uintptr_t userspace_addr;
	size_t userspace_size;
	Memory::GetUserVirtualArea(&userspace_addr, &userspace_size);

	const size_t stack_size = 512UL * 1024UL;
	void* stack_hint = (void*) (userspace_addr + userspace_size - stack_size);
	const int stack_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK;

	if ( !aux.tls_mem_align )
		aux.tls_mem_align = 1;
	if ( Page::Size() < aux.tls_mem_align )
		return errno = EINVAL, -1;
	if ( !aux.uthread_align )
		aux.uthread_align = 1;
	if ( Page::Size() < aux.uthread_align )
		return errno = EINVAL, -1;
	if ( aux.uthread_size < sizeof(struct uthread) )
		aux.uthread_size = sizeof(struct uthread);

	size_t raw_tls_size = aux.tls_mem_size;
	size_t raw_tls_size_aligned = -(-raw_tls_size & ~(aux.tls_mem_align-1));
	if ( raw_tls_size && raw_tls_size_aligned == 0 /* overflow */ )
		return errno = EINVAL, -1;
	int raw_tls_kprot = PROT_WRITE | PROT_KWRITE | PROT_FORK;
	int raw_tls_prot = PROT_READ | PROT_KREAD | PROT_FORK;
	void* raw_tls_hint = stack_hint;

	size_t tls_size = raw_tls_size_aligned + aux.uthread_size;
	size_t tls_offset_tls = 0;
	size_t tls_offset_uthread = raw_tls_size_aligned;
	if ( aux.tls_mem_align < aux.uthread_align )
	{
		size_t more_aligned = -(-raw_tls_size_aligned & ~(aux.uthread_align-1));
		if ( raw_tls_size_aligned && more_aligned == 0 /* overflow */ )
			return errno = EINVAL, -1;
		size_t difference = more_aligned - raw_tls_size_aligned;
		tls_size += difference;
		tls_offset_tls += difference;
		tls_offset_uthread += difference;
	}
	assert((tls_offset_tls & (aux.tls_mem_align-1)) == 0);
	assert((tls_offset_uthread & (aux.uthread_align-1)) == 0);
	int tls_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK;
	void* tls_hint = stack_hint;

	size_t auxcode_size = Page::Size();
	int auxcode_kprot = PROT_KWRITE | PROT_FORK;
	int auxcode_prot = PROT_EXEC | PROT_READ | PROT_KREAD | PROT_FORK;
	void* auxcode_hint = stack_hint;

	size_t arg_size = 0;

	size_t argv_size = sizeof(char*) * (argc + 1);
	size_t envp_size = sizeof(char*) * (envc + 1);

	arg_size += argv_size;
	arg_size += envp_size;

	for ( int i = 0; i < argc; i++ )
		arg_size += strlen(argv[i]) + 1;
	for ( int i = 0; i < envc; i++ )
		arg_size += strlen(envp[i]) + 1;

	struct segment arg_segment;
	struct segment stack_segment;
	struct segment raw_tls_segment;
	struct segment tls_segment;
	struct segment auxcode_segment;

	kthread_mutex_lock(&segment_write_lock);
	kthread_mutex_lock(&segment_lock);

	if ( !(MapSegment(&arg_segment, stack_hint, arg_size, 0, stack_prot) &&
	       MapSegment(&stack_segment, stack_hint, stack_size, 0, stack_prot) &&
	       MapSegment(&raw_tls_segment, raw_tls_hint, raw_tls_size, 0, raw_tls_kprot) &&
	       MapSegment(&tls_segment, tls_hint, tls_size, 0, tls_prot) &&
	       MapSegment(&auxcode_segment, auxcode_hint, auxcode_size, 0, auxcode_kprot)) )
	{
		kthread_mutex_unlock(&segment_lock);
		kthread_mutex_unlock(&segment_write_lock);
		ResetForExecute();
		return errno = ENOMEM, -1;
	}

	char** target_argv = (char**) ((char*) arg_segment.addr + 0);
	char** target_envp = (char**) ((char*) arg_segment.addr + argv_size);
	char* target_strings = (char*) ((char*) arg_segment.addr + argv_size + envp_size);
	size_t target_strings_offset = 0;

	for ( int i = 0; i < argc; i++ )
	{
		const char* arg = argv[i];
		size_t arg_len = strlen(arg);
		char* target_arg = target_strings + target_strings_offset;
		strcpy(target_arg, arg);
		target_argv[i] = target_arg;
		target_strings_offset += arg_len + 1;
	}
	target_argv[argc] = (char*) NULL;

	for ( int i = 0; i < envc; i++ )
	{
		const char* env = envp[i];
		size_t env_len = strlen(env);
		char* target_env = target_strings + target_strings_offset;
		strcpy(target_env, env);
		target_envp[i] = target_env;
		target_strings_offset += env_len + 1;
	}
	target_envp[envc] = (char*) NULL;

	static const uintmax_t off_max = OFF_MAX; // Silence false -Wtype-limits
	if ( (uintmax_t) off_max < aux.tls_file_offset ||
	     OFF_MAX - aux.tls_file_offset < aux.tls_file_size )
		return errno = EINVAL, -1;
	uint8_t* target_raw_tls = (uint8_t*) raw_tls_segment.addr;
	ioctx_t user_ctx; SetupUserIOCtx(&user_ctx);
	kthread_mutex_unlock(&segment_lock);
	for ( size_t done = 0; done < aux.tls_file_size; )
	{
		ssize_t amount =
			program->pread(&user_ctx, target_raw_tls + done,
			               aux.tls_file_size - done,
			               (off_t) aux.tls_file_offset + done);
		if ( amount < 0 )
			return -1;
		if ( !amount )
			return errno = EINVAL, -1;
		done += amount;
	}
	kthread_mutex_lock(&segment_lock);
	memset(target_raw_tls + aux.tls_file_size, 0, aux.tls_mem_size - aux.tls_file_size);
	Memory::ProtectMemory(this, raw_tls_segment.addr, raw_tls_segment.size, raw_tls_prot);

	uint8_t* target_tls = (uint8_t*) (tls_segment.addr + tls_offset_tls);
	assert((((uintptr_t) target_tls) & (aux.tls_mem_align-1)) == 0);
	memcpy(target_tls, target_raw_tls, aux.tls_file_size);
	memset(target_tls + aux.tls_file_size, 0, aux.tls_mem_size - aux.tls_file_size);

	struct uthread* uthread = (struct uthread*) (tls_segment.addr + tls_offset_uthread);
	assert((((uintptr_t) uthread) & (aux.uthread_align-1)) == 0);
	memset(uthread, 0, sizeof(*uthread));
	uthread->uthread_pointer = uthread;
	uthread->uthread_size = aux.uthread_size;
	uthread->uthread_flags = UTHREAD_FLAG_INITIAL;
	uthread->tls_master_mmap = (void*) raw_tls_segment.addr;
	uthread->tls_master_size = aux.tls_mem_size;
	uthread->tls_master_align = aux.tls_mem_align;
	uthread->tls_mmap = (void*) tls_segment.addr;
	uthread->tls_size = tls_size;
	uthread->stack_mmap = (void*) stack_segment.addr;
	uthread->stack_size = stack_segment.size;
	uthread->arg_mmap = (void*) arg_segment.addr;
	uthread->arg_size = arg_segment.size;
	memset(uthread + 1, 0, aux.uthread_size - sizeof(struct uthread));

	memset(regs, 0, sizeof(*regs));
#if defined(__i386__)
	regs->eax = argc;
	regs->ebx = (size_t) target_argv;
	regs->edx = envc;
	regs->ecx = (size_t) target_envp;
	regs->eip = entry;
	regs->esp = (stack_segment.addr + stack_segment.size) & ~15UL;
	regs->ebp = regs->esp;
	regs->cs = UCS | URPL;
	regs->ds = UDS | URPL;
	regs->ss = UDS | URPL;
	regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
	regs->signal_pending = 0;
	regs->gsbase = (uint32_t) uthread;
	regs->cr3 = addrspace;
	regs->kernel_stack = GDT::GetKernelStack();
	memcpy(regs->fpuenv, Float::fpu_initialized_regs, 512);
#elif defined(__x86_64__)
	regs->rdi = argc;
	regs->rsi = (size_t) target_argv;
	regs->rdx = envc;
	regs->rcx = (size_t) target_envp;
	regs->rip = entry;
	regs->rsp = (stack_segment.addr + stack_segment.size) & ~15UL;
	regs->rbp = regs->rsp;
	regs->cs = UCS | URPL;
	regs->ds = UDS | URPL;
	regs->ss = UDS | URPL;
	regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
	regs->signal_pending = 0;
	regs->fsbase = (uint64_t) uthread;
	regs->cr3 = addrspace;
	regs->kernel_stack = GDT::GetKernelStack();
	memcpy(regs->fpuenv, Float::fpu_initialized_regs, 512);
#else
#warning "You need to implement initializing the first thread after execute"
#endif

	uint8_t* auxcode = (uint8_t*) auxcode_segment.addr;
#if defined(__i386__)
	sigreturn = (void (*)(void)) &auxcode[0];
	auxcode[0] = 0xCD; /* int .... */
	auxcode[1] = 0x83; /* ... $131 */
#elif defined(__x86_64__)
	sigreturn = (void (*)(void)) &auxcode[0];
	auxcode[0] = 0xCD; /* int .... */
	auxcode[1] = 0x83; /* ... $131 */
#else
	(void) auxcode;
	#warning "You need to initialize auxcode with a sigreturn routine"
#endif
	Memory::ProtectMemory(this, auxcode_segment.addr, auxcode_segment.size, auxcode_prot);

	kthread_mutex_unlock(&segment_lock);
	kthread_mutex_unlock(&segment_write_lock);

	dtable->OnExecute();

	return 0;
}

static
const char* shebang_lookup_environment(const char* name, char* const* envp)
{
	size_t equalpos = strcspn(name, "=");
	if ( name[equalpos] == '=' )
		return NULL;
	size_t namelen = equalpos;
	for ( size_t i = 0; envp[i]; i++ )
	{
		if ( strncmp(name, envp[i], namelen) )
			continue;
		if ( envp[i][namelen] != '=' )
			continue;
		return envp[i] + namelen + 1;
	}
	return NULL;
}

static char* shebang_tokenize(char** saved)
{
	char* data = *saved;
	if ( !data )
		return *saved = NULL;
	while ( data[0] && isspace((unsigned char) data[0]) )
		data++;
	if ( !data[0] )
		return *saved = NULL;
	size_t input = 0;
	size_t output = 0;
	bool singly = false;
	bool doubly = false;
	bool escaped = false;
	for ( ; data[input]; input++ )
	{
		char c = data[input];
		if ( !escaped && !singly && !doubly && isspace((unsigned char) c) )
			break;
		if ( !escaped && !doubly && c == '\'' )
		{
			singly = !singly;
			continue;
		}
		if ( !escaped && !singly && c == '"' )
		{
			doubly = !doubly;
			continue;
		}
		if ( !singly && !escaped && c == '\\' )
		{
			escaped = true;
			continue;
		}
		if ( escaped )
		{
			switch ( c )
			{
			case 'a': c = '\a'; break;
			case 'b': c = '\b'; break;
			case 'e': c = '\e'; break;
			case 'f': c = '\f'; break;
			case 'n': c = '\n'; break;
			case 'r': c = '\r'; break;
			case 't': c = '\t'; break;
			case 'v': c = '\v'; break;
			default: break;
			};
		}
		escaped = false;
		data[output++] = c;
	}
	if ( data[input] )
		*saved = data + input + 1;
	else
		*saved = NULL;
	data[output] = '\0';
	return data;
}

static size_t shebang_count_arguments(char* line)
{
	size_t result = 0;
	while ( shebang_tokenize(&line) )
		result++;
	return result;
}

// NOTE: The PATH-searching logic is repeated multiple places. Until this logic
//       can be shared somehow, you need to keep this comment in sync as well
//       as the logic in these files:
//         * kernel/process.cpp
//         * libc/unistd/execvpe.c
//         * utils/which.c
// NOTE: See comments in execvpe() for algorithmic commentary.

static bool sys_execve_alloc(addralloc_t* alloc, size_t size)
{
	if ( !AllocateKernelAddress(alloc, size) )
		return false;
	if ( !Memory::MapRange(alloc->from, alloc->size, PROT_KREAD | PROT_KWRITE, PAGE_USAGE_EXECVE) )
		return FreeKernelAddress(alloc), false;
	Memory::Flush();
	return true;
}

static void sys_execve_free(addralloc_t* alloc)
{
	Memory::UnmapRange(alloc->from, alloc->size, PAGE_USAGE_EXECVE);
	Memory::Flush();
	FreeKernelAddress(alloc);
}

static
int sys_execve_kernel(const char* filename,
                      int argc,
                      char* const* argv,
                      int envc,
                      char* const* envp,
                      struct thread_registers* regs)
{
	Process* process = CurrentProcess();

	ioctx_t ctx;
	SetupKernelIOCtx(&ctx);
	Ref<Descriptor> from = filename[0] == '/' ? process->GetRoot() : process->GetCWD();
	Ref<Descriptor> desc = from->open(&ctx, filename, O_EXEC | O_READ, 0);
	if ( !desc )
		return -1;
	from.Reset();

	struct stat st;
	if ( desc->stat(&ctx, &st) )
		return -1;
	if ( !(st.st_mode & 0111) )
		return errno = EACCES, -1;

	if ( !process->Execute(filename, desc, argc, argv, envc, envp, regs) )
		return 0;
	else if ( errno != ENOEXEC )
		return -1;

	if ( st.st_size < 0 )
		return errno = EINVAL, -1;
	if ( (uintmax_t) SIZE_MAX < (uintmax_t) st.st_size )
		return errno = EFBIG, -1;

	size_t filesize = (size_t) st.st_size;

	addralloc_t buffer_alloc;
	if ( !sys_execve_alloc(&buffer_alloc, filesize) )
		return -1;

	uint8_t* buffer = (uint8_t*) buffer_alloc.from;
	for ( size_t sofar = 0; sofar < filesize; )
	{
		ssize_t amount = desc->read(&ctx, buffer + sofar, filesize - sofar);
		if ( amount < 0 )
			return sys_execve_free(&buffer_alloc), -1;
		if ( amount == 0 )
			return sys_execve_free(&buffer_alloc), errno = EEOF, -1;
		sofar += amount;
	}

	desc.Reset();

	if ( filesize < 2 || buffer[0] != '#' || buffer[1] != '!' )
		return sys_execve_free(&buffer_alloc), -1;

	size_t line_length = 0;
	while ( 2 + line_length < filesize && buffer[2 + line_length] != '\n' )
		line_length++;
	if ( line_length == filesize )
		return sys_execve_free(&buffer_alloc), errno = ENOEXEC, -1;

	char* line = new char[line_length+1];
	if ( !line )
		return sys_execve_free(&buffer_alloc), -1;
	memcpy(line, buffer + 2, line_length);
	line[line_length] = '\0';
	sys_execve_free(&buffer_alloc);

	char* line_clone = String::Clone(line);
	if ( !line_clone )
		return delete[] line, -1;
	size_t argument_count = shebang_count_arguments(line_clone);
	delete[] line_clone;

	if ( !argument_count || INT_MAX < argument_count )
		return delete[] line, errno = ENOEXEC, -1;

	int sb_argc = (int) argument_count;
	char** sb_argv = new char*[sb_argc];
	if ( !sb_argv )
		return delete[] line, -1;

	char* sb_saved = line;
	for ( int i = 0; i < sb_argc; i++ )
		sb_argv[i] = shebang_tokenize(&sb_saved);

	if ( INT_MAX - argc <= sb_argc )
		return delete[] sb_argv, delete[] line, errno = EOVERFLOW, -1;

	if ( !sb_argv[0] || !sb_argv[0][0] )
		return delete[] sb_argv, delete[] line, errno = ENOENT, -1;

	int new_argc = sb_argc + argc;
	char** new_argv = new char*[new_argc + 1];
	if ( !new_argv )
		return delete[] sb_argv, delete[] line, -1;

	for ( int i = 0; i < sb_argc; i++ )
		new_argv[i] = sb_argv[i];
	new_argv[sb_argc + 0] = (char*) filename;
	for ( int i = 1; i < argc; i++ )
		new_argv[sb_argc + i] = argv[i];
	new_argv[new_argc] = (char*) NULL;

	int result = -1;

	// (See the above comment block before editing this searching logic)
	const char* path = shebang_lookup_environment("PATH", envp);
	bool search_path = !strchr(sb_argv[0], '/') && path;
	bool any_tries = false;
	bool any_eacces = false;

	const char* new_argv0 = sb_argv[0];
	while ( search_path && *path )
	{
		size_t len = strcspn(path, ":");
		if ( !len )
		{
			path++;
			continue;
		}

		any_tries = true;

		char* dirpath = strndup(path, len);
		if ( !dirpath )
			return -1;
		if ( (path += len)[0] == ':' )
			path++;
		while ( len && dirpath[len - 1] == '/' )
			dirpath[--len] = '\0';

		char* fullpath;
		if ( asprintf(&fullpath, "%s/%s", dirpath, sb_argv[0]) < 0 )
			return free(dirpath), -1;

		result = sys_execve_kernel(fullpath, new_argc, new_argv, envc, envp, regs);

		free(fullpath);
		free(dirpath);

		if ( result == 0 )
			break;

		if ( errno == ENOENT )
			continue;

		if ( errno == ELOOP ||
		     errno == EISDIR ||
		     errno == ENAMETOOLONG ||
		     errno == ENOTDIR )
			continue;

		if ( errno == EACCES )
		{
			any_eacces = true;
			continue;
		}

		if ( errno == EACCES )
		{
			any_eacces = true;
			continue;
		}

		break;
	}

	if ( !any_tries )
		result = sys_execve_kernel(new_argv0, new_argc, new_argv, envc, envp, regs);

	if ( result < 0 && any_eacces )
		errno = EACCES;

	delete[] new_argv;
	delete[] sb_argv;
	delete[] line;

	return result;
}

int sys_execve(const char* user_filename,
               char* const* user_argv,
               char* const* user_envp)
{
	char* filename;
	int argc;
	int envc;
	char** argv;
	char** envp;
	int result = -1;
	struct thread_registers regs;
	memset(&regs, 0, sizeof(regs));

	if ( !user_filename || !user_argv || !user_envp )
		return errno = EFAULT, -1;

	if ( !(filename = GetStringFromUser(user_filename)) )
		goto cleanup_done;

	argc = 0;
	while ( true )
	{
		const char* user_arg;
		if ( !CopyFromUser(&user_arg, user_argv + argc, sizeof(user_arg)) )
			goto cleanup_filename;
		if ( !user_arg )
			break;
		if ( ++argc == INT_MAX )
		{
			errno = E2BIG;
			goto cleanup_filename;
		}
	}

	argv = (char**) calloc(argc+1, sizeof(char*));
	if ( !argv )
		goto cleanup_filename;

	for ( int i = 0; i < argc; i++ )
	{
		const char* user_arg;
		if ( !CopyFromUser(&user_arg, user_argv + i, sizeof(user_arg)) )
			goto cleanup_argv;
		if ( !(argv[i] = GetStringFromUser(user_arg)) )
			goto cleanup_argv;
	}

	envc = 0;
	while ( true )
	{
		const char* user_env;
		if ( !CopyFromUser(&user_env, user_envp + envc, sizeof(user_env)) )
			goto cleanup_argv;
		if ( !user_env )
			break;
		if ( ++envc == INT_MAX  )
		{
			errno = E2BIG;
			goto cleanup_argv;
		}
	}

	envp = (char**) calloc(envc+1, sizeof(char*));
	if ( !envp )
		goto cleanup_argv;

	for ( int i = 0; i < envc; i++ )
	{
		const char* user_env;
		if ( !CopyFromUser(&user_env, user_envp + i, sizeof(user_env)) )
			goto cleanup_envp;
		if ( !(envp[i] = GetStringFromUser(user_envp[i])) )
			goto cleanup_envp;
	}

	result = sys_execve_kernel(filename, argc, argv, envc, envp, &regs);

cleanup_envp:
	for ( int i = 0; i < envc; i++)
		delete[] envp[i];
	free(envp);
cleanup_argv:
	for ( int i = 0; i < argc; i++)
		delete[] argv[i];
	free(argv);
cleanup_filename:
	delete[] filename;
cleanup_done:
	if ( result == 0 )
		LoadRegisters(&regs);
	return result;
}

pid_t sys_tfork(int flags, struct tfork* user_regs)
{
	struct tfork regs;
	if ( !CopyFromUser(&regs, user_regs, sizeof(regs)) )
		return -1;

	if ( Signal::IsPending() )
		return errno = EINTR, -1;

	bool making_process = flags == SFFORK;
	bool making_thread = (flags & (SFPROC | SFPID | SFFD | SFMEM | SFCWD | SFROOT)) == SFPROC;

	// TODO: Properly support tfork(2).
	if ( !(making_thread || making_process) )
		return errno = ENOSYS, -1;

	if ( regs.altstack.ss_flags & ~__SS_SUPPORTED_FLAGS )
		return errno = EINVAL, -1;

	size_t stack_alignment = 16;

	// TODO: Is it a hack to create a new kernel stack here?
	Thread* curthread = CurrentThread();
	size_t newkernel_stack_size = curthread->kernel_stack_size;
	uint8_t* newkernelstack = new uint8_t[newkernel_stack_size + stack_alignment];
	if ( !newkernelstack )
		return -1;

	uintptr_t stack_aligned = (uintptr_t) newkernelstack;
	size_t stack_aligned_size = newkernel_stack_size;

	if ( ((uintptr_t) stack_aligned) & (stack_alignment-1) )
		stack_aligned = (stack_aligned + 16) & ~(stack_alignment-1);
	stack_aligned_size &= 0xFFFFFFF0;

	Process* parent_process = CurrentProcess();
	Process* child_process;
	if ( making_thread )
		child_process = parent_process;
	else if ( !(child_process = parent_process->Fork()) )
		return delete[] newkernelstack, -1;

	struct thread_registers cpuregs;
	memset(&cpuregs, 0, sizeof(cpuregs));
#if defined(__i386__)
	cpuregs.eip = regs.eip;
	cpuregs.esp = regs.esp;
	cpuregs.eax = regs.eax;
	cpuregs.ebx = regs.ebx;
	cpuregs.ecx = regs.ecx;
	cpuregs.edx = regs.edx;
	cpuregs.edi = regs.edi;
	cpuregs.esi = regs.esi;
	cpuregs.ebp = regs.ebp;
	cpuregs.cs = UCS | URPL;
	cpuregs.ds = UDS | URPL;
	cpuregs.ss = UDS | URPL;
	cpuregs.eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
	cpuregs.fsbase = regs.fsbase;
	cpuregs.gsbase = regs.gsbase;
	cpuregs.cr3 = child_process->addrspace;
	cpuregs.kernel_stack = stack_aligned + stack_aligned_size;
	memcpy(&cpuregs.fpuenv, Float::fpu_initialized_regs, 512);
#elif defined(__x86_64__)
	cpuregs.rip = regs.rip;
	cpuregs.rsp = regs.rsp;
	cpuregs.rax = regs.rax;
	cpuregs.rbx = regs.rbx;
	cpuregs.rcx = regs.rcx;
	cpuregs.rdx = regs.rdx;
	cpuregs.rdi = regs.rdi;
	cpuregs.rsi = regs.rsi;
	cpuregs.rbp = regs.rbp;
	cpuregs.r8  = regs.r8;
	cpuregs.r9  = regs.r9;
	cpuregs.r10 = regs.r10;
	cpuregs.r11 = regs.r11;
	cpuregs.r12 = regs.r12;
	cpuregs.r13 = regs.r13;
	cpuregs.r14 = regs.r14;
	cpuregs.r15 = regs.r15;
	cpuregs.cs = UCS | URPL;
	cpuregs.ds = UDS | URPL;
	cpuregs.ss = UDS | URPL;
	cpuregs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
	cpuregs.fsbase = regs.fsbase;
	cpuregs.gsbase = regs.gsbase;
	cpuregs.cr3 = child_process->addrspace;
	cpuregs.kernel_stack = stack_aligned + stack_aligned_size;
	memcpy(&cpuregs.fpuenv, Float::fpu_initialized_regs, 512);
#else
#warning "You need to implement initializing the registers of the new thread"
#endif

	// Forbid the creation of new threads if init has exited.
	ScopedLock process_family_lock_lock(&process_family_lock);
	if ( child_process->init->is_init_exiting )
		return errno = EPERM, -1;

	// If the thread could not be created, make the process commit suicide
	// in a manner such that we don't wait for its zombie.
	Thread* thread = CreateKernelThread(child_process, &cpuregs,
	                                    making_process ? "main" : "second");
	process_family_lock_lock.Reset();
	if ( !thread )
	{
		if ( making_process )
			child_process->AbortConstruction();
		return -1;
	}

	thread->kernel_stack_pos = (addr_t) newkernelstack;
	thread->kernel_stack_size = newkernel_stack_size;
	thread->kernel_stack_malloced = true;
	memcpy(&thread->signal_mask, &regs.sigmask, sizeof(sigset_t));
	memcpy(&thread->signal_stack, &regs.altstack, sizeof(stack_t));

	StartKernelThread(thread);

	return child_process->pid;
}

pid_t sys_getpid(void)
{
	return CurrentProcess()->pid;
}

pid_t sys_getppid(void)
{
	Process* process = CurrentProcess();
	ScopedLock lock(&process_family_lock);
	if ( !process->parent )
		return 0;
	return process->parent->pid;
}

pid_t sys_getpgid(pid_t pid)
{
	ScopedLock lock(&process_family_lock);
	Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
	if ( !process )
		return errno = ESRCH, -1;
	if ( !process->group )
		return errno = ESRCH, -1;
	return process->group->pid;
}

pid_t sys_getsid(pid_t pid)
{
	ScopedLock lock(&process_family_lock);
	Process* process =
		!pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
	if ( !process )
		return errno = ESRCH, -1;
	if ( !process->session )
		return errno = ESRCH, -1;
	return process->session->pid;
}

pid_t sys_getinit(pid_t pid)
{
	ScopedLock lock(&process_family_lock);
	Process* process =
		!pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
	if ( !process->init )
		return errno = ESRCH, -1;
	return process->init->pid;
}

int sys_setpgid(pid_t pid, pid_t pgid)
{
	if ( pid < 0 || pgid < 0 )
		return errno = EINVAL, -1;

	// TODO: Either prevent changing the process group after an exec or provide
	//       a version of this system call with a flags parameter that lets you
	//       decide if you want this behavior. This will fix a race condition
	//       where the shell spawns a child and both parent and child sets the
	//       process group, but the child sets the process group and execve's
	//       and the new program image exploits this 'bug' and also changes the
	//       process group, and then the shell gets around to change the process
	//       group. This probably unlikely, but correctness over all!

	Process* current_process = CurrentProcess();

	ScopedLock lock(&process_family_lock);

	// Find the processes in question.
	Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid);
	if ( !process )
		return errno = ESRCH, -1;
	Process* group = !pgid ? process : CurrentProcess()->GetPTable()->Get(pgid);
	if ( !group )
		return errno = ESRCH, -1;

	// The process must be this one or a direct child.
	if ( process != current_process && process->parent != current_process )
		return errno = EPERM, -1;
	// The process must be in this session.
	if ( process->session != current_process->session )
		return errno = EPERM, -1;
	// The new group must be in the same session as the process.
	if ( group->session != process->session )
		return errno = EPERM, -1;
	// The process must not be a process group leader.
	// TODO: Maybe POSIX actually allows this.
	if ( process->group_first )
		return errno = EPERM, -1;
	// The process must not be a session leader.
	if ( process->session_first )
		return errno = EPERM, -1;
	// The group must either exist or be the process itself.
	if ( !group->group_first && group != process )
		return errno = EPERM, -1;

	// Exit early if this is a noop.
	if ( process->group == group )
		return 0;

	// Remove the process from its current process group.
	if ( process->group )
		process->group->GroupRemoveMember(process);

	// Insert the process into its new process group.
	process->group_prev = NULL;
	process->group_next = group->group_first;
	if ( group->group_first )
		group->group_first->group_prev = process;
	group->group_first = process;
	process->group = group;

	return 0;
}

pid_t sys_setsid(void)
{
	Process* process = CurrentProcess();

	ScopedLock lock(&process_family_lock);

	// Test if already a process group leader.
	if ( process->group == process )
		return errno = EPERM, -1;

	// Remove the process from its current process group.
	if ( process->group )
		process->group->GroupRemoveMember(process);

	// Remove the process from its current session.
	if ( process->session )
		process->session->SessionRemoveMember(process);

	// Insert the process into its new session.
	process->session_prev = NULL;
	process->session_next = NULL;
	process->session_first = process;
	process->session = process;

	// Insert the process into its new process group.
	process->group_prev = NULL;
	process->group_next = NULL;
	process->group_first = process;
	process->group = process;

	return process->pid;
}

int sys_setinit(void)
{
	Process* process = CurrentProcess();

	ScopedLock lock(&process_family_lock);

	// Test if already a process group leader.
	if ( process->group == process )
		return errno = EPERM, -1;

	// Remove the process from its current process group.
	if ( process->group )
		process->group->GroupRemoveMember(process);

	// Remove the process from its current session.
	if ( process->session )
		process->session->SessionRemoveMember(process);

	// Remove the process from its current init.
	if ( process->init )
		process->init->InitRemoveMember(process);

	// Insert the process into its new init.
	process->init_prev = NULL;
	process->init_next = NULL;
	process->init_first = process;
	process->init = process;

	// Insert the process into its new session.
	process->session_prev = NULL;
	process->session_next = NULL;
	process->session_first = process;
	process->session = process;

	// Insert the process into its new process group.
	process->group_prev = NULL;
	process->group_next = NULL;
	process->group_first = process;
	process->group = process;

	return process->pid;
}

size_t sys_getpagesize(void)
{
	return Page::Size();
}

mode_t sys_umask(mode_t newmask)
{
	Process* process = CurrentProcess();
	ScopedLock lock(&process->id_lock);
	mode_t oldmask = process->umask;
	process->umask = newmask & 0666;
	return oldmask;
}

mode_t sys_getumask(void)
{
	Process* process = CurrentProcess();
	ScopedLock lock(&process->id_lock);
	return process->umask;
}

static void GetAssertInfo(struct scram_assert* info,
                          const void* user_info_ptr)
{
	memset(info, 0, sizeof(*info));
	struct scram_assert user_info;
	if ( !CopyFromUser(&user_info, user_info_ptr, sizeof(user_info)) )
		return;
	info->filename = GetStringFromUser(user_info.filename);
	info->line = user_info.line;
	info->function = GetStringFromUser(user_info.function);
	info->expression = GetStringFromUser(user_info.expression);
}

static void FreeAssertInfo(struct scram_assert* info)
{
	delete[] (char*) info->filename;
	delete[] (char*) info->function;
	delete[] (char*) info->expression;
}

static
void GetUndefinedBehaviorInfo(struct scram_undefined_behavior* info,
                              const void* user_info_ptr)
{
	memset(info, 0, sizeof(*info));
	struct scram_undefined_behavior user_info;
	if ( !CopyFromUser(&user_info, user_info_ptr, sizeof(user_info)) )
		return;
	info->filename = GetStringFromUser(user_info.filename);
	info->line = user_info.line;
	info->column = user_info.column;
	info->violation = GetStringFromUser(user_info.violation);
}

static void FreeUndefinedBehaviorInfo(struct scram_undefined_behavior* info)
{
	delete[] info->filename;
	delete[] info->violation;
}

__attribute__((noreturn))
void sys_scram(int event, const void* user_info)
{
	Process* process = CurrentProcess();
	// TODO: Prohibit execve such that program_image_path is protected.
	process->ExitThroughSignal(SIGABRT);
	if ( event == SCRAM_ASSERT )
	{
		struct scram_assert info;
		GetAssertInfo(&info, user_info);
		Log::PrintF("%s[%ji]: Assertion failure: %s:%lu: %s: %s\n",
			process->program_image_path,
			(intmax_t) process->pid,
			info.filename ? info.filename : "<unknown>",
			info.line,
			info.function ? info.function : "<unknown>",
			info.expression ? info.expression : "<unknown>");
		FreeAssertInfo(&info);
	}
	else if ( event == SCRAM_STACK_SMASH )
	{
		Log::PrintF("%s[%ji]: Stack smashing detected\n",
			process->program_image_path,
			(intmax_t) process->pid);
	}
	else if ( event == SCRAM_UNDEFINED_BEHAVIOR )
	{
		struct scram_undefined_behavior info;
		GetUndefinedBehaviorInfo(&info, user_info);
		Log::PrintF("%s[%ji]: Undefined behavior: %s at %s:%lu:%lu\n",
			process->program_image_path,
			(intmax_t) process->pid,
			info.violation ? info.violation : "<unknown>",
			info.filename ? info.filename : "<unknown>",
			info.line,
			info.column);
		FreeUndefinedBehaviorInfo(&info);
	}
	else
	{
		Log::PrintF("%s[%ji]: Unknown scram event %i\n",
			process->program_image_path,
			(intmax_t) process->pid,
			event);
	}
	// TODO: Allow debugging this event (and see signal.cpp sigreturn).
	kthread_exit();
}

} // namespace Sortix
