procdump.c (4276B)
1#include <asm-generic/errno-base.h> 2#include <linux/limits.h> 3#include <sys/stat.h> 4#include <sys/signal.h> 5#include <libgen.h> 6#include <unistd.h> 7#include <pwd.h> 8 9#include <errno.h> 10#include <stdio.h> 11#include <stdarg.h> 12#include <stdlib.h> 13#include <string.h> 14#include <inttypes.h> 15#include <stdint.h> 16#include <stdbool.h> 17 18#define _STR(x) #x 19#define STR(x) _STR(x) 20 21static pid_t pid = -1; 22 23void 24__attribute__((format(printf, 1, 2))) 25die(const char *fmt, ...) 26{ 27 va_list ap; 28 29 va_start(ap, fmt); 30 fprintf(stderr, "procdump: "); 31 vfprintf(stderr, fmt, ap); 32 if (*fmt && fmt[strlen(fmt)-1] == ':') { 33 fputc(' ', stderr); 34 perror(NULL); 35 } else { 36 fputc('\n', stderr); 37 } 38 va_end(ap); 39 exit(1); 40} 41 42void 43dump(const char *dir, pid_t pid) 44{ 45 char *path = malloc(PATH_MAX+1); 46 if (!path) die("malloc:"); 47 48 char *lib = malloc(PATH_MAX+1); 49 if (!lib) die("malloc:"); 50 51 if (setreuid(geteuid(), getuid()) || setregid(getegid(), getgid())) 52 die("setreuid / setregid:"); 53 54 snprintf(path, PATH_MAX+1, "/proc/%i/maps", pid); 55 FILE *maps = fopen(path, "r"); 56 if (!maps) die("fopen %s:", path); 57 58 snprintf(path, PATH_MAX+1, "/proc/%i/mem", pid); 59 FILE *mem = fopen(path, "r"); 60 if (!mem) die("fopen %s:", path); 61 62 if (setreuid(geteuid(), getuid()) || setregid(getegid(), getgid())) 63 die("setreuid / setregid:"); 64 65 snprintf(path, PATH_MAX+1, "%s/mem", dir); 66 if (mkdir(path, 0755) && errno != EEXIST) 67 die("mkdir %s", path); 68 69 snprintf(path, PATH_MAX+1, "%s/maps", dir); 70 if (mkdir(path, 0755) && errno != EEXIST) 71 die("mkdir %s", path); 72 73 ssize_t rc = 0; 74 size_t linelen; 75 char *line = NULL; 76 while ((rc = getline(&line, &linelen, maps)) >= 0) { 77 if (line[strlen(line)-1] == '\n') 78 line[strlen(line)-1] = '\0'; 79 80 uint64_t start, end; 81 char r, w, x, p; 82 uint64_t offset; 83 int n = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"PRIx64 84 " %*02u:%*02u %*u %"STR(PATH_MAX)"s", 85 &start, &end, &r, &w, &x, &p, &offset, lib); 86 if (n != 7 && n != 8) die("Bad line: %s", line); 87 if (n == 7) *lib = '\0'; 88 89 if (end < start) die("Bad size: %s", line); 90 uint64_t size = end - start; 91 92 if (fseek(mem, start, SEEK_SET)) { 93 fprintf(stderr, "procdump: Skipping '%s' at 0x%lx\n", lib, start); 94 continue; 95 } 96 97 snprintf(path, PATH_MAX+1, "%s/mem/%016lx", dir, start); 98 FILE *out = fopen(path, "w"); 99 if (!out) die("fopen %s:", path); 100 char *buf = malloc(size); 101 if (!buf) die("malloc:"); 102 if (fread(buf, size, 1, mem) != 1) 103 fprintf(stderr, "procdump: Skipping '%s' at 0x%lx\n", lib, start); 104 else if (fwrite(buf, size, 1, out) != 1) 105 die("fwrite: %s", errno ? strerror(errno) : "trunc"); 106 free(buf); 107 fclose(out); 108 109 snprintf(path, PATH_MAX+1, "../../%s/mem/%016lx", dir, start); 110 char *from = strdup(path); 111 if (!from) die("strdup:"); 112 snprintf(path, PATH_MAX+1, "%s/maps/%s-%016lx-%c%c%c%c", 113 dir, basename(lib), start, r, w, x, p); 114 unlink(path); 115 if (symlink(from, path)) die("symlink:"); 116 free(from); 117 } 118 free(line); 119 120 fclose(maps); 121 fclose(mem); 122 free(path); 123 free(lib); 124} 125 126void 127unstop(void) 128{ 129 int rc = kill(pid, SIGCONT); 130 if (rc) die("Sending SIGCONT:"); 131} 132 133int 134main(int argc, const char **argv) 135{ 136 const char **arg; 137 char *end; 138 139 if (!argc) return 1; 140 141 const char *dir = NULL; 142 const char *unpriv = NULL; 143 bool stop = false; 144 for (arg = argv + 1; *arg; arg++) { 145 if (!strcmp(*arg, "-s")) { 146 stop = true; 147 } else if (!strcmp(*arg, "-u")) { 148 unpriv = *++arg; 149 } else if (pid < 0) { 150 pid = strtoul(*arg, &end, 10); 151 if (!end || *end || pid > INT32_MAX) 152 die("Bad pid: %s", *arg); 153 } else if (!dir) { 154 dir = *arg; 155 } else { 156 die("Invalid arg: %s", *arg); 157 } 158 } 159 if (pid < 0) die("Usage: procdump [-s] PID [DIR]"); 160 161 if (stop) { 162 int rc = kill(pid, SIGSTOP); 163 if (rc) die("Sending SIGSTOP:"); 164 atexit(unstop); 165 } 166 167 if (unpriv) { 168 struct passwd *pwd = calloc(1, sizeof(struct passwd)); 169 if (!pwd) die("calloc:"); 170 size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); 171 char *buf = malloc(buflen); 172 if (!buf) die("malloc:"); 173 getpwnam_r(unpriv, pwd, buf, buflen, &pwd); 174 if (!pwd) die("getpwnam_r %s:", unpriv); 175 if (setegid(pwd->pw_gid)) die("setegid:"); 176 if (seteuid(pwd->pw_uid)) die("seteuid:"); 177 } 178 179 if (!dir) { 180 dir = "."; 181 } else { 182 if (mkdir(dir, 0755) && errno != EEXIST) 183 die("mkdir %s", dir); 184 } 185 186 dump(dir, pid); 187}