BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / commit / 9d6eb508c283ff07934a3007f68d4f0c933d5430

// Related

Nucleus

Barry Signals and small task functions 9d6eb50 (3 years, 2 months ago)
diff --git a/include/nucleus/task.h b/include/nucleus/task.h
index 460f32f..5c3d606 100644
--- a/include/nucleus/task.h
+++ b/include/nucleus/task.h
@@ -9,6 +9,7 @@
 #include <nucleus/vfs.h>
 
 typedef struct Task Task;
+typedef struct Signals Signals;
 
 /* Task priorities */
 enum Priority {
@@ -25,6 +26,7 @@ enum State {
 	RUNNING,
 	READY,
 	TERMINATED,
+	WAITING,
 };
 
 /* Structure for a Task */
@@ -33,21 +35,30 @@ struct Task {
 	pid_t tid, tgid;
 	uid_t uid, euid, suid;
 	gid_t gid, egid, sgid;
+	int status;
 	enum Priority priority;
 	enum State state;
 	uint32_t inCriticalSection;
+	uint8_t inSyscall;
 
 	uintptr_t esi, edi, ebx;
 	uintptr_t esp, ebp, eip;
 	page_dir_t pageDir;
 
+	File *executable;
+	VMRegion *stack;
+	Task *target;
+	ObjectList *wait;
+
 	/* Namespaces */
 	FileSystem *fs;
 	Files *files;
 	VirtualMemory *vm;
+	Signals *signals;
 };
 
 extern ObjectType taskType;
+extern ObjectType signalsType;
 
 extern Task *currentTask[];
 #define current currentTask[CPUID]
@@ -73,7 +84,10 @@ exit_critical_section(void)
 }
 
 void init_tasking(void);
+void block_task(enum State reason, ObjectList *list);
+Task *find_task(pid_t tid);
 void schedule(void);
 pid_t clone(int flags);
+int syscall_handler(int num, uintptr_t args);
 
 #endif
diff --git a/task/clone.c b/task/clone.c
index 0ec2f95..7516f18 100644
--- a/task/clone.c
+++ b/task/clone.c
@@ -34,9 +34,24 @@ clone(int flags)
 
 	/* Clone parent's virtual memory namespace */
 	if (flags & CLONE_VM)
-		child->vm = get(parent->fs);
+		child->vm = get(parent->vm);
 	else
-		child->fs = copy(parent->fs);
+		child->vm = copy(parent->vm);
+	/* Copy stack */
+	if (parent->stack)
+		child->stack = copy(parent->stack);
+
+	/* Clone parent's signals namespace */
+	if (flags & CLONE_SIGHAND)
+		child->signals = get(parent->signals);
+	else
+		child->signals = copy(parent->signals);
+
+	/* Get executable file */
+	if (parent->executable)
+		child->executable = get(parent->executable);
+
+	child->inSyscall = parent->inSyscall;
 
 	/* After this, anything on the stack is desynchronised */
 	child->pageDir = clone_dir();
@@ -50,7 +65,7 @@ clone(int flags)
 	put(child);
 	exit_critical_section();
 end:
-	if (!tid) {
+	if (tid && !current->inSyscall) {
 		outb(0x20, 0x20);
 		if (apic)
 			LAPIC(0xB0) = 0;
diff --git a/task/exit.c b/task/exit.c
new file mode 100644
index 0000000..52547b9
--- /dev/null
+++ b/task/exit.c
@@ -0,0 +1,39 @@
+/*
+ * This file handles task termination and contains the exit() system call.  It
+ * destroys a task and releases all resources it holds, notifies any waiting
+ * tasks and the parent.
+ */
+
+#include <nucleus/panic.h>
+#include <nucleus/task.h>
+
+extern ObjectList *readyQueue[];
+
+/* Terminate the current task */
+_Noreturn void
+terminate(void)
+{
+	current->state = TERMINATED;
+	current->inCriticalSection = 0;
+
+	/* Unblock waiting tasks */
+	Task *tmp;
+	while (current->wait && count(current->wait)) {
+		tmp = pop_from_start(current->wait);
+		add(readyQueue[tmp->priority], tmp);
+		put(tmp);
+	}
+
+	schedule();
+	__builtin_unreachable();
+}
+
+/* Exit the current task */
+_Noreturn void
+exit(int status)
+{
+	if (current->tid == 1)
+		panic("Attempted to exit init! Exit code %d", status);
+	current->status = (1 << 31) | (status & 0x0F);
+	terminate();
+}
diff --git a/task/signals.c b/task/signals.c
new file mode 100644
index 0000000..47d8c9b
--- /dev/null
+++ b/task/signals.c
@@ -0,0 +1,126 @@
+/*
+ * This file implements the Signals namespace and the associated signal
+ * functions.  It registers and dispatches signals to tasks and allows them to
+ * register signal handlers, which it runs when appropriate.
+ */
+
+#include <signal.h>
+#include <errno.h>
+#include <nucleus/memory.h>
+#include <nucleus/object.h>
+#include <nucleus/task.h>
+
+/* Signals namespace */
+struct Signals {
+	Object obj;
+	sighandler_t sig_handler[32];
+	sigset_t sigset;
+	sigset_t blocked;
+};
+
+static void signals_copy(Object *, Object *);
+
+/* Signals object type */
+ObjectType signalsType = {
+	.name = "SIGNALS",
+	.size = sizeof(Signals),
+	.copy = signals_copy,
+};
+
+/* Copy a Signals object */
+static void
+signals_copy(Object *a, Object *b)
+{
+	Signals *sa = (void *) a, *sb = (void *) b;
+	int i;
+	for (i = 0; i < 32; i++)
+		sb->sig_handler[i] = sa->sig_handler[i];
+	sb->sigset = sa->sigset;
+	sb->blocked = sa->blocked;
+}
+
+/* Send a signal to a task */
+void
+send_signal(Task *target, int sig)
+{
+	target->signals->sigset |= (1 << (sig - 1));
+}
+
+/* Send a signal to a thread */
+int
+tgkill(pid_t tgid, pid_t tid, int sig)
+{
+	if (sig < 0 || sig > 32)
+		return -EINVAL;
+
+	Task *task = find_task(tid);
+	if (!task || task->tgid != tgid)
+		return -ESRCH;
+	if (sig)
+		send_signal(task, sig);
+
+	return 0;
+}
+
+/* Send a signal to a process */
+int
+kill(pid_t pid, int sig)
+{
+	if (sig < 0 || sig > 32)
+		return -EINVAL;
+
+	size_t sent = 0;
+	Task *task;
+	foreach (taskType.objects, task) {
+		if (task->tgid != pid)
+			continue;
+		if (sig)
+			send_signal(task, sig);
+		sent++;
+	}
+
+	return sent ? 0 : -ESRCH;
+}
+
+/* Install a signal handler */
+sighandler_t
+signal(int signum, sighandler_t handler)
+{
+	if (signum <= 0 || signum > 32)
+		return (sighandler_t) -EINVAL;
+	if (!verify_access(handler, 8, PROT_EXEC))
+		return (sighandler_t) -EFAULT;
+
+	sighandler_t old = current->signals->sig_handler[signum - 1];
+	current->signals->sig_handler[signum - 1] = handler;
+	return old;
+}
+
+/* Examine and change blocked signals */
+int
+sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
+{
+	if (set && !verify_access(set, sizeof(sigset_t), PROT_READ))
+		return -EFAULT;
+	if (oldset && !verify_access(oldset, sizeof(sigset_t), PROT_WRITE))
+		return -EFAULT;
+
+	if (oldset)
+		*oldset = current->signals->blocked;
+	if (!set)
+		return 0;
+
+	switch (how) {
+	case SIG_BLOCK:
+		current->signals->blocked |= *set;
+		return 0;
+	case SIG_UNBLOCK:
+		current->signals->blocked &= ~*set;
+		return 0;
+	case SIG_SETMASK:
+		current->signals->blocked = *set;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
diff --git a/task/task.c b/task/task.c
index b23c54b..a4d137d 100644
--- a/task/task.c
+++ b/task/task.c
@@ -16,12 +16,14 @@ void init_scheduler(void);
 void timer_handler(struct InterruptFrame *frame);
 
 static void task_new(Object *);
+static void task_delete(Object *);
 
 /* Task object type */
 ObjectType taskType = {
 	.name = "TASK",
 	.size = sizeof(Task),
 	.new = task_new,
+	.delete = task_delete,
 };
 
 Task *currentTask[MAX_CPUS];
@@ -39,6 +41,23 @@ task_new(Object *obj)
 	task->state = READY;
 }
 
+/* Destroy a Task object */
+static void
+task_delete(Object *obj)
+{
+	Task *task = (void *) obj;
+	put(task->executable);
+	put(task->stack);
+	if (task->target)
+		put(task->target);
+	if (task->wait)
+		destroy_list(task->wait);
+	put(task->fs);
+	put(task->files);
+	put(task->vm);
+	put(task->signals);
+}
+
 /* Move the stack */
 static void
 move_stack(uintptr_t top, size_t size)
@@ -85,8 +104,41 @@ init_tasking(void)
 	current->files = new(&filesType);
 	/* Virtual Memory namespace */
 	current->vm = new(&virtualMemoryType);
+	/* Signals namespace */
+	current->signals = new(&signalsType);
 
 	init_scheduler();
 	register_interrupt(0, timer_handler);
 
 }
+
+/* Get the current task's PID */
+pid_t
+getpid(void)
+{
+	return current->tgid;
+}
+
+/* Block a task */
+void
+block_task(enum State reason, ObjectList *list)
+{
+	lock(current);
+	current->state = reason;
+	if (list)
+		add(list, current);
+	unlock(current);
+	schedule();
+}
+
+/* Find a task by ID */
+Task *
+find_task(pid_t tid)
+{
+	Task *task = NULL;
+	foreach (taskType.objects, task) {
+		if (task->tid == tid)
+			break;
+	}
+	return task;
+}
diff --git a/task/uid.c b/task/uid.c
new file mode 100644
index 0000000..5b50c90
--- /dev/null
+++ b/task/uid.c
@@ -0,0 +1,95 @@
+/*
+ * This file implements the wrapper functions for setting/getting the
+ * (effective) user/group ID of the current task.  These functions also check
+ * the required permission when setting any of these attributes.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <nucleus/task.h>
+
+/* Get the current task's UID */
+uid_t
+getuid(void)
+{
+	return current->uid;
+}
+
+/* Set the current task's (E)UID */
+int
+setuid(uid_t uid)
+{
+	if (uid != current->uid
+	 && uid != current->suid
+	 && !super_user())
+		return -EPERM;
+	if (super_user()) {
+		current->uid = uid;
+		current->suid = uid;
+	}
+	current->euid = uid;
+	return 0;
+}
+
+/* Get the current task's EUID */
+uid_t
+geteuid(void)
+{
+	return current->euid;
+}
+
+/* Set the current task's EUID */
+int
+seteuid(uid_t euid)
+{
+	if (euid != current->uid
+	 && euid != current->euid
+	 && euid != current->suid
+	 && !super_user())
+		return -EPERM;
+	current->euid = euid;
+	return 0;
+}
+
+/* Get the current task's GID */
+gid_t
+getgid(void)
+{
+	return current->gid;
+}
+
+/* Set the current task's (E)GID */
+int
+setgid(gid_t gid)
+{
+	if (gid != current->gid
+	 && gid != current->sgid
+	 && !super_user())
+		return -EPERM;
+	if (super_user()) {
+		current->gid = gid;
+		current->sgid = gid;
+	}
+	current->egid = gid;
+	return 0;
+}
+
+/* Get the current task's EGID */
+gid_t
+getegid(void)
+{
+	return current->egid;
+}
+
+/* Set the current task's EGID */
+int
+setegid(gid_t egid)
+{
+	if (egid != current->gid
+	 && egid != current->egid
+	 && egid != current->sgid
+	 && !super_user())
+		return -EPERM;
+	current->egid = egid;
+	return 0;
+}
diff --git a/task/wait.c b/task/wait.c
new file mode 100644
index 0000000..35ee29a
--- /dev/null
+++ b/task/wait.c
@@ -0,0 +1,36 @@
+/*
+ * This file implements task waiting.  A task may wait for another task to
+ * change state.  While waiting, the current task is blocked.  When the target
+ * process changes state the current task is unblocked and gets any relevant
+ * information from it before releasing it.
+ */
+
+#include <errno.h>
+#include <nucleus/task.h>
+
+/* Wait for a child process to change state */
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+	if (wstatus && !verify_access(wstatus, sizeof(int), PROT_WRITE))
+		return -EFAULT;
+
+	Task *task = find_task(pid);
+	if (!task)
+		return -ECHILD;
+	current->target = get(task);
+
+	if (task->state != TERMINATED) {
+		if (!task->wait)
+			task->wait = create_list(&taskType, LIST_NORMAL);
+		block_task(WAITING, task->wait);
+	}
+
+	if (wstatus)
+		*wstatus = task->status;
+
+	put(current->target);
+	current->target = NULL;
+
+	return task->tid;
+}