summaryrefslogtreecommitdiff
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/proc-region-rw.c194
-rw-r--r--src/cli/proc-region-rw.h9
2 files changed, 203 insertions, 0 deletions
diff --git a/src/cli/proc-region-rw.c b/src/cli/proc-region-rw.c
new file mode 100644
index 0000000..fa8f343
--- /dev/null
+++ b/src/cli/proc-region-rw.c
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+
+static void
+usage(const char *argv0)
+{
+ fprintf(stderr, "usage: %s pid map file [offset] [len] < regions\n"
+ " %s pid write file [offset] [len] < regions\n"
+ " %s pid read [offset] [len] < regions\n"
+ " regions must be in /proc/<pid>/maps format", argv0, argv0, argv0);
+ exit(EXIT_FAILURE);
+}
+
+#include "io/io.h"
+
+struct context {
+ void *buf;
+
+ struct {
+ size_t offset, len;
+ bool has_offset, has_len;
+
+ struct {
+ FILE *src;
+ size_t size;
+ } wm; // write/map
+
+ enum {
+ MODE_MAP,
+ MODE_WRITE,
+ MODE_READ
+ } mode;
+ } op;
+
+ struct io io;
+};
+
+static inline void
+context_init(struct context *ctx, size_t argc, const char *argv[])
+{
+ size_t arg = 0;
+ *ctx = (struct context){0};
+
+ {
+ bool m = false, w = false, r = false;
+ const char *mode = argv[arg++];
+ if (!(m = !strcmp(mode, "map")) && !(w = !strcmp(mode, "write")) && !(r = !strcmp(mode, "read")))
+ errx(EXIT_FAILURE, "mode must be write or read");
+
+ ctx->op.mode = (m ? MODE_MAP : (w ? MODE_WRITE : MODE_READ));
+ }
+
+ const char *wmname = NULL;
+ if (ctx->op.mode == MODE_MAP || ctx->op.mode == MODE_WRITE) {
+ if (argc < arg + 1)
+ usage(argv[0]);
+
+ wmname = argv[arg++];
+ }
+
+ if (argc >= arg + 1) {
+ ctx->op.offset = strtoull(argv[arg++], NULL, 10);
+ ctx->op.has_offset = true;
+ }
+
+ if (argc >= arg + 1) {
+ ctx->op.len = strtoull(argv[arg++], NULL, 10);
+ ctx->op.has_len = true;
+ }
+
+ if (wmname) {
+ if (!(ctx->op.wm.src = fopen(wmname, "rb")))
+ err(EXIT_FAILURE, "fopen(%s)", wmname);
+
+ if (fseek(ctx->op.wm.src, 0, SEEK_END) != 0)
+ err(EXIT_FAILURE, "fseek");
+
+ ctx->op.wm.size = ftell(ctx->op.wm.src);
+ }
+}
+
+static void
+context_release(struct context *ctx)
+{
+ if (ctx->op.wm.src)
+ fclose(ctx->op.wm.src);
+
+ free(ctx->buf);
+ *ctx = (struct context){0};
+}
+
+static void
+region_cb(const char *line, void *data)
+{
+ struct context *ctx = data;
+ unsigned long start, end, region_offset;
+ if (sscanf(line, "%lx-%lx %*s %lx", &start, &end, &region_offset) < 3) {
+ warnx("failed to parse mapping:\n%s", line);
+ return;
+ }
+
+ warnx("%s", line);
+ start += ctx->op.offset;
+
+ if (start > end) {
+ warnx("write offset %lx is out of bounds", start);
+ return;
+ }
+
+ region_offset = (ctx->op.mode == MODE_MAP ? region_offset : 0);
+
+ // requested write/read
+ const size_t rlen = (ctx->op.has_len ? ctx->op.len : (ctx->op.mode == MODE_READ ? end - start : ctx->op.wm.size));
+
+ // actual write/read
+ const size_t len = (rlen > end - start ? end - start : rlen);
+
+ if (!len)
+ return;
+
+ if (!(ctx->buf = realloc(ctx->buf, len)))
+ err(EXIT_FAILURE, "realloc");
+
+ if (ctx->op.mode == MODE_MAP || ctx->op.mode == MODE_WRITE) {
+ if (fseek(ctx->op.wm.src, region_offset, SEEK_SET) != 0)
+ err(EXIT_FAILURE, "fseek");
+
+ const size_t rd = fread(ctx->buf, 1, len, ctx->op.wm.src);
+ const size_t wd = ctx->io.write(&ctx->io, ctx->buf, start, rd);
+
+ if (ctx->op.mode == MODE_WRITE) {
+ if (rlen > wd) {
+ warnx("wrote %lu bytes (%lu bytes truncated) to offset 0x%lx", wd, rlen - wd, start);
+ } else {
+ warnx("wrote %lu bytes to offset 0x%lx", wd, start);
+ }
+ } else {
+ if (rlen > wd) {
+ warnx("mapped %lu bytes (%lu bytes truncated) from offset 0x%lx to offset 0x%lx", wd, rlen - wd, region_offset, start);
+ } else {
+ warnx("mapped %lu bytes from offset 0x%lx to offset 0x%lx", wd, region_offset, start);
+ }
+ }
+ } else {
+ const size_t rd = ctx->io.read(&ctx->io, ctx->buf, start, len);
+
+ if (fwrite(ctx->buf, 1, rd, stdout) != rd)
+ err(EXIT_FAILURE, "fwrite");
+ }
+}
+
+static inline void
+for_each_line_in_file(FILE *f, void (*cb)(const char *line, void *data), void *data)
+{
+ char *buffer = NULL;
+ size_t step = 1024, allocated = 0, written = 0, read = 0;
+ do {
+ if (written + read >= allocated && !(buffer = realloc(buffer, (allocated += step) + 1)))
+ err(EXIT_FAILURE, "realloc");
+
+ buffer[(written += read)] = 0;
+
+ size_t ate = 0;
+ for (char *line = buffer, *nl; (nl = strchr(line, '\n')); line = nl + 1) {
+ *nl = 0;
+ cb(line, data);
+ ate += nl + 1 - line;
+ }
+
+ memmove(buffer, buffer + ate, (written = written - ate));
+ } while ((read = fread(buffer + written, 1, allocated - written, f)));
+ free(buffer);
+}
+
+int
+proc_region_rw(int argc, const char *argv[], bool (*io_init)(struct io*, const pid_t))
+{
+ if (argc < 3)
+ usage(argv[0]);
+
+ const pid_t pid = strtoull(argv[1], NULL, 10);
+ struct context ctx;
+ context_init(&ctx, argc - 2, argv + 2);
+
+ if (!io_init(&ctx.io, pid))
+ return EXIT_FAILURE;
+
+ for_each_line_in_file(stdin, region_cb, &ctx);
+ io_release(&ctx.io);
+ context_release(&ctx);
+ return EXIT_SUCCESS;
+}
diff --git a/src/cli/proc-region-rw.h b/src/cli/proc-region-rw.h
new file mode 100644
index 0000000..7ebae67
--- /dev/null
+++ b/src/cli/proc-region-rw.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h> // pid_t
+
+struct io;
+
+int
+proc_region_rw(int argc, const char *argv[], bool (*io_init)(struct io*, const pid_t));