#include "procfs.h"
#include "process.h"
#include "spinlock.h"
#include "utils.h"
#include "heap.h"
#include "pmm.h"
#include "timer.h"
#include "kernel/cmdline.h"
#include <stddef.h>
static fs_node_t g_proc_root;
static fs_node_t g_proc_self;
static fs_node_t g_proc_self_status;
static fs_node_t g_proc_uptime;
static fs_node_t g_proc_meminfo;
static fs_node_t g_proc_cmdline;
static fs_node_t g_proc_dmesg;
#define PID_NODE_POOL 8
static fs_node_t g_pid_dir[PID_NODE_POOL];
static fs_node_t g_pid_status[PID_NODE_POOL];
static fs_node_t g_pid_maps[PID_NODE_POOL];
static uint32_t g_pid_pool_idx = 0;
extern struct process* ready_queue_head;
extern spinlock_t sched_lock;
static struct process* proc_find_pid_safe(uint32_t pid) {
return process_find_by_pid(pid);
}
static int proc_snprintf(char* buf, uint32_t sz, const char* key, uint32_t val) {
if (sz < 2) return 0;
uint32_t w = 0;
const char* p = key;
while (*p && w + 1 < sz) buf[w++] = *p++;
char num[16];
itoa(val, num, 10);
p = num;
while (*p && w + 1 < sz) buf[w++] = *p++;
if (w + 1 < sz) buf[w++] = '\n';
buf[w] = 0;
return (int)w;
}
static uint32_t proc_self_status_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
(void)node;
if (!current_process) return 0;
char tmp[512];
uint32_t len = 0;
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Pid:\t", current_process->pid);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "PPid:\t", current_process->parent_pid);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Pgrp:\t", current_process->pgrp_id);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Session:\t", current_process->session_id);
const char* state_str = "unknown\n";
switch (current_process->state) {
case PROCESS_READY: state_str = "R (ready)\n"; break;
case PROCESS_RUNNING: state_str = "R (running)\n"; break;
case PROCESS_BLOCKED: state_str = "S (blocked)\n"; break;
case PROCESS_SLEEPING: state_str = "S (sleeping)\n"; break;
case PROCESS_ZOMBIE: state_str = "Z (zombie)\n"; break;
}
const char* s = "State:\t";
while (*s && len + 1 < sizeof(tmp)) tmp[len++] = *s++;
s = state_str;
while (*s && len + 1 < sizeof(tmp)) tmp[len++] = *s++;
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "SigPnd:\t", current_process->sig_pending_mask);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "SigBlk:\t", current_process->sig_blocked_mask);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "HeapStart:\t", (uint32_t)current_process->heap_start);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "HeapBreak:\t", (uint32_t)current_process->heap_break);
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
extern size_t klog_read(char* out, size_t out_size);
static uint32_t proc_dmesg_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
(void)node;
/* Allocate from heap — too large for kernel stack */
char* tmp = kmalloc(16384);
if (!tmp) return 0;
size_t len = klog_read(tmp, 16384);
uint32_t ret = 0;
if (offset < (uint32_t)len) {
uint32_t avail = (uint32_t)len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
ret = size;
}
kfree(tmp);
return ret;
}
static uint32_t proc_cmdline_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
(void)node;
const char* raw = cmdline_raw();
uint32_t len = (uint32_t)strlen(raw);
char tmp[CMDLINE_MAX + 1];
memcpy(tmp, raw, len);
tmp[len] = '\n';
len++;
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
static uint32_t proc_uptime_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
(void)node;
uint32_t ticks = get_tick_count();
uint32_t secs = (ticks * TIMER_MS_PER_TICK) / 1000;
uint32_t frac = ((ticks * TIMER_MS_PER_TICK) % 1000) / 10;
char tmp[64];
uint32_t len = 0;
char num[16];
itoa(secs, num, 10);
const char* p = num;
while (*p && len + 2 < sizeof(tmp)) tmp[len++] = *p++;
if (len + 2 < sizeof(tmp)) tmp[len++] = '.';
if (frac < 10 && len + 2 < sizeof(tmp)) tmp[len++] = '0';
itoa(frac, num, 10);
p = num;
while (*p && len + 2 < sizeof(tmp)) tmp[len++] = *p++;
if (len + 1 < sizeof(tmp)) tmp[len++] = '\n';
if (len < sizeof(tmp)) tmp[len] = 0;
else tmp[sizeof(tmp) - 1] = 0;
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
extern void pmm_print_stats(void);
static uint32_t proc_meminfo_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
(void)node;
char tmp[256];
uint32_t len = 0;
/* Count processes (under sched_lock to avoid race) */
uint32_t nprocs = 0;
{
uintptr_t fl = spin_lock_irqsave(&sched_lock);
if (ready_queue_head) {
struct process* it = ready_queue_head;
const struct process* start = it;
do {
nprocs++;
it = it->next;
} while (it && it != start);
}
spin_unlock_irqrestore(&sched_lock, fl);
}
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Processes:\t", nprocs);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "TickCount:\t", get_tick_count());
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
/* --- per-PID status read (inode == target pid) --- */
static uint32_t proc_pid_status_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
uint32_t pid = node->inode;
struct process* p = proc_find_pid_safe(pid);
if (!p) return 0;
char tmp[512];
uint32_t len = 0;
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Pid:\t", p->pid);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "PPid:\t", p->parent_pid);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Pgrp:\t", p->pgrp_id);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "Session:\t", p->session_id);
const char* state_str = "unknown\n";
switch (p->state) {
case PROCESS_READY: state_str = "R (ready)\n"; break;
case PROCESS_RUNNING: state_str = "R (running)\n"; break;
case PROCESS_BLOCKED: state_str = "S (blocked)\n"; break;
case PROCESS_SLEEPING: state_str = "S (sleeping)\n"; break;
case PROCESS_ZOMBIE: state_str = "Z (zombie)\n"; break;
}
const char* s = "State:\t";
while (*s && len + 1 < sizeof(tmp)) tmp[len++] = *s++;
s = state_str;
while (*s && len + 1 < sizeof(tmp)) tmp[len++] = *s++;
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "SigPnd:\t", p->sig_pending_mask);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "SigBlk:\t", p->sig_blocked_mask);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "HeapStart:\t", (uint32_t)p->heap_start);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "HeapBreak:\t", (uint32_t)p->heap_break);
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
/* --- per-PID cmdline read (inode == target pid) --- */
static uint32_t proc_pid_cmdline_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
uint32_t pid = node->inode;
struct process* p = proc_find_pid_safe(pid);
if (!p) return 0;
uint32_t len = (uint32_t)strlen(p->cmdline);
char tmp[256];
memcpy(tmp, p->cmdline, len);
if (len + 1 < sizeof(tmp)) { tmp[len] = '\n'; len++; }
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
/* --- per-PID maps read (inode == target pid) --- */
static uint32_t proc_pid_maps_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
uint32_t pid = node->inode;
struct process* p = proc_find_pid_safe(pid);
if (!p) return 0;
char tmp[1024];
uint32_t len = 0;
if (p->heap_start && p->heap_break > p->heap_start) {
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "heap:\t", (uint32_t)p->heap_start);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "brk:\t", (uint32_t)p->heap_break);
}
for (int i = 0; i < PROCESS_MAX_MMAPS; i++) {
if (p->mmaps[i].length == 0) continue;
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "mmap:\t", (uint32_t)p->mmaps[i].base);
len += (uint32_t)proc_snprintf(tmp + len, sizeof(tmp) - len, "len:\t", p->mmaps[i].length);
}
if (len == 0) {
const char* msg = "(empty)\n";
while (*msg && len + 1 < sizeof(tmp)) tmp[len++] = *msg++;
}
if (offset >= len) return 0;
uint32_t avail = len - offset;
if (size > avail) size = avail;
memcpy(buffer, tmp + offset, size);
return size;
}
/* --- per-PID directory --- */
/* Forward declarations for file_operations / inode_operations tables */
static const struct file_operations procfs_pid_dir_fops;
static const struct inode_operations procfs_pid_dir_iops;
static const struct file_operations procfs_pid_status_fops;
static const struct file_operations procfs_pid_maps_fops;
static const struct file_operations procfs_pid_cmdline_fops;
static fs_node_t* proc_pid_finddir(fs_node_t* node, const char* name) {
uint32_t pid = node->inode;
uint32_t slot = g_pid_pool_idx;
if (strcmp(name, "status") == 0) {
g_pid_pool_idx = (slot + 1) % PID_NODE_POOL;
memset(&g_pid_status[slot], 0, sizeof(fs_node_t));
strcpy(g_pid_status[slot].name, "status");
g_pid_status[slot].flags = FS_FILE;
g_pid_status[slot].inode = pid;
g_pid_status[slot].f_ops = &procfs_pid_status_fops;
return &g_pid_status[slot];
}
if (strcmp(name, "maps") == 0) {
g_pid_pool_idx = (slot + 1) % PID_NODE_POOL;
memset(&g_pid_maps[slot], 0, sizeof(fs_node_t));
strcpy(g_pid_maps[slot].name, "maps");
g_pid_maps[slot].flags = FS_FILE;
g_pid_maps[slot].inode = pid;
g_pid_maps[slot].f_ops = &procfs_pid_maps_fops;
return &g_pid_maps[slot];
}
if (strcmp(name, "cmdline") == 0) {
g_pid_pool_idx = (slot + 1) % PID_NODE_POOL;
memset(&g_pid_status[slot], 0, sizeof(fs_node_t));
strcpy(g_pid_status[slot].name, "cmdline");
g_pid_status[slot].flags = FS_FILE;
g_pid_status[slot].inode = pid;
g_pid_status[slot].f_ops = &procfs_pid_cmdline_fops;
return &g_pid_status[slot];
}
return NULL;
}
static int proc_pid_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
(void)node;
if (!inout_index || !buf) return -1;
if (buf_len < sizeof(struct vfs_dirent)) return -1;
static const char* entries[] = { "status", "maps", "cmdline" };
uint32_t idx = *inout_index;
if (idx >= 3) return 0;
struct vfs_dirent* d = (struct vfs_dirent*)buf;
d->d_ino = 300 + idx;
d->d_type = FS_FILE;
d->d_reclen = sizeof(struct vfs_dirent);
{
const char* s = entries[idx];
uint32_t j = 0;
while (s[j] && j + 1 < sizeof(d->d_name)) { d->d_name[j] = s[j]; j++; }
d->d_name[j] = 0;
}
*inout_index = idx + 1;
return (int)sizeof(struct vfs_dirent);
}
static fs_node_t* proc_get_pid_dir(uint32_t pid) {
if (!proc_find_pid_safe(pid)) return NULL;
uint32_t slot = g_pid_pool_idx;
g_pid_pool_idx = (slot + 1) % PID_NODE_POOL;
memset(&g_pid_dir[slot], 0, sizeof(fs_node_t));
char num[16];
itoa(pid, num, 10);
strcpy(g_pid_dir[slot].name, num);
g_pid_dir[slot].flags = FS_DIRECTORY;
g_pid_dir[slot].inode = pid;
g_pid_dir[slot].f_ops = &procfs_pid_dir_fops;
g_pid_dir[slot].i_ops = &procfs_pid_dir_iops;
return &g_pid_dir[slot];
}
/* --- /proc/self --- */
static fs_node_t* proc_self_finddir(fs_node_t* node, const char* name) {
(void)node;
if (strcmp(name, "status") == 0) return &g_proc_self_status;
return NULL;
}
static int proc_self_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
(void)node;
if (!inout_index || !buf) return -1;
if (buf_len < sizeof(struct vfs_dirent)) return -1;
static const char* entries[] = { "status" };
uint32_t idx = *inout_index;
if (idx >= 1) return 0;
struct vfs_dirent* d = (struct vfs_dirent*)buf;
d->d_ino = 100 + idx;
d->d_type = 0;
d->d_reclen = sizeof(struct vfs_dirent);
{
const char* s = entries[idx];
uint32_t j = 0;
while (s[j] && j + 1 < sizeof(d->d_name)) { d->d_name[j] = s[j]; j++; }
d->d_name[j] = 0;
}
*inout_index = idx + 1;
return (int)sizeof(struct vfs_dirent);
}
static int is_numeric(const char* s) {
if (!s || !*s) return 0;
while (*s) { if (*s < '0' || *s > '9') return 0; s++; }
return 1;
}
static uint32_t parse_uint(const char* s) {
uint32_t v = 0;
while (*s >= '0' && *s <= '9') { v = v * 10 + (uint32_t)(*s - '0'); s++; }
return v;
}
static fs_node_t* proc_root_finddir(fs_node_t* node, const char* name) {
(void)node;
if (strcmp(name, "self") == 0) return &g_proc_self;
if (strcmp(name, "uptime") == 0) return &g_proc_uptime;
if (strcmp(name, "meminfo") == 0) return &g_proc_meminfo;
if (strcmp(name, "cmdline") == 0) return &g_proc_cmdline;
if (strcmp(name, "dmesg") == 0) return &g_proc_dmesg;
if (is_numeric(name)) return proc_get_pid_dir(parse_uint(name));
return NULL;
}
static int proc_root_readdir(fs_node_t* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
(void)node;
if (!inout_index || !buf) return -1;
if (buf_len < sizeof(struct vfs_dirent)) return -1;
uint32_t idx = *inout_index;
struct vfs_dirent* d = (struct vfs_dirent*)buf;
static const char* fixed[] = { "self", "uptime", "meminfo", "cmdline", "dmesg" };
if (idx < 5) {
d->d_ino = 200 + idx;
d->d_type = (idx == 0) ? FS_DIRECTORY : FS_FILE;
d->d_reclen = sizeof(struct vfs_dirent);
const char* s = fixed[idx];
uint32_t j = 0;
while (s[j] && j + 1 < sizeof(d->d_name)) { d->d_name[j] = s[j]; j++; }
d->d_name[j] = 0;
*inout_index = idx + 1;
return (int)sizeof(struct vfs_dirent);
}
/* After fixed entries, list numeric PIDs (under sched_lock) */
uint32_t pi = idx - 5;
uint32_t count = 0;
int found = 0;
{
uintptr_t fl = spin_lock_irqsave(&sched_lock);
if (ready_queue_head) {
struct process* it = ready_queue_head;
const struct process* start = it;
do {
if (count == pi) {
char num[16];
itoa(it->pid, num, 10);
d->d_ino = 400 + it->pid;
d->d_type = FS_DIRECTORY;
d->d_reclen = sizeof(struct vfs_dirent);
uint32_t j = 0;
while (num[j] && j + 1 < sizeof(d->d_name) && j < sizeof(num) - 1) { d->d_name[j] = num[j]; j++; }
d->d_name[j] = 0;
*inout_index = idx + 1;
found = 1;
break;
}
count++;
it = it->next;
} while (it && it != start);
}
spin_unlock_irqrestore(&sched_lock, fl);
}
return found ? (int)sizeof(struct vfs_dirent) : 0;
}
/* --- file_operations tables --- */
static const struct file_operations procfs_root_fops = {0};
static const struct inode_operations procfs_root_iops = {
.lookup = proc_root_finddir,
.readdir = proc_root_readdir,
};
static const struct file_operations procfs_self_fops = {0};
static const struct inode_operations procfs_self_iops = {
.lookup = proc_self_finddir,
.readdir = proc_self_readdir,
};
static const struct file_operations procfs_self_status_fops = {
.read = proc_self_status_read,
};
static const struct file_operations procfs_uptime_fops = {
.read = proc_uptime_read,
};
static const struct file_operations procfs_meminfo_fops = {
.read = proc_meminfo_read,
};
static const struct file_operations procfs_cmdline_fops = {
.read = proc_cmdline_read,
};
static const struct file_operations procfs_dmesg_fops = {
.read = proc_dmesg_read,
};
static const struct file_operations procfs_pid_dir_fops = {0};
static const struct inode_operations procfs_pid_dir_iops = {
.lookup = proc_pid_finddir,
.readdir = proc_pid_readdir,
};
static const struct file_operations procfs_pid_status_fops = {
.read = proc_pid_status_read,
};
static const struct file_operations procfs_pid_maps_fops = {
.read = proc_pid_maps_read,
};
static const struct file_operations procfs_pid_cmdline_fops = {
.read = proc_pid_cmdline_read,
};
fs_node_t* procfs_create_root(void) {
memset(&g_proc_root, 0, sizeof(g_proc_root));
strcpy(g_proc_root.name, "proc");
g_proc_root.flags = FS_DIRECTORY;
g_proc_root.f_ops = &procfs_root_fops;
g_proc_root.i_ops = &procfs_root_iops;
memset(&g_proc_self, 0, sizeof(g_proc_self));
strcpy(g_proc_self.name, "self");
g_proc_self.flags = FS_DIRECTORY;
g_proc_self.f_ops = &procfs_self_fops;
g_proc_self.i_ops = &procfs_self_iops;
memset(&g_proc_self_status, 0, sizeof(g_proc_self_status));
strcpy(g_proc_self_status.name, "status");
g_proc_self_status.flags = FS_FILE;
g_proc_self_status.f_ops = &procfs_self_status_fops;
memset(&g_proc_uptime, 0, sizeof(g_proc_uptime));
strcpy(g_proc_uptime.name, "uptime");
g_proc_uptime.flags = FS_FILE;
g_proc_uptime.f_ops = &procfs_uptime_fops;
memset(&g_proc_meminfo, 0, sizeof(g_proc_meminfo));
strcpy(g_proc_meminfo.name, "meminfo");
g_proc_meminfo.flags = FS_FILE;
g_proc_meminfo.f_ops = &procfs_meminfo_fops;
memset(&g_proc_cmdline, 0, sizeof(g_proc_cmdline));
strcpy(g_proc_cmdline.name, "cmdline");
g_proc_cmdline.flags = FS_FILE;
g_proc_cmdline.f_ops = &procfs_cmdline_fops;
memset(&g_proc_dmesg, 0, sizeof(g_proc_dmesg));
strcpy(g_proc_dmesg.name, "dmesg");
g_proc_dmesg.flags = FS_FILE;
g_proc_dmesg.f_ops = &procfs_dmesg_fops;
return &g_proc_root;
}