commit 0a04a7c0dfaaf17dd49a7e4ed11d1952375614c6 from: Benjamin Stürz date: Tue Dec 19 16:31:54 2023 UTC Initial commit commit - /dev/null commit + 0a04a7c0dfaaf17dd49a7e4ed11d1952375614c6 blob - /dev/null blob + b08600347e76b7f83bcd5007aef65da71483e99e (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,17 @@ +CFLAGS = -Wall -Wextra -O2 -pipe +LIBS = -lutil -lkvm -lfuse + +all: procfs + +procfs: procfs.c files.h + ${CC} -o procfs procfs.c ${CFLAGS} ${LIBS} + +clean: + rm -f procfs + +mount: procfs + mkdir -p mp + doas ./procfs -f mp + +umount: + doas umount mp blob - /dev/null blob + 9caddb70f332c3075760d096ac33443c09f2c32b (mode 644) --- /dev/null +++ files.h @@ -0,0 +1,244 @@ + +static int +copy_str(const char *field, size_t max, char *buf, size_t size, off_t off) +{ + const size_t n = strnlen(field, max); + if ((size_t)off < n) { + if ((n - off) < size) + size = n - off; + memcpy(buf, field + off, size); + return n - off; + } else if ((size_t)off > n) { + return 0; + } else { + buf[0] = '\n'; + return 1; + } +} + +static int +copy_str2(const char *field, char *buf, size_t size, off_t off) +{ + const size_t n = strlen(field); + if ((size_t)off < n) { + if ((n - off) < size) + size = n - off; + memcpy(buf, field + off, size); + return n - off; + } else { + return 0; + } +} + +static int +copy_uint(unsigned int field, char *buf, size_t size, off_t off) +{ + char buffer[32]; + int n; + + n = snprintf(buffer, sizeof (buffer), "%u\n", field); + if (n >= (int)sizeof (buffer)) { + warnx("copy_uint(%u): Failed to format field.", field); + return -EIO; + } + + return copy_str2(buffer, buf, size, off); +} + +static int +copy_int(int field, char *buf, size_t size, off_t off) +{ + char buffer[32]; + int n; + + n = snprintf(buffer, sizeof (buffer), "%d\n", field); + if (n >= (int)sizeof (buffer)) { + warnx("copy_int(%u): Failed to format field.", field); + return -EIO; + } + + return copy_str2(buffer, buf, size, off); +} + +#define def_uint(arg, name, value) \ +static int \ +name(arg, char *buf, size_t size, off_t off) \ +{ \ + return copy_uint((value), buf, size, off); \ +} + +#define def_int(arg, name, value) \ +static int \ +name(arg, char *buf, size_t size, off_t off) \ +{ \ + return copy_int((value), buf, size, off); \ +} + +#define def_str(arg, name, value, max) \ +static int \ +name(arg, char *buf, size_t size, off_t off) \ +{ \ + return copy_str((value), max, buf, size, off); \ +} + +def_uint(struct kinfo_proc *kp, read_pid, kp->p_pid); +def_str(struct kinfo_proc *kp, read_comm, kp->p_comm, KI_MAXCOMLEN); +def_str(struct kinfo_proc *kp, read_name, kp->p_name, KI_MAXCOMLEN); +def_str(struct kinfo_proc *kp, read_emul, kp->p_emul, KI_EMULNAMELEN); +def_str(struct kinfo_proc *kp, read_wmesg, kp->p_wmesg, KI_WMESGLEN); +def_str(struct kinfo_proc *kp, read_login, kp->p_login, KI_MAXLOGNAME); +def_uint(struct kinfo_proc *kp, read_uid, kp->p_uid); +def_uint(struct kinfo_proc *kp, read_ruid, kp->p_ruid); +def_uint(struct kinfo_proc *kp, read_gid, kp->p_gid); +def_uint(struct kinfo_proc *kp, read_rgid, kp->p_rgid); +def_uint(struct kinfo_proc *kp, read_priority, kp->p_priority); +def_int(struct kinfo_proc *kp, read_nice, kp->p_nice - 20); +def_int(struct kinfo_proc *kp, read_vm_rssize, kp->p_vm_rssize); +def_int(struct kinfo_proc *kp, read_vm_tsize, kp->p_vm_tsize); +def_int(struct kinfo_proc *kp, read_vm_dsize, kp->p_vm_dsize); +def_int(struct kinfo_proc *kp, read_vm_ssize, kp->p_vm_ssize); + +static int +read_flag(struct kinfo_proc *kp, char *buf, size_t size, off_t off) +{ + struct flag { + const char *name; + uint32_t mask; + }; + static struct flag flags[] = { + { .name = "inktr", .mask = P_INKTR, }, + { .name = "profpend", .mask = P_PROFPEND, }, + { .name = "alrmpend", .mask = P_ALRMPEND, }, + { .name = "sigsuspend", .mask = P_SIGSUSPEND, }, + { .name = "cantsleep", .mask = P_CANTSLEEP, }, + { .name = "wsleep", .mask = P_WSLEEP, }, + { .name = "sintr", .mask = P_SINTR, }, + { .name = "system", .mask = P_SYSTEM, }, + { .name = "timeout", .mask = P_TIMEOUT, }, + { .name = "wexit", .mask = P_WEXIT, }, + { .name = "oweupc", .mask = P_OWEUPC, }, + { .name = "suspsingle", .mask = P_SUSPSINGLE, }, + { .name = "continued", .mask = P_CONTINUED, }, + { .name = "thread", .mask = P_THREAD, }, + { .name = "suspsig", .mask = P_SUSPSIG, }, + { .name = "softdep", .mask = P_SOFTDEP, }, + { .name = "cpupeg", .mask = P_CPUPEG, }, + { .name = NULL, .mask = 0, }, + }; + static char buffer[200]; + + buffer[0] = '\0'; + for (const struct flag *f = flags; f->name != NULL; ++f) { + if ((kp->p_flag & f->mask) == f->mask) { + strlcat(buffer, f->name, sizeof (buffer)); + strlcat(buffer, "\n", sizeof (buffer)); + } + } + return copy_str2(buffer, buf, size, off); +} + +static int +read_groups(struct kinfo_proc *kp, char *buf, size_t size, off_t off) +{ + static char buffer[KI_NGROUPS * 32]; + + buffer[0] = '\0'; + + for (int i = 0; i < kp->p_ngroups; ++i) { + char tmp[32]; + snprintf(tmp, sizeof (tmp), "%u\n", kp->p_groups[i]); + strlcat(buffer, tmp, sizeof (buffer)); + } + return copy_str2(buffer, buf, size, off); +} + +static int +read_status(struct kinfo_proc *kp, char *buf, size_t size, off_t off) +{ + const char *s; + switch (kp->p_stat) { + case SIDL: s = "idle"; break; + case SRUN: s = "runnable"; break; + case SSLEEP: s = "sleeping"; break; + case SSTOP: s = "stopped"; break; + case SZOMB: s = "zomb"; break; // unused + case SDEAD: s = "zombie"; break; + case SONPROC: s = "running"; break; + default: s = "unknown"; break; + } + return copy_str2(s, buf, size, off); +} + +static int +read_pledge(struct kinfo_proc *kp, char *buf, size_t size, off_t off) +{ + static char buffer[sizeof (pledgenames) / sizeof (*pledgenames) * 16]; + + buffer[0] = '\0'; + + for (int i = 0; pledgenames[i].bits != 0; ++i) { + const uint64_t mask = pledgenames[i].bits; + if ((kp->p_pledge & mask) == mask) { + strlcat(buffer, pledgenames[i].name, sizeof (buffer)); + strlcat(buffer, "\n", sizeof (buffer)); + } + } + + return copy_str2(buffer, buf, size, off); +} + +// TODO: argv, envv, fd + +static struct myfile files[] = { + { .name = "/pid", .read = read_pid, }, + { .name = "/comm", .read = read_comm, }, + { .name = "/name", .read = read_name, }, + { .name = "/emul", .read = read_emul, }, + { .name = "/wmesg", .read = read_wmesg, }, + { .name = "/login", .read = read_login, }, + { .name = "/uid", .read = read_uid, }, + { .name = "/ruid", .read = read_ruid, }, + { .name = "/gid", .read = read_gid, }, + { .name = "/rgid", .read = read_rgid, }, + { .name = "/priority", .read = read_priority, }, + { .name = "/nice", .read = read_nice, }, + { .name = "/vm_rssize", .read = read_vm_rssize, }, + { .name = "/vm_tsize", .read = read_vm_tsize, }, + { .name = "/vm_dsize", .read = read_vm_dsize, }, + { .name = "/vm_ssize", .read = read_vm_ssize, }, + { .name = "/flag", .read = read_flag, }, + { .name = "/groups", .read = read_groups, }, + { .name = "/status", .read = read_status, }, + { .name = "/pledge", .read = read_pledge, }, + { .name = NULL, .read = NULL, }, +}; + +static int +read_fd_type(struct kinfo_file *f, char *buf, size_t size, off_t off) +{ + const char *s; + switch (f->f_type) { + case DTYPE_VNODE: s = "vnode\n"; break; + case DTYPE_SOCKET: s = "socket\n"; break; + case DTYPE_PIPE: s = "pipe\n"; break; + case DTYPE_KQUEUE: s = "kqueue\n"; break; + case DTYPE_DMABUF: s = "dmabuf\n"; break; + case DTYPE_SYNC: s = "sync\n"; break; + } + return copy_str2(s, buf, size, off); +} +def_uint(struct kinfo_file *f, read_fd_fd, f->fd_fd); +def_uint(struct kinfo_file *f, read_fd_pid, f->p_pid); +def_uint(struct kinfo_file *f, read_fd_uid, f->f_uid); +def_uint(struct kinfo_file *f, read_fd_gid, f->f_gid); +def_str(struct kinfo_file *f, read_fd_comm, f->p_comm, KI_MAXCOMLEN); + +static struct myfdfile fd_files[] = { + { .name = "/type", .read = read_fd_type, }, + { .name = "/fd", .read = read_fd_fd, }, + { .name = "/pid", .read = read_fd_pid, }, + { .name = "/uid", .read = read_fd_uid, }, + { .name = "/gid", .read = read_fd_gid, }, + { .name = "/comm", .read = read_fd_comm, }, + { .name = NULL, .read = NULL, }, +}; blob - /dev/null blob + 7099c363ed3e95148cd005e7917e3c4463e71ab1 (mode 644) --- /dev/null +++ procfs.c @@ -0,0 +1,641 @@ +#define PLEDGENAMES +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO: envv: not idx, but envar name + +#define MY_KERN_PROC KERN_PROC_KTHREAD + +static kvm_t *kd; +struct myfile { + const char *name; + int(*read)(struct kinfo_proc *, char *, size_t, off_t); +}; +struct myfdfile { + const char *name; + int(*read)(struct kinfo_file *, char *, size_t, off_t); +}; + +#include "files.h" + +enum path_type { + PATH_ROOT, + PATH_PID, + PATH_PID_FILE, + PATH_PID_ARGV, + PATH_PID_ARGV_NUM, + PATH_PID_ENVV, + PATH_PID_ENVV_NAME, + PATH_PID_FD, + PATH_PID_FD_NUM, + PATH_PID_FD_NUM_FILE, +}; + +struct path { + enum path_type type; + int pid; + int fd; + int num; + const char *file; +}; + +static int +parse_path(const char *path, struct path *p) +{ + const char *suffix; + int num; + + memset(p, 0, sizeof (*p)); + + if (strcmp(path, "/") == 0) { + p->type = PATH_ROOT; + return 0; + } else if (sscanf(path, "/%d%n", &p->pid, &num) == 1) { + suffix = path + num; + + if (*suffix == '\0') { + p->type = PATH_PID; + return 0; + } else if (strncmp(suffix, "/argv", 5) == 0) { + suffix += 5; + if (*path == '\0') { + p->type = PATH_PID_ARGV; + return 0; + } else if (sscanf(suffix, "/%d%n", &p->num, &num) == 1) { + p->type = PATH_PID_ARGV_NUM; + return 0; + } else { + return -ENOENT; + } + } else if (strncmp(suffix, "/envv", 5) == 0) { + suffix += 5; + if (*path == '\0') { + p->type = PATH_PID_ENVV; + return 0; + } else if (strchr(suffix + 1, '/') == NULL) { + p->type = PATH_PID_ENVV_NAME; + p->file = suffix + 1; + return 0; + } else { + return -ENOENT; + } + } else if (strncmp(suffix, "/fd", 3) == 0) { + suffix += 3; + if (*path == '\0') { + p->type = PATH_PID_FD; + return 0; + } else if (sscanf(suffix, "/%d%n", &p->fd, &num) == 1) { + suffix += num; + if (*path == '\0') { + p->type = PATH_PID_FD_NUM; + return 0; + } else { + return -ENOENT; + } + } else { + return -ENOENT; + } + } else { + // TODO + return -ENOENT; + } + } else { + return -ENOENT; + } +} + +static int +fs_readdir(const char *path, void *data, fuse_fill_dir_t filler, + off_t off, struct fuse_file_info *ffi) +{ + int pid, num; + + (void)ffi; + + printf("readdir(%s, off=%u);\n", path, (unsigned int)off); + + if (strcmp(path, "/") == 0) { + struct kinfo_proc *kp; + int nentries; + + kp = kvm_getprocs(kd, MY_KERN_PROC, 0, sizeof(*kp), &nentries); + if (kp == NULL) { + warnx("readdir(/): %s", kvm_geterr(kd)); + return -EIO; + } + + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + for (int i = 0; i < nentries; ++i) { + char str[32]; + snprintf (str, sizeof(str), "%d", kp[i].p_pid); + filler(data, str, NULL, 0); + } + return 0; + } else if (sscanf(path, "/%d%n", &pid, &num) == 1) { + const char *suffix = path + num; + struct kinfo_proc *kp; + unsigned int idx; + int nentries; + + kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(*kp), &nentries); + if (kp == NULL || nentries == 0) { + warnx("readdir(%s): %s", path, kvm_geterr(kd)); + return -ENOENT; + } else if (nentries > 1) { + warnx("readdir(%s): Too many results", path); + return -EIO; + } + + if (strcmp(suffix, "") == 0) { + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + filler(data, "parent", NULL, 0); + filler(data, "argv", NULL, 0); + filler(data, "envv", NULL, 0); + filler(data, "fd", NULL, 0); + for (const struct myfile *f = files; f->name != NULL; ++f) + filler(data, f->name + 1, NULL, 0); + return 0; + } else if (strcmp(suffix, "/argv") == 0) { + char **argv = kvm_getargv(kd, kp, 0); + + if (argv == NULL) { + warnx("readdir(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + for (size_t i = 0; argv[i] != NULL; ++i) { + char buffer[32]; + int n; + + n = snprintf(buffer, sizeof (buffer), "%zu", i); + assert(n < (int)sizeof (buffer)); + + filler(data, buffer, NULL, 0); + } + return 0; + } else if (strcmp(suffix, "/envv") == 0) { + char **envv = kvm_getenvv(kd, kp, 0); + + if (envv == NULL) { + warnx("readdir(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + for (size_t i = 0; envv[i] != NULL; ++i) { + char buffer[32]; + int n; + + n = snprintf(buffer, sizeof (buffer), "%zu", i); + assert(n < (int)sizeof (buffer)); + + filler(data, buffer, NULL, 0); + } + return 0; + } else if (strcmp(suffix, "/fd") == 0) { + struct kinfo_file *files; + + files = kvm_getfiles (kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries); + + if (files == NULL) { + warnx("readdir(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + + printf("nentries = %d\n", nentries); + + for (int i = 0; i < nentries; ++i) { + char buffer[32]; + snprintf(buffer, sizeof (buffer), "%d", i); + filler(data, buffer, NULL, 0); + } + + return 0; + } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) { + struct kinfo_file *files; + + suffix += num; + files = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries); + if (files == NULL) { + warnx("readdir(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + if (idx >= (unsigned int)nentries) + return -ENOENT; + + if (strcmp(suffix, "") == 0) { + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) { + filler(data, f->name + 1, NULL, 0); + } + return 0; + } else { + for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) + return -ENOTDIR; + } + return -ENOENT; + } + } else { + for (const struct myfile *f = files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) + return -ENOTDIR; + } + return -ENOENT; + } + } else { + return -ENOENT; + } +} + +static int +fs_read( + const char *path, + char *buf, + size_t size, + off_t off, + struct fuse_file_info *ffi +) { + int pid, num; + + (void)ffi; + + printf("read(%s);\n", path); + + if (strcmp(path, "/") == 0) { + return -EISDIR; + } else if (sscanf(path, "/%d%n", &pid, &num) == 1) { + const char *suffix = path + num; + struct kinfo_proc *kp; + unsigned int idx; + int nentries; + + kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries); + if (kp == NULL || nentries == 0) { + warnx("read(%s): %s", path, kvm_geterr(kd)); + return -ENOENT; + } else if (nentries > 1) { + warnx("read(%s): Too many results", path); + return -EIO; + } + + if (strcmp(suffix, "") == 0) { + return -EISDIR; + } else if (strcmp(suffix, "/argv") == 0) { + return -EISDIR; + } else if (sscanf(suffix, "/argv/%u%n", &idx, &num) == 1) { + char **argv; + + if (suffix[num] != '\0') + return -ENOENT; + + argv = kvm_getargv(kd, kp, 0); + if (argv == NULL) { + warnx("read(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + for (nentries = 0; argv[nentries] != NULL; ++nentries); + + if (idx > (unsigned int)nentries) + return -ENOENT; + + return copy_str(argv[idx], strlen(argv[idx]), buf, off, size); + } else if (strcmp(suffix, "/envv") == 0) { + return -EISDIR; + } else if (sscanf(suffix, "/envv/%u%n", &idx, &num) == 1) { + char **envv; + + if (suffix[num] != '\0') + return -ENOENT; + + envv = kvm_getenvv(kd, kp, 0); + if (envv == NULL) { + warnx("read(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + for (nentries = 0; envv[nentries] != NULL; ++nentries); + + if (idx > (unsigned int)nentries) + return -ENOENT; + + return copy_str(envv[idx], strlen(envv[idx]), buf, off, size); + } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) { + struct kinfo_file *files; + + suffix += num; + files = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries); + if (files == NULL) { + warnx("read(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + if (idx >= (unsigned int)nentries) + return -ENOENT; + + if (strcmp(suffix, "") == 0) { + return -EISDIR; + } else { + for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) + return f->read(&files[idx], buf, size, off); + } + return -ENOENT; + } + } else { + for (const struct myfile *f = files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) + return f->read(kp, buf, size, off); + } + return -ENOENT; + } + } else { + return -ENOENT; + } +} + +static int +fs_open(const char *path, struct fuse_file_info *ffi) +{ + int pid, num; + + (void)ffi; + + printf("open(%s);\n", path); + + if (strcmp(path, "/") == 0) { + return 0; + } else if (sscanf(path, "/%d%n", &pid, &num) == 1) { + const char *suffix = path + num; + struct kinfo_proc *kp; + unsigned int idx; + int nentries; + bool found; + + kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries); + if (kp == NULL || nentries == 0) { + warnx("open(%s): %s", path, kvm_geterr(kd)); + return -ENOENT; + } else if (nentries > 1) { + warnx("open(%s): Too many results", path); + return -EIO; + } + + if (strcmp(suffix, "/argv") == 0) { + return 0; + } else if (sscanf(suffix, "/argv/%u%n", &idx, &num) == 1) { + if (suffix[num] != '\0') + return -ENOENT; + return 0; + } else if (strcmp(suffix, "/envv") == 0) { + return 0; + } else if (sscanf(suffix, "/envv/%u%n", &idx, &num) == 1) { + if (suffix[num] != '\0') + return -ENOENT; + return 0; + } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) { + suffix += num; + if (strcmp(suffix, "") == 0) { + return 0; + } else { + found = false; + for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) { + found = true; + break; + } + } + if (!found) { + printf("Invalid fd suffix: '%s'\n", suffix); + return -ENOENT; + } + return 0; + } + } else { + found = false; + for (const struct myfile *f = files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) { + found = true; + break; + } + } + if (!found) { + printf("Invalid suffix: '%s'\n", suffix); + return -ENOENT; + } + return 0; + } + } else { + return -ENOENT; + } +} + +static int +fs_getattr(const char *path, struct stat *st) +{ + int pid, num; + + printf("getattr(%s);\n", path); + + st->st_blksize = 512; + + if (strcmp(path, "/") == 0) { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + st->st_size = 512; + return 0; + } else if (sscanf(path, "/%d%n", &pid, &num) == 1) { + const char *suffix = path + num; + struct kinfo_proc *kp; + int nentries; + unsigned int idx; + + kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries); + if (kp == NULL || nentries == 0) { + warnx("getattr(%s): %s", path, kvm_geterr(kd)); + return -ENOENT; + } else if (nentries > 1) { + warnx("getattr(%s): Too many results", path); + return -EIO; + } + + if (strcmp(suffix, "") == 0) { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + st->st_size = 512; + return 0; + } else if (strcmp(suffix, "/parent") == 0) { + st->st_mode = S_IFLNK | 0755; + st->st_nlink = 1; + st->st_size = snprintf(NULL, 0, "%d", kp->p_ppid); + return 0; + } else if (strcmp(suffix, "/argv") == 0) { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + st->st_size = 512; + return 0; + } else if (sscanf(suffix, "/argv/%u%n", &idx, &num) == 1) { + char **argv; + + if (suffix[num] != '\0') + return -ENOENT; + + argv = kvm_getargv(kd, kp, 0); + if (argv == NULL) { + warnx("getattr(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + for (nentries = 0; argv[nentries] != NULL; ++nentries); + if (idx >= (unsigned int)nentries) + return -ENOENT; + + st->st_mode = S_IFREG | 0644; + st->st_nlink = 1; + st->st_size = strlen(argv[idx]); + return 0; + } else if (strcmp(suffix, "/envv") == 0) { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + st->st_size = 512; + return 0; + } else if (sscanf(suffix, "/envv/%u%n", &idx, &num) == 1) { + char **envv; + + if (suffix[num] != '\0') + return -ENOENT; + + envv = kvm_getenvv(kd, kp, 0); + if (envv == NULL) { + warnx("getattr(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + for (nentries = 0; envv[nentries] != NULL; ++nentries); + if (idx >= (unsigned int)nentries) + return -ENOENT; + + st->st_mode = S_IFREG | 0644; + st->st_nlink = 1; + st->st_size = strlen(envv[idx]); + return 0; + } else if (strcmp(suffix, "/fd") == 0) { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + st->st_size = 512; + return 0; + } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) { + struct kinfo_file *files; + + suffix += num; + files = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries); + if (files == NULL) { + warnx("getattr(%s): %s", path, kvm_geterr(kd)); + return -EIO; + } + + if (strcmp(suffix, "") == 0) { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + st->st_size = 512; + return 0; + } else { + for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) { + st->st_mode = S_IFREG | 0644; + st->st_nlink = 1; + st->st_size = 0; + return 0; + } + } + return -ENOENT; + } + } else { + for (const struct myfile *f = files; f->name != NULL; ++f) { + if (strcmp(suffix, f->name) == 0) { + st->st_mode = S_IFREG; + st->st_nlink = 1; + st->st_size = 0; + return 0; + } + } + return -ENOENT; + } + } else { + return -ENOENT; + } +} + +static int +fs_readlink(const char *path, char *buf, size_t size) +{ + int pid, num; + + if (sscanf(path, "/%d%n", &pid, &num) == 1) { + const char *suffix = path + num; + struct kinfo_proc *kp; + int nentries; + + kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries); + if (kp == NULL || nentries == 0) { + warnx("readlink(%s): %s", path, kvm_geterr(kd)); + return -ENOENT; + } else if (nentries > 1) { + warnx("readlink(%s): Too many results", path); + return -EIO; + } + + if (strcmp(suffix, "/parent") == 0) { + snprintf(buf, size, "../%d", kp->p_ppid); + return 0; + } else { + return -EINVAL; + } + } else { + return -ENOENT; + } +} + +struct fuse_operations fsops = { + .readdir = fs_readdir, + .read = fs_read, + .open = fs_open, + .getattr = fs_getattr, + .readlink = fs_readlink, +}; + +int +main(int argc, char **argv) +{ + char errbuf[_POSIX2_LINE_MAX]; + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (kd == NULL) + errx(1, "%s", errbuf); + + return (fuse_main(argc, argv, &fsops, NULL)); +}