commit d9d200dbe38f62844e5ddb885e1778a888669c18
Author: Louis Burda <quent.burda@gmail.com>
Date: Fri, 10 May 2024 05:10:00 +0200
Add initial version
Diffstat:
A | .gitignore | | | 1 | + |
A | Makefile | | | 24 | ++++++++++++++++++++++++ |
A | procdump.c | | | 187 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 212 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+procdump
diff --git a/Makefile b/Makefile
@@ -0,0 +1,24 @@
+PREFIX ?= /usr/local
+BINDIR ?= /bin
+
+ifeq ($(DEBUG),1)
+CFLAGS = -Og -g
+else
+CFLAGS = -O3
+endif
+
+all: procdump
+
+procdump: procdump.c
+ gcc -o $@ $< $(CFLAGS)
+
+clean:
+ rm -f procdump
+
+install:
+ install -m755 procdump -t "$(DESTDIR)$(PREFIX)$(BINDIR)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(PREFIX)$(BINDIR)/procdump"
+
+.PHONY: all install uninstall
diff --git a/procdump.c b/procdump.c
@@ -0,0 +1,187 @@
+#include <asm-generic/errno-base.h>
+#include <linux/limits.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+static pid_t pid = -1;
+
+void
+__attribute__((format(printf, 1, 2)))
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "procdump: ");
+ vfprintf(stderr, fmt, ap);
+ if (*fmt && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+ va_end(ap);
+ exit(1);
+}
+
+void
+dump(const char *dir, pid_t pid)
+{
+ char *path = malloc(PATH_MAX+1);
+ if (!path) die("malloc:");
+
+ char *lib = malloc(PATH_MAX+1);
+ if (!lib) die("malloc:");
+
+ if (setreuid(geteuid(), getuid()) || setregid(getegid(), getgid()))
+ die("setreuid / setregid:");
+
+ snprintf(path, PATH_MAX+1, "/proc/%i/maps", pid);
+ FILE *maps = fopen(path, "r");
+ if (!maps) die("fopen %s:", path);
+
+ snprintf(path, PATH_MAX+1, "/proc/%i/mem", pid);
+ FILE *mem = fopen(path, "r");
+ if (!mem) die("fopen %s:", path);
+
+ if (setreuid(geteuid(), getuid()) || setregid(getegid(), getgid()))
+ die("setreuid / setregid:");
+
+ snprintf(path, PATH_MAX+1, "%s/mem", dir);
+ if (mkdir(path, 0755) && errno != EEXIST)
+ die("mkdir %s", path);
+
+ snprintf(path, PATH_MAX+1, "%s/maps", dir);
+ if (mkdir(path, 0755) && errno != EEXIST)
+ die("mkdir %s", path);
+
+ ssize_t rc = 0;
+ size_t linelen;
+ char *line = NULL;
+ while ((rc = getline(&line, &linelen, maps)) >= 0) {
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ uint64_t start, end;
+ char r, w, x, p;
+ uint64_t offset;
+ int n = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"PRIx64
+ " %*02u:%*02u %*u %"STR(PATH_MAX)"s",
+ &start, &end, &r, &w, &x, &p, &offset, lib);
+ if (n != 7 && n != 8) die("Bad line: %s", line);
+ if (n == 7) *lib = '\0';
+
+ if (end < start) die("Bad size: %s", line);
+ uint64_t size = end - start;
+
+ if (fseek(mem, start, SEEK_SET)) {
+ fprintf(stderr, "procdump: Skipping '%s' at 0x%lx\n", lib, start);
+ continue;
+ }
+
+ snprintf(path, PATH_MAX+1, "%s/mem/%016lx", dir, start);
+ FILE *out = fopen(path, "w");
+ if (!out) die("fopen %s:", path);
+ char *buf = malloc(size);
+ if (!buf) die("malloc:");
+ if (fread(buf, size, 1, mem) != 1)
+ fprintf(stderr, "procdump: Skipping '%s' at 0x%lx\n", lib, start);
+ else if (fwrite(buf, size, 1, out) != 1)
+ die("fwrite: %s", errno ? strerror(errno) : "trunc");
+ free(buf);
+ fclose(out);
+
+ snprintf(path, PATH_MAX+1, "../../%s/mem/%016lx", dir, start);
+ char *from = strdup(path);
+ if (!from) die("strdup:");
+ snprintf(path, PATH_MAX+1, "%s/maps/%s-%016lx-%c%c%c%c",
+ dir, basename(lib), start, r, w, x, p);
+ unlink(path);
+ if (symlink(from, path)) die("symlink:");
+ free(from);
+ }
+ free(line);
+
+ fclose(maps);
+ fclose(mem);
+ free(path);
+ free(lib);
+}
+
+void
+unstop(void)
+{
+ int rc = kill(pid, SIGCONT);
+ if (rc) die("Sending SIGCONT:");
+}
+
+int
+main(int argc, const char **argv)
+{
+ const char **arg;
+ char *end;
+
+ if (!argc) return 1;
+
+ const char *dir = NULL;
+ const char *unpriv = NULL;
+ bool stop = false;
+ for (arg = argv + 1; *arg; arg++) {
+ if (!strcmp(*arg, "-s")) {
+ stop = true;
+ } else if (!strcmp(*arg, "-u")) {
+ unpriv = *++arg;
+ } else if (pid < 0) {
+ pid = strtoul(*arg, &end, 10);
+ if (!end || *end || pid > INT32_MAX)
+ die("Bad pid: %s", *arg);
+ } else if (!dir) {
+ dir = *arg;
+ } else {
+ die("Invalid arg: %s", *arg);
+ }
+ }
+ if (pid < 0) die("Usage: procdump [-s] PID [DIR]");
+
+ if (stop) {
+ int rc = kill(pid, SIGSTOP);
+ if (rc) die("Sending SIGSTOP:");
+ atexit(unstop);
+ }
+
+ if (unpriv) {
+ struct passwd *pwd = calloc(1, sizeof(struct passwd));
+ if (!pwd) die("calloc:");
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char *buf = malloc(buflen);
+ if (!buf) die("malloc:");
+ getpwnam_r(unpriv, pwd, buf, buflen, &pwd);
+ if (!pwd) die("getpwnam_r %s:", unpriv);
+ if (setegid(pwd->pw_gid)) die("setegid:");
+ if (seteuid(pwd->pw_uid)) die("seteuid:");
+ }
+
+ if (!dir) {
+ dir = ".";
+ } else {
+ if (mkdir(dir, 0755) && errno != EEXIST)
+ die("mkdir %s", dir);
+ }
+
+ dump(dir, pid);
+}