/************************************************************
* MultiUser - MultiUser Task/File Support System				*
* ---------------------------------------------------------	*
* Task Management Routines												*
* ---------------------------------------------------------	*
* © Copyright 1993-1994 Geert Uytterhoeven						*
* All Rights Reserved.													*
************************************************************/


#include <exec/execbase.h>
#include <exec/alerts.h>
#include <intuition/intuitionbase.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>

#include "Memory.h"
#include "Task.h"
#include "Config.h"
#include "Misc.h"
#include "Locale.h"
#include "LibHeader.h"
#include "Monitor.h"
#include "UserInfo.h"


	/*
	 *		Static Routines
	 */

static struct muTaskLevel *AllocTaskLevel(struct muExtOwner *owner);
static void FreeTaskLevel(struct muTaskLevel *level);
static struct muTaskNode *AllocTaskNode(struct Task *task, ULONG defprotection);
static void FreeTaskNode(struct muTaskNode *node);
static struct muTaskNode *FindTaskNode(struct Task *task);
static void AddTaskLevel(struct muTaskLevel *level, struct muTaskLevel *dest);
static void RemTaskLevel(struct muTaskLevel *level);
static void MoveTaskLevel(struct muTaskLevel *level, struct muTaskLevel *dest);
static void MoveAll(struct muTaskLevel *source, struct muTaskLevel *dest);
static void AddTaskNode(struct muTaskNode *node, struct muTaskLevel *dest);
static void RemTaskNode(struct muTaskNode *node);
static void MoveTaskNode(struct muTaskNode *node, struct muTaskLevel *dest);
static void CleanUpTaskLevel(struct muTaskLevel *level);
static void __saveds CleanUpBody(void);


	/*
	 *		Extended Owner Information Structure for root
	 */

struct muExtOwner RootExtOwner = {
	muROOT_UID, muROOT_GID, 0
};


	/*
	 *		Allocate a Task Level
	 */

static struct muTaskLevel *AllocTaskLevel(struct muExtOwner *owner)
{
	struct muTaskLevel *level;
	ULONG size;

	size = sizeof(struct muTaskLevel);
	if (owner)
		size += owner->NumSecGroups*sizeof(UWORD);
	if (level = MAlloc(size)) {
		NewList((struct List *)&level->Tasks);
		NewList((struct List *)&level->Children);
		if (owner)
			CopyMem(owner, &level->Owner, size-sizeof(struct muTaskLevel)+sizeof(struct muExtOwner));
	}
	return(level);
}


	/*
	 *		Deallocate a Task Level
	 */

static void __inline FreeTaskLevel(struct muTaskLevel *level)
{
	Free(level, sizeof(struct muTaskLevel)+level->Owner.NumSecGroups*sizeof(UWORD));
}


	/*
	 *		Allocate a Task Node
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static struct muTaskNode *AllocTaskNode(struct Task *task, ULONG defprotection)
{
	struct muTaskNode *node;

	if (node = MAlloc(sizeof(struct muTaskNode))) {
		node->Task = task;
		node->DefProtection = defprotection;
	}
	return(node);
}


	/*
	 *		Deallocate a Task Node
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void __inline FreeTaskNode(struct muTaskNode *node)
{
	Free(node, sizeof(struct muTaskNode));
}


	/*
	 *		Find the Task Node for a given Task
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static struct muTaskNode *FindTaskNode(struct Task *task)
{
	struct MinNode *node;
	struct muTaskNode *tnode;

	for (node = muBase->TaskOwnerList[(ULONG)task%TASKHASHVALUE].mlh_Head;
		  (node->mln_Succ &&
			((tnode = (struct muTaskNode *)((ULONG)node-
						 (ULONG)&((struct muTaskNode *)NULL)->ListNode))->Task != task));
		  node = node->mln_Succ);
	return(node->mln_Succ ? tnode : NULL);
}


	/*
	 *		Add a Task Level
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void AddTaskLevel(struct muTaskLevel *level, struct muTaskLevel *dest)
{
	AddHead((struct List *)&dest->Children, (struct Node *)&level->Node);
	level->Parent = dest;
}


	/*
	 *		Remove a Task Level
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void __inline RemTaskLevel(struct muTaskLevel *level)
{
	Remove((struct Node *)&level->Node);
}


	/*
	 *		Move a Task Level to another Task Level
	 */

static void MoveTaskLevel(struct muTaskLevel *level, struct muTaskLevel *dest)
{
	Remove((struct Node *)&level->Node);
	AddHead((struct List *)&dest->Children, (struct Node *)&level->Node);
	level->Parent = dest;
}


	/*
	 *		Move all TaskNodes and TaskLevels to another Task Level
	 */

static void MoveAll(struct muTaskLevel *source, struct muTaskLevel *dest)
{
	struct muTaskNode *node;
	struct muTaskLevel *level;

	while ((node = (struct muTaskNode *)source->Tasks.mlh_Head) && node->LevelNode.mln_Succ)
		MoveTaskNode(node, dest);
	while ((level = (struct muTaskLevel *)source->Children.mlh_Head) && level->Node.mln_Succ)
		MoveTaskLevel(level, dest);
}


	/*
	 *		Add a Task Node to a Task Level
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void AddTaskNode(struct muTaskNode *node, struct muTaskLevel *dest)
{
	AddHead((struct List *)&dest->Tasks, (struct Node *)&node->LevelNode);
	AddHead((struct List *)&muBase->TaskOwnerList[(ULONG)node->Task%TASKHASHVALUE],
			  (struct Node *)&node->ListNode);
	node->Level = dest;
	CallMonitors(muTrgB_OwnerChange, muNOBODY_UID, dest->Owner.uid, NULL);
}


	/*
	 *		Remove a Task Node from a Task Level
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void __inline RemTaskNode(struct muTaskNode *node)
{
	Remove((struct Node *)&node->LevelNode);
	Remove((struct Node *)&node->ListNode);
	CallMonitors(muTrgB_OwnerChange, node->Level->Owner.uid, muNOBODY_UID, NULL);
}


	/*
	 *		Move a Task Node to another TaskLevel
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void MoveTaskNode(struct muTaskNode *node, struct muTaskLevel *dest)
{
	Remove((struct Node *)&node->LevelNode);
	AddHead((struct List *)&dest->Tasks, (struct Node *)&node->LevelNode);
	node->Level = dest;
	CallMonitors(muTrgB_OwnerChange, node->Level->Owner.uid, dest->Owner.uid, NULL);
}


	/*
	 *		Clean Up a Task Level
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void CleanUpTaskLevel(struct muTaskLevel *level)
{
	struct muTaskLevel *parent;

	while (level && IsListEmpty((struct List *)&level->Tasks) &&
			 IsListEmpty((struct List *)&level->Children)) {
		if (parent = level->Parent)
			RemTaskLevel(level);
		FreeTaskLevel(level);
		level = parent;
	};
}


	/*
	 *		Init Task List
	 */

void InitTaskList(void)
{
	ULONG i;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	for (i = 0; i < TASKHASHVALUE; i++)
		NewList((struct List *)&muBase->TaskOwnerList[i]);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
}


	/*
	 *		Create an Orphan Task Node
	 */

struct muTaskNode *CreateOrphanTask(struct Task *task, ULONG defprotection)
{
	struct muTaskLevel *level;
	struct muTaskNode *node;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (level = AllocTaskLevel(NULL))
		if (node = AllocTaskNode(task, defprotection))
			AddTaskNode(node, level);
		else
			FreeTaskLevel(level);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(node);
}


	/*
	 *		Remove all Task Nodes
	 */

void RemAllTaskNodes(void)
{
	struct muTaskNode *node;
	struct muTaskLevel *level;
	ULONG i;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	for (i = 0; i < TASKHASHVALUE; i++)
		while ((node = (struct muTaskNode *)muBase->TaskOwnerList[i].mlh_Head) && node->ListNode.mln_Succ) {
			level = node->Level;
			RemTaskNode(node);
			FreeTaskNode(node);
			CleanUpTaskLevel(level);
		}
	ReleaseSemaphore(&muBase->TaskOwnerSem);
}


	/*
	 *		Push a Task to a new Owner
	 */

BOOL PushTask(struct Task *task, struct muExtOwner *owner)
{
	BOOL res = FALSE;
	struct muTaskNode *node;
	struct muTaskLevel *child, *parent;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if ((node = FindTaskNode(task)) || (node = CreateOrphanTask(task, DEFPROTECTION))) {
		parent = node->Level;
		if (child = AllocTaskLevel(owner)) {
			MoveTaskNode(node, child);
			AddTaskLevel(child, parent);
			res = TRUE;
		}
	}
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(res);
}


	/*
	 *		Push a Task Level to a new Owner
	 */

BOOL PushTaskLevel(struct Task *task, struct muExtOwner *owner)
{
	BOOL res = FALSE;
	struct muTaskNode *node;
	struct muTaskLevel *child, *parent;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if ((node = FindTaskNode(task)) || (node = CreateOrphanTask(task, DEFPROTECTION))) {
		parent = node->Level;
		if (child = AllocTaskLevel(owner)) {
			MoveAll(parent, child);
			AddTaskLevel(child, parent);
			res = TRUE;
		}
	}
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(res);
}


	/*
	 *		Pop a Task to the previous Owner
	 */

BOOL PopTask(struct Task *task)
{
	BOOL res = FALSE;
	struct muTaskNode *node;
	struct muTaskLevel *child, *parent;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task)) {
		child = node->Level;
		if (parent = child->Parent) {
			MoveTaskNode(node, parent);
			CleanUpTaskLevel(child);
			if (parent->Parent)
				res = TRUE;
		}
	} else
		CreateOrphanTask(task, DEFPROTECTION);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(res);
}


	/*
	 *		Pop a Task to the previous Owner and Detach when the Root of the Tree is reached
	 */

BOOL PopTaskDetach(struct Task *task)
{
	BOOL res = FALSE;
	struct muTaskNode *node;
	struct muTaskLevel *child, *parent;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task)) {
		child = node->Level;
		if ((parent = child->Parent) && parent->Parent) {
			MoveTaskNode(node, parent);
			CleanUpTaskLevel(child);
			res = TRUE;
		} else if (parent = AllocTaskLevel(NULL)) {
			MoveTaskNode(node, parent);
			CleanUpTaskLevel(child);
		}
	} else
		CreateOrphanTask(task, DEFPROTECTION);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(res);
}


	/*
	 *		Pop a Task Level to the previous Owner and Detach when the Root of the Tree is reached
	 */

BOOL PopTaskLevelDetach(struct Task *task)
{
	BOOL res = FALSE;
	struct muTaskNode *node;
	struct muTaskLevel *child, *parent;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task)) {
		child = node->Level;
		if ((parent = child->Parent) && parent->Parent) {
			MoveAll(child, parent);
			CleanUpTaskLevel(child);
			res = TRUE;
		} else if (parent = AllocTaskLevel(NULL)) {
			MoveAll(child, parent);
			CleanUpTaskLevel(child);
		}
	} else
		CreateOrphanTask(task, DEFPROTECTION);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(res);
}


	/*
	 *		Get the Owner (uid:gid) of a Task
	 */

ULONG GetTaskOwner(struct Task *task)
{
	ULONG owner = muOWNER_NOBODY;
	struct muTaskNode *node;

	if (!muBase->SecurityViolation) {
		ObtainSemaphoreShared(&muBase->TaskOwnerSem);
		if (node = FindTaskNode(task))
			owner = muExtOwner2ULONG(&node->Level->Owner);
		ReleaseSemaphore(&muBase->TaskOwnerSem);
	}
	return(owner);
}


	/*
	 *		Get the Owner (struct muExtOwner *) of a Task
	 */

struct muExtOwner *GetTaskExtOwner(struct Task *task)
{
	struct muExtOwner *owner = NULL;
	struct muTaskNode *node;
	ULONG size;

	if (!muBase->SecurityViolation) {
		ObtainSemaphoreShared(&muBase->TaskOwnerSem);
		if (node = FindTaskNode(task)) {
			size = sizeof(struct muExtOwner)+node->Level->Owner.NumSecGroups*sizeof(UWORD);
			if (owner = (struct muExtOwner *)MAlloc(size))
				CopyMem(&node->Level->Owner, owner, size);
		}
		ReleaseSemaphore(&muBase->TaskOwnerSem);
	}
	return(owner);
}


	/*
	 *		Get the Default Protection Bits for a Task
	 */

ULONG GetTaskDefProtect(struct Task *task)
{
	ULONG defprotection = DEFPROTECTION;
	struct muTaskNode *node;

	ObtainSemaphoreShared(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task))
		defprotection = node->DefProtection;
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(defprotection);
}


	/*
	 *		Set the Default Protection Bits for a Task
	 */

BOOL SetTaskDefProtect(struct Task *task, ULONG defprotection)
{
	BOOL result;
	struct muTaskNode *node;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task)) {
		node->DefProtection = defprotection;
		result = TRUE;
	} else
		result = (BOOL)CreateOrphanTask(task, defprotection);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(result);
}


	/*
	 *		Set the Default Protection Bits for a Level
	 */

BOOL SetLevelDefProtect(struct Task *task, ULONG defprotection)
{
	BOOL result;
	struct muTaskLevel *level;
	struct muTaskNode *node;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task)) {
		level = node->Level;
		for (node = (struct muTaskNode *)level->Tasks.mlh_Head; node->LevelNode.mln_Succ;
			  node = (struct muTaskNode *)node->LevelNode.mln_Succ)
			node->DefProtection = defprotection;
		result = TRUE;
	} else
		result = (BOOL)CreateOrphanTask(task, defprotection);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(result);
}


	/*
	 *		Replacement for the exec.library AddTask() function
	 */

APTR __asm __saveds NEWAddTask(register __a1 struct Task *task, register __a2 APTR initpc,
										 register __a3 APTR finalpc, register __a6 struct ExecBase *sysbase)
{
	struct muTaskNode *node;
	struct muTaskLevel *level;

	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(sysbase->ThisTask)) {
		level = node->Level;
		if (node = AllocTaskNode(task, node->DefProtection))
			AddTaskNode(node, level);
	} else
		CreateOrphanTask(task, DEFPROTECTION);
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	return(muBase->OLDAddTask(task, initpc, finalpc, sysbase));
}


	/*
	 *		Replacement for the exec.library RemTask() function
	 */

void __asm __saveds NEWRemTask(register __a1 struct Task *task, register __a6 struct ExecBase *sysbase)
{
	struct muTaskNode *node;
	struct muTaskLevel *level;

	if (!task)
		task = sysbase->ThisTask;
	ObtainSemaphore(&muBase->TaskOwnerSem);
	if (node = FindTaskNode(task)) {
		level = node->Level;
		RemTaskNode(node);
		FreeTaskNode(node);
		CleanUpTaskLevel(level);
	}
	ReleaseSemaphore(&muBase->TaskOwnerSem);
	muBase->OLDRemTask(task, sysbase);
}


	/*
	 *		Get the Owner (uid:gid) of a Task
	 *
	 *		Public Library Function
	 */

ULONG __asm __saveds muGetTaskOwner(register __d0 struct Task *task)
{
	if (!task)
		task = SysBase->ThisTask;
	return(GetTaskOwner(task));
}


	/*
	 *		Get the Owner (struct muExtOwner *) of a Task
	 *
	 *		Public Library Function
	 */

struct muExtOwner __asm __saveds *muGetTaskExtOwner(register __d0 struct Task *task)
{
	if (!task)
		task = SysBase->ThisTask;
	return(GetTaskExtOwner(task));
}


	/*
	 *		Free an Extended Owner structure
	 *
	 *		Public Library Function
	 */

void __asm __saveds muFreeExtOwner(register __a0 struct muExtOwner *owner)
{
	ULONG size;

	if (owner) {
		size = sizeof(struct muExtOwner)+owner->NumSecGroups*sizeof(UWORD);
		Free(owner, size);
	}
}


	/*
	 *		Kill a task or process
	 *
	 *		Public Library Function
	 *
	 *		This function may be called by root only!
	 */

BOOL __asm __saveds muKill(register __d0 struct Task *task)
{
	BOOL res = FALSE;
	UBYTE *sp;
	ULONG size;
	struct muExtOwner *xowner;

	xowner = GetTaskExtOwner(SysBase->ThisTask);
	if (task && (task != SysBase->ThisTask) && (task != muBase->Server) &&
		 (muGetRelationshipA(xowner, NULL, NULL) & muRelF_ROOT_UID)) {
		Disable();
		switch (task->tc_Node.ln_Type) {
			case NT_TASK:
				RemTask(task);
				res = TRUE;
				break;

			case NT_PROCESS:
				Remove(task);
				task->tc_State = TS_READY;
				sp = task->tc_SPReg;
				if (SysBase->AttnFlags & AFF_68881) {
					if (size = *(ULONG *)sp) {
						sp += 110;
						if (size == 0x90)
							sp += 12;
						if ((SysBase->LibNode.lib_Version > 37) || ((SysBase->LibNode.lib_Version == 37) &&
																				  (SysBase->LibNode.lib_Revision >= 132)))
							sp += 2;
						size = sp[1];
						sp += size;
					}
					sp += 4;
				}
				*(ULONG *)sp = (ULONG)CleanUpBody;
				AddHead(&SysBase->TaskReady, task);
				res = TRUE;
				break;
		}
		Enable();
	}
	muFreeExtOwner(xowner);
	return(res);
}


	/*
	 *		Clean up a dead process
	 */

static void __saveds CleanUpBody(void)
{
	struct Task *task;
	struct Screen *scr, *t;
	struct Window *win;
	struct MsgPort *port;
	struct Requester *req;
	BOOL doclose;

	task = SysBase->ThisTask;
	Forbid();
	scr = IntuitionBase->FirstScreen;
	while (scr) {
		doclose = FALSE;
		win = scr->FirstWindow;
		while (win)
			if ((port = win->UserPort) && (port->mp_SigTask == task)) {
				ModifyIDCMP(win, NULL);
				ClearDMRequest(win);
				ClearMenuStrip(win);
				ClearPointer(win);
				while (req = win->FirstRequest)
					EndRequest(req, win);
				CloseWindow(win);
				doclose = TRUE;
				win = scr->FirstWindow;
			} else
				win = win->NextWindow;
		t = scr->NextScreen;
		if (doclose && ((scr->Flags & SCREENTYPE) != WBENCHSCREEN))
			CloseScreen(scr);
		scr = t;
	}
	Exit(NULL);
}


	/*
	 *		Freeze a task or process
	 *
	 *		Public Library Function
	 *
	 *		This function may be called by root only!
	 */

BOOL __asm __saveds muFreeze(register __d0 struct Task *task)
{
	BOOL res = FALSE;
	struct muExtOwner *xowner;

	xowner = GetTaskExtOwner(SysBase->ThisTask);
	if (task && (task != SysBase->ThisTask) && (task != muBase->Server) &&
		 (muGetRelationshipA(xowner, NULL, NULL) & muRelF_ROOT_UID)) {
		Disable();
		switch (task->tc_Node.ln_Type) {
			case NT_TASK:
			case NT_PROCESS:
				if (task->tc_State < 7) {
					Remove(task);
					AddHead((struct List *)&muBase->Frozen, task);
					task->tc_State += 7;
					res = TRUE;
				}
				break;
		}
		Enable();
	}
	muFreeExtOwner(xowner);
	return(res);
}


	/*
	 *		Unfreeze a task or process
	 *
	 *		Public Library Function
	 *
	 *		This function may be called by root only!
	 */

BOOL __asm __saveds muUnfreeze(register __d0 struct Task *task)
{
	BOOL res = FALSE;
	struct muExtOwner *xowner;

	xowner = GetTaskExtOwner(SysBase->ThisTask);
	if (task && (muGetRelationshipA(xowner, NULL, NULL) & muRelF_ROOT_UID)) {
		Disable();
		switch (task->tc_Node.ln_Type) {
			case NT_TASK:
			case NT_PROCESS:
				if (task->tc_State >= 7) {
					Remove(task);
					if ((task->tc_State -= 7) == TS_READY)
						Enqueue(&SysBase->TaskReady, task);
					else
						Enqueue(&SysBase->TaskWait, task);
					res = TRUE;
				}
				break;
		}
		Enable();
	}
	muFreeExtOwner(xowner);
	return(res);
}
