diff options
Diffstat (limited to 'src/cli')
-rw-r--r-- | src/cli/proc-region-rw.c | 194 | ||||
-rw-r--r-- | src/cli/proc-region-rw.h | 9 |
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, ®ion_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)); |