summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authordzwdz2023-01-06 21:11:44 +0100
committerdzwdz2023-01-06 21:11:44 +0100
commita1a4ab33cd75f0bc1d5e71989b01ba3446c998ae (patch)
tree4796f9ffc780dcb0336ed7c66c0cb42584320d61 /src/kernel
parente0c7bad47a54d865ef6194643e2cd20f6094e507 (diff)
kernel: basic procfs
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/proc.c26
-rw-r--r--src/kernel/proc.h6
-rw-r--r--src/kernel/syscalls.c2
-rw-r--r--src/kernel/vfs/procfs.c122
-rw-r--r--src/kernel/vfs/procfs.h4
-rw-r--r--src/kernel/vfs/request.c4
-rw-r--r--src/kernel/vfs/request.h2
7 files changed, 158 insertions, 8 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index 6bc7754..3f3aba5 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -1,18 +1,19 @@
-#include <camellia/flags.h>
#include <camellia/errno.h>
+#include <camellia/flags.h>
#include <kernel/arch/generic.h>
#include <kernel/execbuf.h>
#include <kernel/mem/alloc.h>
#include <kernel/panic.h>
#include <kernel/proc.h>
#include <kernel/vfs/mount.h>
+#include <kernel/vfs/procfs.h>
#include <shared/mem.h>
#include <stdint.h>
static struct process *process_first = NULL;
struct process *process_current;
-static uint32_t next_pid = 0;
+static uint32_t next_pid = 1;
/** Removes a process from the process tree. */
@@ -211,6 +212,8 @@ void process_try2collect(struct process *dead) {
process_transition(parent, PS_RUNNING);
}
+ handle_close(dead->specialh.procfs);
+ assert(dead->refcount == 0);
if (dead != process_first) {
process_forget(dead);
kfree(dead);
@@ -291,12 +294,25 @@ struct handle *process_handle_get(struct process *p, handle_t id) {
static struct handle h = (struct handle){
.type = HANDLE_FS_FRONT,
.backend = NULL,
- .refcount = 2,
+ .refcount = 2, /* never free */
};
return &h;
+ } else if (id == HANDLE_PROCFS) {
+ if (!p->specialh.procfs) {
+ struct handle *h = kmalloc(sizeof *h);
+ *h = (struct handle){
+ .type = HANDLE_FS_FRONT,
+ .backend = procfs_backend(p),
+ .refcount = 1,
+ };
+ p->specialh.procfs = h;
+ }
+ return p->specialh.procfs;
+ } else if (0 <= id && id < HANDLE_MAX) {
+ return p->_handles[id];
+ } else {
+ return NULL;
}
- if (id < 0 || id >= HANDLE_MAX) return NULL;
- return p->_handles[id];
}
handle_t process_handle_init(struct process *p, enum handle_type type, struct handle **hs) {
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index eb39b51..07cba28 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -50,10 +50,14 @@ struct process {
struct vfs_mount *mount;
struct handle **_handles; /* points to struct handle *[HANDLE_MAX] */
uint64_t *handles_refcount; /* works just like pages_refcount */
+ struct {
+ struct handle *procfs;
+ } specialh;
uint32_t cid; /* child id. unique amongst all of this process' siblings */
uint32_t nextcid; /* the child id to assign to the next spawned child */
- uint32_t globalid; /* only for debugging, don't expose to userland */
+ uint32_t globalid; /* only for internal use, don't expose to userland */
+ uint32_t refcount; /* non-owning. should always be 0 on kill */
bool noreap;
/* allocated once, the requests from WAITS4FS get stored here */
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index 2ace0f4..f1abe30 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -60,7 +60,7 @@ long _syscall_fork(int flags, handle_t __user *fs_front) {
SYSCALL_RETURN(-EMFILE);
}
- h->backend = kmalloc(sizeof *h->backend);
+ h->backend = kzalloc(sizeof *h->backend);
h->backend->is_user = true;
h->backend->potential_handlers = 1;
h->backend->refcount = 2; // child + handle
diff --git a/src/kernel/vfs/procfs.c b/src/kernel/vfs/procfs.c
new file mode 100644
index 0000000..e434c45
--- /dev/null
+++ b/src/kernel/vfs/procfs.c
@@ -0,0 +1,122 @@
+#include <camellia/errno.h>
+#include <kernel/mem/virt.h>
+#include <kernel/panic.h>
+#include <kernel/proc.h>
+#include <kernel/vfs/procfs.h>
+#include <kernel/vfs/request.h>
+#include <shared/mem.h>
+
+static uint32_t openpath(const char *path, size_t len, struct process *root);
+static struct process *findgid(uint32_t gid, struct process *root);
+static void procfs_accept(struct vfs_request *req);
+static void procfs_cleanup(struct vfs_backend *be);
+
+static uint32_t
+openpath(const char *path, size_t len, struct process *p)
+{
+ if (len == 0) return 0;
+ path++, len--;
+
+ while (len) {
+ uint32_t cid = 0;
+ for (; 0 < len && *path != '/'; path++, len--) {
+ char c = *path;
+ if (!('0' <= c && c <= '9')) {
+ return 0;
+ }
+ cid = cid * 10 + *path - '0';
+ }
+ if (len == 0) {
+ return 0;
+ }
+ assert(*path == '/');
+ path++, len--;
+
+ p = p->child;
+ if (!p) return 0;
+ while (p->cid != cid) {
+ p = p->sibling;
+ if (!p) return 0;
+ }
+ }
+ return p->globalid;
+}
+
+static struct process *
+findgid(uint32_t gid, struct process *root)
+{
+ for (struct process *p = root; p; p = process_next(p)) {
+ if (p->globalid == gid) return p;
+ }
+ return NULL;
+}
+
+static void
+procfs_accept(struct vfs_request *req)
+{
+ struct process *root = req->backend->kern.data;
+ struct process *p;
+ char buf[512];
+ assert(root);
+ if (req->type == VFSOP_OPEN) {
+ int gid;
+ assert(req->input.kern);
+ gid = openpath(req->input.buf_kern, req->input.len, root);
+ vfsreq_finish_short(req, gid == 0 ? -ENOENT : gid);
+ return;
+ }
+ p = findgid((uintptr_t)req->id, root);
+ if (!p) {
+ vfsreq_finish_short(req, -EGENERIC);
+ return;
+ }
+
+ if (req->type == VFSOP_READ) {
+ // TODO port dirbuild to kernel
+ int pos = 0;
+ if (req->offset != 0) {
+ vfsreq_finish_short(req, -ENOSYS);
+ return;
+ }
+ for (struct process *iter = p->child; iter; iter = iter->sibling) {
+ assert(pos < 512);
+ // processes could possibly be identified by unique identifiers instead
+ // e.g. an encrypted gid, or just a randomly generated one
+ // con: would require bringing in a crypto library
+ pos += snprintf(buf + pos, 512 - pos, "%d/", iter->cid) + 1;
+ if (512 <= pos) {
+ vfsreq_finish_short(req, -1);
+ }
+ }
+ assert(0 <= pos && (size_t)pos <= sizeof buf);
+ virt_cpy_to(req->caller->pages, req->output.buf, buf, pos);
+ vfsreq_finish_short(req, pos);
+ } else {
+ vfsreq_finish_short(req, -ENOSYS);
+ }
+}
+
+static void
+procfs_cleanup(struct vfs_backend *be)
+{
+ struct process *p = be->kern.data;
+ assert(p);
+ p->refcount--;
+}
+
+struct vfs_backend *
+procfs_backend(struct process *proc)
+{
+ struct vfs_backend *be = kzalloc(sizeof(struct vfs_backend));
+ *be = (struct vfs_backend) {
+ .is_user = false,
+ .potential_handlers = 1,
+ .refcount = 1,
+ .kern.accept = procfs_accept,
+ .kern.data = proc,
+ .kern.cleanup = procfs_cleanup,
+ };
+ proc->refcount++;
+ assert(proc->refcount); /* overflow */
+ return be;
+}
diff --git a/src/kernel/vfs/procfs.h b/src/kernel/vfs/procfs.h
new file mode 100644
index 0000000..5ee4e96
--- /dev/null
+++ b/src/kernel/vfs/procfs.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <kernel/vfs/request.h>
+
+struct vfs_backend *procfs_backend(struct process *proc);
diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c
index a129f2e..2288de5 100644
--- a/src/kernel/vfs/request.c
+++ b/src/kernel/vfs/request.c
@@ -155,7 +155,9 @@ void vfs_backend_refdown(struct vfs_backend *b) {
assert(b);
assert(b->refcount > 0);
if (--(b->refcount) > 0) return;
-
assert(!b->queue);
+ if (!b->is_user && b->kern.cleanup) {
+ b->kern.cleanup(b);
+ }
kfree(b);
}
diff --git a/src/kernel/vfs/request.h b/src/kernel/vfs/request.h
index db707d0..ef7c83a 100644
--- a/src/kernel/vfs/request.h
+++ b/src/kernel/vfs/request.h
@@ -22,6 +22,8 @@ struct vfs_backend {
} user;
struct {
void (*accept)(struct vfs_request *);
+ void (*cleanup)(struct vfs_backend *);
+ void *data;
} kern;
};
};