From 82eb10b0c7ae9382a4bbc97071c74de9055ad56e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 18 Oct 2018 15:07:39 +0300 Subject: Nicer cli interface Remove some behaviour that isn't clear to user, such as if no offset argument is given, write uses the map offsets from regions to write data from file starting from the region's map offset! now write is explicit and does exactly what you would expect. map is provided for remapping regions, that is providing file (such as shared library usually), and it will write contents of it starting from the region map offset for each given region. Assuming maps has this line: `7f392b87e000-7f392b8b9000 r-xp 00017000 08:01 133842 /usr/lib/libncursesw.so.6.1` And we do this: `proc-region-rw write /usr/lib/libncursesw.so << maps` The contents of /usr/lib/libncursesw.so would be written to 7f392b87e000-7f392b8b9000 (This will most likely cause htop to crash) Now if we do this instead: `proc-region-rw map /usr/lib/libncursesw.so << maps` The contents of /usr/lib/libncursesw.so _starting from offset 000170000_ would be written to 7f392b87e000-7f392b8b9000. Notice the difference, map function uses the offset information from region, to write contents from the input file, starting from the offset in the region. (This should not crash htop.) --- proc-region-rw.c | 104 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 39 deletions(-) (limited to 'proc-region-rw.c') diff --git a/proc-region-rw.c b/proc-region-rw.c index 0c1ca4f..c2a9b13 100644 --- a/proc-region-rw.c +++ b/proc-region-rw.c @@ -11,13 +11,15 @@ struct context { struct { size_t offset, len; - bool with_offset_and_len; + bool has_offset, has_len; struct { FILE *src; - } write; + size_t size; + } wm; // write/map enum { + MODE_MAP, MODE_WRITE, MODE_READ } mode; @@ -32,44 +34,57 @@ struct context { static void usage(const char *argv0) { - fprintf(stderr, "usage: %s pid write file [offset len] < regions\n" - " %s pid read [offset len] < regions\n\n" - " regions must be in /proc//maps format", argv0, 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//maps format", argv0, argv0, argv0); exit(EXIT_FAILURE); } static void -context_init(struct context *ctx, int argc, const char *argv[]) +context_init(struct context *ctx, size_t argc, const char *argv[]) { if (argc < 3) usage(argv[0]); + size_t arg = 1; *ctx = (struct context){0}; - ctx->proc.pid = strtoull(argv[1], NULL, 10); + ctx->proc.pid = strtoull(argv[arg++], NULL, 10); { - bool w, r; - const char *mode = argv[2]; - if ((w = strcmp(mode, "write")) && (r = strcmp(mode, "read"))) + bool w = false, r = false, m = false; + const char *mode = argv[arg++]; + if (!(m = !strcmp(mode, "map")) && !(w = !strcmp(mode, "write")) && !(r = !strcmp(mode, "read"))) err(EXIT_FAILURE, "mode must be write or read"); - ctx->op.mode = (!w ? MODE_WRITE : MODE_READ); + ctx->op.mode = (m ? MODE_MAP : (w ? MODE_WRITE : MODE_READ)); } - if (ctx->op.mode == MODE_WRITE && argc < 4) - usage(argv[0]); - - if (argc > (ctx->op.mode == MODE_WRITE) + 3) { - if (argc < (ctx->op.mode == MODE_WRITE) + 5) + const char *wmname = NULL; + if (ctx->op.mode == MODE_MAP || ctx->op.mode == MODE_WRITE) { + if (argc < arg + 1) usage(argv[0]); - ctx->op.offset = strtoull(argv[(ctx->op.mode == MODE_WRITE) + 3], NULL, 10); - ctx->op.len = strtoull(argv[(ctx->op.mode == MODE_WRITE) + 4], NULL, 10); - ctx->op.with_offset_and_len = true; + 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 (ctx->op.mode == MODE_WRITE && !(ctx->op.write.src = fopen(argv[3], "rb"))) - err(EXIT_FAILURE, "fopen(%s)", argv[3]); + if (wmname && !(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); char path[128]; snprintf(path, sizeof(path), "/proc/%u/mem", ctx->proc.pid); @@ -80,8 +95,8 @@ context_init(struct context *ctx, int argc, const char *argv[]) static void context_release(struct context *ctx) { - if (ctx->op.write.src) - fclose(ctx->op.write.src); + if (ctx->op.wm.src) + fclose(ctx->op.wm.src); fclose(ctx->proc.mem); free(ctx->buf); @@ -112,25 +127,23 @@ static void region_cb(const char *line, void *data) { struct context *ctx = data; - unsigned long start, end, offset; - if (sscanf(line, "%lx-%lx %*s %lx", &start, &end, &offset) < 3) { + 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; - offset = (ctx->op.with_offset_and_len ? 0 : offset); - const size_t len = (ctx->op.with_offset_and_len ? ctx->op.len : end - start); - if (start + ctx->op.offset > end) { - warnx("write offset %lx is out of bounds", start + ctx->op.offset); + if (start > end) { + warnx("write offset %lx is out of bounds", start); return; } - if (len > (end - start - ctx->op.offset)) { - warnx("%zu bytes doesn't fit in the region", len); - return; - } + region_offset = (ctx->op.mode == MODE_MAP ? region_offset : 0); + const size_t rlen = (ctx->op.has_len ? ctx->op.len : ctx->op.wm.size); // requested write/read + const size_t len = (rlen > end - start ? end - start : rlen); // actual write/read if (!len) return; @@ -139,24 +152,37 @@ region_cb(const char *line, void *data) err(EXIT_FAILURE, "realloc"); clearerr(ctx->proc.mem); - if (ctx->op.mode == MODE_WRITE) { - if (fseek(ctx->op.write.src, offset, SEEK_SET) != 0) + 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.write.src); + const size_t rd = fread(ctx->buf, 1, len, ctx->op.wm.src); - if (fseek(ctx->proc.mem, start + ctx->op.offset, SEEK_SET) != 0) + if (fseek(ctx->proc.mem, start, SEEK_SET) != 0) err(EXIT_FAILURE, "fseek"); const size_t wd = fwrite(ctx->buf, 1, rd, ctx->proc.mem); if (ferror(ctx->proc.mem)) { warn("fread(/proc/%u/mem)", ctx->proc.pid); + return; + } + + 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 { - warnx("wrote %lu bytes from offset 0x%lx to offset 0x%lx", wd, offset, start + ctx->op.offset); + 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 { - if (fseek(ctx->proc.mem, start + ctx->op.offset, SEEK_SET) != 0) + if (fseek(ctx->proc.mem, start, SEEK_SET) != 0) err(EXIT_FAILURE, "fseek"); const size_t rd = fread(ctx->buf, 1, len, ctx->proc.mem); -- cgit v1.2.3