Blob


1 #define PLEDGENAMES
2 #include <sys/param.h>
3 #include <sys/sysctl.h>
4 #include <sys/proc.h>
5 #include <sys/file.h>
6 #include <sys/pledge.h>
8 #include <stdbool.h>
9 #include <assert.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <fuse.h>
16 #include <kvm.h>
17 #include <err.h>
19 // TODO: envv: not idx, but envar name
21 #define MY_KERN_PROC KERN_PROC_KTHREAD
23 static kvm_t *kd;
24 struct myfile {
25 const char *name;
26 int(*read)(struct kinfo_proc *, char *, size_t, off_t);
27 };
28 struct myfdfile {
29 const char *name;
30 int(*read)(struct kinfo_file *, char *, size_t, off_t);
31 };
33 #include "files.h"
35 enum path_type {
36 PATH_ROOT,
37 PATH_PID,
38 PATH_PID_FILE,
39 PATH_PID_ARGV,
40 PATH_PID_ARGV_NUM,
41 PATH_PID_ENVV,
42 PATH_PID_ENVV_NAME,
43 PATH_PID_FD,
44 PATH_PID_FD_NUM,
45 PATH_PID_FD_NUM_FILE,
46 };
48 struct path {
49 enum path_type type;
50 int pid;
51 int fd;
52 int num;
53 const char *file;
54 };
56 static int
57 parse_path(const char *path, struct path *p)
58 {
59 const char *suffix;
60 int num;
62 memset(p, 0, sizeof (*p));
64 if (strcmp(path, "/") == 0) {
65 p->type = PATH_ROOT;
66 return 0;
67 } else if (sscanf(path, "/%d%n", &p->pid, &num) == 1) {
68 suffix = path + num;
70 if (*suffix == '\0') {
71 p->type = PATH_PID;
72 return 0;
73 } else if (strncmp(suffix, "/argv", 5) == 0) {
74 suffix += 5;
75 if (*path == '\0') {
76 p->type = PATH_PID_ARGV;
77 return 0;
78 } else if (sscanf(suffix, "/%d%n", &p->num, &num) == 1) {
79 p->type = PATH_PID_ARGV_NUM;
80 return 0;
81 } else {
82 return -ENOENT;
83 }
84 } else if (strncmp(suffix, "/envv", 5) == 0) {
85 suffix += 5;
86 if (*path == '\0') {
87 p->type = PATH_PID_ENVV;
88 return 0;
89 } else if (strchr(suffix + 1, '/') == NULL) {
90 p->type = PATH_PID_ENVV_NAME;
91 p->file = suffix + 1;
92 return 0;
93 } else {
94 return -ENOENT;
95 }
96 } else if (strncmp(suffix, "/fd", 3) == 0) {
97 suffix += 3;
98 if (*path == '\0') {
99 p->type = PATH_PID_FD;
100 return 0;
101 } else if (sscanf(suffix, "/%d%n", &p->fd, &num) == 1) {
102 suffix += num;
103 if (*path == '\0') {
104 p->type = PATH_PID_FD_NUM;
105 return 0;
106 } else {
107 return -ENOENT;
109 } else {
110 return -ENOENT;
112 } else {
113 // TODO
114 return -ENOENT;
116 } else {
117 return -ENOENT;
121 static int
122 fs_readdir(const char *path, void *data, fuse_fill_dir_t filler,
123 off_t off, struct fuse_file_info *ffi)
125 int pid, num;
127 (void)ffi;
129 printf("readdir(%s, off=%u);\n", path, (unsigned int)off);
131 if (strcmp(path, "/") == 0) {
132 struct kinfo_proc *kp;
133 int nentries;
135 kp = kvm_getprocs(kd, MY_KERN_PROC, 0, sizeof(*kp), &nentries);
136 if (kp == NULL) {
137 warnx("readdir(/): %s", kvm_geterr(kd));
138 return -EIO;
141 filler(data, ".", NULL, 0);
142 filler(data, "..", NULL, 0);
143 for (int i = 0; i < nentries; ++i) {
144 char str[32];
145 snprintf (str, sizeof(str), "%d", kp[i].p_pid);
146 filler(data, str, NULL, 0);
148 return 0;
149 } else if (sscanf(path, "/%d%n", &pid, &num) == 1) {
150 const char *suffix = path + num;
151 struct kinfo_proc *kp;
152 unsigned int idx;
153 int nentries;
155 kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(*kp), &nentries);
156 if (kp == NULL || nentries == 0) {
157 warnx("readdir(%s): %s", path, kvm_geterr(kd));
158 return -ENOENT;
159 } else if (nentries > 1) {
160 warnx("readdir(%s): Too many results", path);
161 return -EIO;
164 if (strcmp(suffix, "") == 0) {
165 filler(data, ".", NULL, 0);
166 filler(data, "..", NULL, 0);
167 filler(data, "parent", NULL, 0);
168 filler(data, "argv", NULL, 0);
169 filler(data, "envv", NULL, 0);
170 filler(data, "fd", NULL, 0);
171 for (const struct myfile *f = files; f->name != NULL; ++f)
172 filler(data, f->name + 1, NULL, 0);
173 return 0;
174 } else if (strcmp(suffix, "/argv") == 0) {
175 char **argv = kvm_getargv(kd, kp, 0);
177 if (argv == NULL) {
178 warnx("readdir(%s): %s", path, kvm_geterr(kd));
179 return -EIO;
182 filler(data, ".", NULL, 0);
183 filler(data, "..", NULL, 0);
184 for (size_t i = 0; argv[i] != NULL; ++i) {
185 char buffer[32];
186 int n;
188 n = snprintf(buffer, sizeof (buffer), "%zu", i);
189 assert(n < (int)sizeof (buffer));
191 filler(data, buffer, NULL, 0);
193 return 0;
194 } else if (strcmp(suffix, "/envv") == 0) {
195 char **envv = kvm_getenvv(kd, kp, 0);
197 if (envv == NULL) {
198 warnx("readdir(%s): %s", path, kvm_geterr(kd));
199 return -EIO;
202 filler(data, ".", NULL, 0);
203 filler(data, "..", NULL, 0);
204 for (size_t i = 0; envv[i] != NULL; ++i) {
205 char buffer[32];
206 int n;
208 n = snprintf(buffer, sizeof (buffer), "%zu", i);
209 assert(n < (int)sizeof (buffer));
211 filler(data, buffer, NULL, 0);
213 return 0;
214 } else if (strcmp(suffix, "/fd") == 0) {
215 struct kinfo_file *files;
217 files = kvm_getfiles (kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries);
219 if (files == NULL) {
220 warnx("readdir(%s): %s", path, kvm_geterr(kd));
221 return -EIO;
224 filler(data, ".", NULL, 0);
225 filler(data, "..", NULL, 0);
227 printf("nentries = %d\n", nentries);
229 for (int i = 0; i < nentries; ++i) {
230 char buffer[32];
231 snprintf(buffer, sizeof (buffer), "%d", i);
232 filler(data, buffer, NULL, 0);
235 return 0;
236 } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) {
237 struct kinfo_file *files;
239 suffix += num;
240 files = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries);
241 if (files == NULL) {
242 warnx("readdir(%s): %s", path, kvm_geterr(kd));
243 return -EIO;
246 if (idx >= (unsigned int)nentries)
247 return -ENOENT;
249 if (strcmp(suffix, "") == 0) {
250 filler(data, ".", NULL, 0);
251 filler(data, "..", NULL, 0);
252 for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) {
253 filler(data, f->name + 1, NULL, 0);
255 return 0;
256 } else {
257 for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) {
258 if (strcmp(suffix, f->name) == 0)
259 return -ENOTDIR;
261 return -ENOENT;
263 } else {
264 for (const struct myfile *f = files; f->name != NULL; ++f) {
265 if (strcmp(suffix, f->name) == 0)
266 return -ENOTDIR;
268 return -ENOENT;
270 } else {
271 return -ENOENT;
275 static int
276 fs_read(
277 const char *path,
278 char *buf,
279 size_t size,
280 off_t off,
281 struct fuse_file_info *ffi
282 ) {
283 int pid, num;
285 (void)ffi;
287 printf("read(%s);\n", path);
289 if (strcmp(path, "/") == 0) {
290 return -EISDIR;
291 } else if (sscanf(path, "/%d%n", &pid, &num) == 1) {
292 const char *suffix = path + num;
293 struct kinfo_proc *kp;
294 unsigned int idx;
295 int nentries;
297 kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries);
298 if (kp == NULL || nentries == 0) {
299 warnx("read(%s): %s", path, kvm_geterr(kd));
300 return -ENOENT;
301 } else if (nentries > 1) {
302 warnx("read(%s): Too many results", path);
303 return -EIO;
306 if (strcmp(suffix, "") == 0) {
307 return -EISDIR;
308 } else if (strcmp(suffix, "/argv") == 0) {
309 return -EISDIR;
310 } else if (sscanf(suffix, "/argv/%u%n", &idx, &num) == 1) {
311 char **argv;
313 if (suffix[num] != '\0')
314 return -ENOENT;
316 argv = kvm_getargv(kd, kp, 0);
317 if (argv == NULL) {
318 warnx("read(%s): %s", path, kvm_geterr(kd));
319 return -EIO;
322 for (nentries = 0; argv[nentries] != NULL; ++nentries);
324 if (idx > (unsigned int)nentries)
325 return -ENOENT;
327 return copy_str(argv[idx], strlen(argv[idx]), buf, off, size);
328 } else if (strcmp(suffix, "/envv") == 0) {
329 return -EISDIR;
330 } else if (sscanf(suffix, "/envv/%u%n", &idx, &num) == 1) {
331 char **envv;
333 if (suffix[num] != '\0')
334 return -ENOENT;
336 envv = kvm_getenvv(kd, kp, 0);
337 if (envv == NULL) {
338 warnx("read(%s): %s", path, kvm_geterr(kd));
339 return -EIO;
342 for (nentries = 0; envv[nentries] != NULL; ++nentries);
344 if (idx > (unsigned int)nentries)
345 return -ENOENT;
347 return copy_str(envv[idx], strlen(envv[idx]), buf, off, size);
348 } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) {
349 struct kinfo_file *files;
351 suffix += num;
352 files = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries);
353 if (files == NULL) {
354 warnx("read(%s): %s", path, kvm_geterr(kd));
355 return -EIO;
358 if (idx >= (unsigned int)nentries)
359 return -ENOENT;
361 if (strcmp(suffix, "") == 0) {
362 return -EISDIR;
363 } else {
364 for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) {
365 if (strcmp(suffix, f->name) == 0)
366 return f->read(&files[idx], buf, size, off);
368 return -ENOENT;
370 } else {
371 for (const struct myfile *f = files; f->name != NULL; ++f) {
372 if (strcmp(suffix, f->name) == 0)
373 return f->read(kp, buf, size, off);
375 return -ENOENT;
377 } else {
378 return -ENOENT;
382 static int
383 fs_open(const char *path, struct fuse_file_info *ffi)
385 int pid, num;
387 (void)ffi;
389 printf("open(%s);\n", path);
391 if (strcmp(path, "/") == 0) {
392 return 0;
393 } else if (sscanf(path, "/%d%n", &pid, &num) == 1) {
394 const char *suffix = path + num;
395 struct kinfo_proc *kp;
396 unsigned int idx;
397 int nentries;
398 bool found;
400 kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries);
401 if (kp == NULL || nentries == 0) {
402 warnx("open(%s): %s", path, kvm_geterr(kd));
403 return -ENOENT;
404 } else if (nentries > 1) {
405 warnx("open(%s): Too many results", path);
406 return -EIO;
409 if (strcmp(suffix, "/argv") == 0) {
410 return 0;
411 } else if (sscanf(suffix, "/argv/%u%n", &idx, &num) == 1) {
412 if (suffix[num] != '\0')
413 return -ENOENT;
414 return 0;
415 } else if (strcmp(suffix, "/envv") == 0) {
416 return 0;
417 } else if (sscanf(suffix, "/envv/%u%n", &idx, &num) == 1) {
418 if (suffix[num] != '\0')
419 return -ENOENT;
420 return 0;
421 } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) {
422 suffix += num;
423 if (strcmp(suffix, "") == 0) {
424 return 0;
425 } else {
426 found = false;
427 for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) {
428 if (strcmp(suffix, f->name) == 0) {
429 found = true;
430 break;
433 if (!found) {
434 printf("Invalid fd suffix: '%s'\n", suffix);
435 return -ENOENT;
437 return 0;
439 } else {
440 found = false;
441 for (const struct myfile *f = files; f->name != NULL; ++f) {
442 if (strcmp(suffix, f->name) == 0) {
443 found = true;
444 break;
447 if (!found) {
448 printf("Invalid suffix: '%s'\n", suffix);
449 return -ENOENT;
451 return 0;
453 } else {
454 return -ENOENT;
458 static int
459 fs_getattr(const char *path, struct stat *st)
461 int pid, num;
463 printf("getattr(%s);\n", path);
465 st->st_blksize = 512;
467 if (strcmp(path, "/") == 0) {
468 st->st_mode = S_IFDIR | 0755;
469 st->st_nlink = 2;
470 st->st_size = 512;
471 return 0;
472 } else if (sscanf(path, "/%d%n", &pid, &num) == 1) {
473 const char *suffix = path + num;
474 struct kinfo_proc *kp;
475 int nentries;
476 unsigned int idx;
478 kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries);
479 if (kp == NULL || nentries == 0) {
480 warnx("getattr(%s): %s", path, kvm_geterr(kd));
481 return -ENOENT;
482 } else if (nentries > 1) {
483 warnx("getattr(%s): Too many results", path);
484 return -EIO;
487 if (strcmp(suffix, "") == 0) {
488 st->st_mode = S_IFDIR | 0755;
489 st->st_nlink = 2;
490 st->st_size = 512;
491 return 0;
492 } else if (strcmp(suffix, "/parent") == 0) {
493 st->st_mode = S_IFLNK | 0755;
494 st->st_nlink = 1;
495 st->st_size = snprintf(NULL, 0, "%d", kp->p_ppid);
496 return 0;
497 } else if (strcmp(suffix, "/argv") == 0) {
498 st->st_mode = S_IFDIR | 0755;
499 st->st_nlink = 2;
500 st->st_size = 512;
501 return 0;
502 } else if (sscanf(suffix, "/argv/%u%n", &idx, &num) == 1) {
503 char **argv;
505 if (suffix[num] != '\0')
506 return -ENOENT;
508 argv = kvm_getargv(kd, kp, 0);
509 if (argv == NULL) {
510 warnx("getattr(%s): %s", path, kvm_geterr(kd));
511 return -EIO;
514 for (nentries = 0; argv[nentries] != NULL; ++nentries);
515 if (idx >= (unsigned int)nentries)
516 return -ENOENT;
518 st->st_mode = S_IFREG | 0644;
519 st->st_nlink = 1;
520 st->st_size = strlen(argv[idx]);
521 return 0;
522 } else if (strcmp(suffix, "/envv") == 0) {
523 st->st_mode = S_IFDIR | 0755;
524 st->st_nlink = 2;
525 st->st_size = 512;
526 return 0;
527 } else if (sscanf(suffix, "/envv/%u%n", &idx, &num) == 1) {
528 char **envv;
530 if (suffix[num] != '\0')
531 return -ENOENT;
533 envv = kvm_getenvv(kd, kp, 0);
534 if (envv == NULL) {
535 warnx("getattr(%s): %s", path, kvm_geterr(kd));
536 return -EIO;
539 for (nentries = 0; envv[nentries] != NULL; ++nentries);
540 if (idx >= (unsigned int)nentries)
541 return -ENOENT;
543 st->st_mode = S_IFREG | 0644;
544 st->st_nlink = 1;
545 st->st_size = strlen(envv[idx]);
546 return 0;
547 } else if (strcmp(suffix, "/fd") == 0) {
548 st->st_mode = S_IFDIR | 0755;
549 st->st_nlink = 2;
550 st->st_size = 512;
551 return 0;
552 } else if (sscanf(suffix, "/fd/%u%n", &idx, &num) == 1) {
553 struct kinfo_file *files;
555 suffix += num;
556 files = kvm_getfiles(kd, KERN_FILE_BYPID, pid, sizeof (*files), &nentries);
557 if (files == NULL) {
558 warnx("getattr(%s): %s", path, kvm_geterr(kd));
559 return -EIO;
562 if (strcmp(suffix, "") == 0) {
563 st->st_mode = S_IFDIR | 0755;
564 st->st_nlink = 2;
565 st->st_size = 512;
566 return 0;
567 } else {
568 for (const struct myfdfile *f = fd_files; f->name != NULL; ++f) {
569 if (strcmp(suffix, f->name) == 0) {
570 st->st_mode = S_IFREG | 0644;
571 st->st_nlink = 1;
572 st->st_size = 0;
573 return 0;
576 return -ENOENT;
578 } else {
579 for (const struct myfile *f = files; f->name != NULL; ++f) {
580 if (strcmp(suffix, f->name) == 0) {
581 st->st_mode = S_IFREG;
582 st->st_nlink = 1;
583 st->st_size = 0;
584 return 0;
587 return -ENOENT;
589 } else {
590 return -ENOENT;
594 static int
595 fs_readlink(const char *path, char *buf, size_t size)
597 int pid, num;
599 if (sscanf(path, "/%d%n", &pid, &num) == 1) {
600 const char *suffix = path + num;
601 struct kinfo_proc *kp;
602 int nentries;
604 kp = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof (*kp), &nentries);
605 if (kp == NULL || nentries == 0) {
606 warnx("readlink(%s): %s", path, kvm_geterr(kd));
607 return -ENOENT;
608 } else if (nentries > 1) {
609 warnx("readlink(%s): Too many results", path);
610 return -EIO;
613 if (strcmp(suffix, "/parent") == 0) {
614 snprintf(buf, size, "../%d", kp->p_ppid);
615 return 0;
616 } else {
617 return -EINVAL;
619 } else {
620 return -ENOENT;
624 struct fuse_operations fsops = {
625 .readdir = fs_readdir,
626 .read = fs_read,
627 .open = fs_open,
628 .getattr = fs_getattr,
629 .readlink = fs_readlink,
630 };
632 int
633 main(int argc, char **argv)
635 char errbuf[_POSIX2_LINE_MAX];
636 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
637 if (kd == NULL)
638 errx(1, "%s", errbuf);
640 return (fuse_main(argc, argv, &fsops, NULL));