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;
+}