Commit | Line | Data |
---|---|---|
5ff67f98 DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
261f5d78 | 7 | #ifndef lint |
5ff67f98 DF |
8 | char copyright[] = |
9 | "@(#) Copyright (c) 1980 Regents of the University of California.\n\ | |
10 | All rights reserved.\n"; | |
11 | #endif not lint | |
12 | ||
13 | #ifndef lint | |
f5a538b6 | 14 | static char sccsid[] = "@(#)savecore.c 5.7 (Berkeley) %G%"; |
5ff67f98 | 15 | #endif not lint |
261f5d78 | 16 | |
d112e9ff | 17 | /* |
97d6da0b | 18 | * savecore |
d112e9ff | 19 | */ |
a3e4f1d7 | 20 | |
d112e9ff BJ |
21 | #include <stdio.h> |
22 | #include <nlist.h> | |
23 | #include <sys/param.h> | |
24 | #include <sys/dir.h> | |
25 | #include <sys/stat.h> | |
ce4fd43b | 26 | #include <sys/time.h> |
865f9d47 SL |
27 | #include <sys/file.h> |
28 | #include <syslog.h> | |
d112e9ff | 29 | |
97d6da0b BJ |
30 | #define DAY (60L*60L*24L) |
31 | #define LEEWAY (3*DAY) | |
32 | ||
33 | #define eq(a,b) (!strcmp(a,b)) | |
261f5d78 | 34 | #ifdef vax |
97d6da0b | 35 | #define ok(number) ((number)&0x7fffffff) |
261f5d78 SL |
36 | #else |
37 | #define ok(number) (number) | |
38 | #endif | |
97d6da0b | 39 | |
a3e4f1d7 | 40 | struct nlist current_nl[] = { /* namelist for currently running system */ |
d112e9ff BJ |
41 | #define X_DUMPDEV 0 |
42 | { "_dumpdev" }, | |
43 | #define X_DUMPLO 1 | |
44 | { "_dumplo" }, | |
45 | #define X_TIME 2 | |
46 | { "_time" }, | |
261f5d78 SL |
47 | #define X_DUMPSIZE 3 |
48 | { "_dumpsize" }, | |
d112e9ff BJ |
49 | #define X_VERSION 4 |
50 | { "_version" }, | |
51 | #define X_PANICSTR 5 | |
52 | { "_panicstr" }, | |
261f5d78 SL |
53 | #define X_DUMPMAG 6 |
54 | { "_dumpmag" }, | |
55 | { "" }, | |
d112e9ff BJ |
56 | }; |
57 | ||
a3e4f1d7 RC |
58 | struct nlist dump_nl[] = { /* name list for dumped system */ |
59 | { "_dumpdev" }, /* entries MUST be the same as */ | |
60 | { "_dumplo" }, /* those in current_nl[] */ | |
61 | { "_time" }, | |
62 | { "_dumpsize" }, | |
63 | { "_version" }, | |
64 | { "_panicstr" }, | |
65 | { "_dumpmag" }, | |
66 | { "" }, | |
67 | }; | |
68 | ||
f6cc4b0d | 69 | char *system; |
97d6da0b BJ |
70 | char *dirname; /* directory to save dumps in */ |
71 | char *ddname; /* name of dump device */ | |
72 | char *find_dev(); | |
73 | dev_t dumpdev; /* dump device */ | |
74 | time_t dumptime; /* time the dump was taken */ | |
75 | int dumplo; /* where dump starts on dumpdev */ | |
261f5d78 SL |
76 | int dumpsize; /* amount of memory dumped */ |
77 | int dumpmag; /* magic number in dump */ | |
97d6da0b BJ |
78 | time_t now; /* current date */ |
79 | char *path(); | |
4d1d39b2 | 80 | char *malloc(); |
97d6da0b BJ |
81 | char *ctime(); |
82 | char vers[80]; | |
83 | char core_vers[80]; | |
84 | char panic_mesg[80]; | |
85 | int panicstr; | |
97d6da0b BJ |
86 | off_t lseek(); |
87 | off_t Lseek(); | |
a3e4f1d7 | 88 | int Verbose; |
d112e9ff BJ |
89 | |
90 | main(argc, argv) | |
97d6da0b BJ |
91 | char **argv; |
92 | int argc; | |
d112e9ff | 93 | { |
865f9d47 SL |
94 | char *cp; |
95 | ||
96 | argc--, argv++; | |
97 | while (argc > 0 && argv[0][0] == '-') { | |
98 | for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { | |
97d6da0b | 99 | |
a3e4f1d7 | 100 | case 'v': |
865f9d47 | 101 | Verbose++; |
a3e4f1d7 | 102 | break; |
865f9d47 | 103 | |
a3e4f1d7 | 104 | default: |
865f9d47 | 105 | usage: |
a3e4f1d7 | 106 | fprintf(stderr, |
865f9d47 | 107 | "usage: savecore [-v] dirname [ system ]\n"); |
a3e4f1d7 RC |
108 | exit(1); |
109 | } | |
865f9d47 | 110 | argc--, argv++; |
d112e9ff | 111 | } |
865f9d47 SL |
112 | if (argc != 1 && argc != 2) |
113 | goto usage; | |
114 | dirname = argv[0]; | |
115 | if (argc == 2) | |
116 | system = argv[1]; | |
a96b688d | 117 | openlog("savecore", LOG_ODELAY, LOG_AUTH); |
865f9d47 SL |
118 | if (access(dirname, W_OK) < 0) { |
119 | syslog(LOG_ERR, "%s: %m", dirname); | |
97d6da0b | 120 | exit(1); |
d112e9ff | 121 | } |
d112e9ff | 122 | read_kmem(); |
d112e9ff BJ |
123 | } |
124 | ||
739955be KM |
125 | dump_exists() |
126 | { | |
127 | register int dumpfd; | |
128 | int word; | |
129 | ||
865f9d47 SL |
130 | dumpfd = Open(ddname, O_RDONLY); |
131 | Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); | |
132 | Read(dumpfd, (char *)&word, sizeof (word)); | |
739955be | 133 | close(dumpfd); |
865f9d47 | 134 | if (Verbose && word != dumpmag) { |
f53aabcb | 135 | printf("dumplo = %d (%d bytes)\n", dumplo/DEV_BSIZE, dumplo); |
a3e4f1d7 RC |
136 | printf("magic number mismatch: %x != %x\n", word, dumpmag); |
137 | } | |
261f5d78 | 138 | return (word == dumpmag); |
739955be KM |
139 | } |
140 | ||
141 | clear_dump() | |
142 | { | |
143 | register int dumpfd; | |
144 | int zero = 0; | |
145 | ||
865f9d47 SL |
146 | dumpfd = Open(ddname, O_WRONLY); |
147 | Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); | |
148 | Write(dumpfd, (char *)&zero, sizeof (zero)); | |
739955be KM |
149 | close(dumpfd); |
150 | } | |
151 | ||
97d6da0b BJ |
152 | char * |
153 | find_dev(dev, type) | |
154 | register dev_t dev; | |
155 | register int type; | |
d112e9ff | 156 | { |
d112e9ff | 157 | struct stat statb; |
f0326588 | 158 | char *dp; |
d112e9ff BJ |
159 | |
160 | strcpy(devname, "/dev/"); | |
97d6da0b | 161 | if (stat(devname, &statb)) { |
d112e9ff | 162 | perror(devname); |
97d6da0b BJ |
163 | continue; |
164 | } | |
165 | if ((statb.st_mode&S_IFMT) != type) | |
166 | continue; | |
167 | if (dev == statb.st_rdev) { | |
4d1d39b2 | 168 | dp = malloc(strlen(devname)+1); |
97d6da0b | 169 | strcpy(dp, devname); |
865f9d47 | 170 | return (dp); |
d112e9ff BJ |
171 | } |
172 | } | |
865f9d47 | 173 | syslog(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev)); |
97d6da0b BJ |
174 | exit(1); |
175 | /*NOTREACHED*/ | |
d112e9ff BJ |
176 | } |
177 | ||
865f9d47 SL |
178 | int cursyms[] = |
179 | { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; | |
180 | int dumpsyms[] = | |
181 | { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; | |
d112e9ff BJ |
182 | read_kmem() |
183 | { | |
d112e9ff | 184 | register char *cp; |
865f9d47 | 185 | FILE *fp; |
a3e4f1d7 | 186 | char *dump_sys; |
865f9d47 | 187 | int kmem, i; |
a3e4f1d7 RC |
188 | |
189 | dump_sys = system ? system : "/vmunix"; | |
a3e4f1d7 RC |
190 | nlist("/vmunix", current_nl); |
191 | nlist(dump_sys, dump_nl); | |
a3e4f1d7 RC |
192 | /* |
193 | * Some names we need for the currently running system, | |
194 | * others for the system that was running when the dump was made. | |
195 | * The values obtained from the current system are used | |
196 | * to look for things in /dev/kmem that cannot be found | |
197 | * in the dump_sys namelist, but are presumed to be the same | |
198 | * (since the disk partitions are probably the same!) | |
199 | */ | |
865f9d47 SL |
200 | for (i = 0; cursyms[i] != -1; i++) |
201 | if (current_nl[cursyms[i]].n_value == 0) { | |
202 | syslog(LOG_ERR, "/vmunix: %s not in namelist", | |
203 | current_nl[cursyms[i]].n_name); | |
204 | exit(1); | |
205 | } | |
206 | for (i = 0; dumpsyms[i] != -1; i++) | |
207 | if (dump_nl[dumpsyms[i]].n_value == 0) { | |
208 | syslog(LOG_ERR, "%s: %s not in namelist", dump_sys, | |
209 | dump_nl[dumpsyms[i]].n_name); | |
210 | exit(1); | |
211 | } | |
212 | kmem = Open("/dev/kmem", O_RDONLY); | |
213 | Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, L_SET); | |
261f5d78 | 214 | Read(kmem, (char *)&dumpdev, sizeof (dumpdev)); |
865f9d47 | 215 | Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, L_SET); |
261f5d78 | 216 | Read(kmem, (char *)&dumplo, sizeof (dumplo)); |
865f9d47 | 217 | Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, L_SET); |
261f5d78 | 218 | Read(kmem, (char *)&dumpmag, sizeof (dumpmag)); |
f53aabcb | 219 | dumplo *= DEV_BSIZE; |
d112e9ff | 220 | ddname = find_dev(dumpdev, S_IFBLK); |
865f9d47 SL |
221 | fp = fdopen(kmem, "r"); |
222 | if (fp == NULL) { | |
223 | syslog(LOG_ERR, "Couldn't fdopen kmem"); | |
97d6da0b | 224 | exit(1); |
d112e9ff | 225 | } |
f6cc4b0d BJ |
226 | if (system) |
227 | return; | |
865f9d47 SL |
228 | fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET); |
229 | fgets(vers, sizeof (vers), fp); | |
d112e9ff | 230 | fclose(fp); |
798fe693 SL |
231 | } |
232 | ||
5638c7c2 SL |
233 | check_kmem() |
234 | { | |
798fe693 SL |
235 | FILE *fp; |
236 | register char *cp; | |
237 | ||
865f9d47 SL |
238 | fp = fopen(ddname, "r"); |
239 | if (fp == NULL) { | |
240 | syslog(LOG_ERR, "%s: %m", ddname); | |
97d6da0b | 241 | exit(1); |
d112e9ff | 242 | } |
865f9d47 SL |
243 | fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET); |
244 | fgets(core_vers, sizeof (core_vers), fp); | |
d112e9ff | 245 | fclose(fp); |
865f9d47 | 246 | if (!eq(vers, core_vers) && system == 0) |
261f5d78 | 247 | fprintf(stderr, |
865f9d47 | 248 | "Warning: vmunix version mismatch:\n\t%sand\n\t%s", |
261f5d78 | 249 | vers, core_vers); |
97d6da0b | 250 | fp = fopen(ddname, "r"); |
865f9d47 SL |
251 | fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); |
252 | fread((char *)&panicstr, sizeof (panicstr), 1, fp); | |
97d6da0b | 253 | if (panicstr) { |
865f9d47 | 254 | fseek(fp, dumplo + ok(panicstr), L_SET); |
d112e9ff BJ |
255 | cp = panic_mesg; |
256 | do | |
257 | *cp = getc(fp); | |
258 | while (*cp++); | |
d112e9ff | 259 | } |
97d6da0b | 260 | fclose(fp); |
f6cc4b0d | 261 | } |
d112e9ff | 262 | |
97d6da0b BJ |
263 | get_crashtime() |
264 | { | |
d112e9ff | 265 | int dumpfd; |
20f89b8d | 266 | time_t clobber = (time_t)0; |
d112e9ff | 267 | |
865f9d47 SL |
268 | dumpfd = Open(ddname, O_RDONLY); |
269 | Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); | |
97d6da0b | 270 | Read(dumpfd, (char *)&dumptime, sizeof dumptime); |
d112e9ff | 271 | close(dumpfd); |
a3e4f1d7 RC |
272 | if (dumptime == 0) { |
273 | if (Verbose) | |
865f9d47 | 274 | printf("Dump time not found.\n"); |
261f5d78 | 275 | return (0); |
a3e4f1d7 | 276 | } |
d112e9ff BJ |
277 | printf("System went down at %s", ctime(&dumptime)); |
278 | if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { | |
865f9d47 | 279 | printf("dump time is unreasonable\n"); |
261f5d78 | 280 | return (0); |
d112e9ff | 281 | } |
261f5d78 | 282 | return (1); |
d112e9ff BJ |
283 | } |
284 | ||
97d6da0b BJ |
285 | char * |
286 | path(file) | |
287 | char *file; | |
d112e9ff | 288 | { |
4d1d39b2 | 289 | register char *cp = malloc(strlen(file) + strlen(dirname) + 2); |
d112e9ff | 290 | |
97d6da0b BJ |
291 | (void) strcpy(cp, dirname); |
292 | (void) strcat(cp, "/"); | |
293 | (void) strcat(cp, file); | |
261f5d78 | 294 | return (cp); |
d112e9ff BJ |
295 | } |
296 | ||
d112e9ff BJ |
297 | check_space() |
298 | { | |
299 | struct stat dsb; | |
300 | register char *ddev; | |
e84afc49 | 301 | int dfd, spacefree; |
798fe693 | 302 | struct fs fs; |
d112e9ff | 303 | |
97d6da0b | 304 | if (stat(dirname, &dsb) < 0) { |
865f9d47 | 305 | syslog(LOG_ERR, "%s: %m", dirname); |
97d6da0b BJ |
306 | exit(1); |
307 | } | |
d112e9ff | 308 | ddev = find_dev(dsb.st_dev, S_IFBLK); |
865f9d47 SL |
309 | dfd = Open(ddev, O_RDONLY); |
310 | Lseek(dfd, (long)(SBLOCK * DEV_BSIZE), L_SET); | |
311 | Read(dfd, (char *)&fs, sizeof (fs)); | |
d112e9ff | 312 | close(dfd); |
865f9d47 SL |
313 | spacefree = freespace(&fs, fs.fs_minfree) * fs.fs_fsize / 1024; |
314 | if (spacefree < read_number("minfree")) { | |
315 | syslog(LOG_WARNING, "Dump omitted, not enough space on device"); | |
97d6da0b | 316 | return (0); |
d112e9ff | 317 | } |
d819dd82 | 318 | if (freespace(&fs, fs.fs_minfree) < 0) |
865f9d47 SL |
319 | syslog(LOG_WARNING, |
320 | "Dump performed, but free space threshold crossed"); | |
97d6da0b | 321 | return (1); |
d112e9ff BJ |
322 | } |
323 | ||
d112e9ff | 324 | read_number(fn) |
97d6da0b | 325 | char *fn; |
d112e9ff BJ |
326 | { |
327 | char lin[80]; | |
328 | register FILE *fp; | |
329 | ||
865f9d47 SL |
330 | fp = fopen(path(fn), "r"); |
331 | if (fp == NULL) | |
261f5d78 | 332 | return (0); |
97d6da0b BJ |
333 | if (fgets(lin, 80, fp) == NULL) { |
334 | fclose(fp); | |
261f5d78 | 335 | return (0); |
d112e9ff | 336 | } |
97d6da0b | 337 | fclose(fp); |
261f5d78 | 338 | return (atoi(lin)); |
d112e9ff BJ |
339 | } |
340 | ||
4d1d39b2 MK |
341 | #define BUFPAGES (256*1024/NBPG) /* 1/4 Mb */ |
342 | ||
d112e9ff BJ |
343 | save_core() |
344 | { | |
345 | register int n; | |
4d1d39b2 | 346 | register char *cp; |
d112e9ff BJ |
347 | register int ifd, ofd, bounds; |
348 | register FILE *fp; | |
349 | ||
4d1d39b2 | 350 | cp = malloc(BUFPAGES*NBPG); |
865f9d47 SL |
351 | if (cp == 0) { |
352 | fprintf(stderr, "savecore: Can't allocate i/o buffer.\n"); | |
4d1d39b2 MK |
353 | return; |
354 | } | |
d112e9ff | 355 | bounds = read_number("bounds"); |
865f9d47 | 356 | ifd = Open(system?system:"/vmunix", O_RDONLY); |
d112e9ff BJ |
357 | while((n = Read(ifd, cp, BUFSIZ)) > 0) |
358 | Write(ofd, cp, n); | |
359 | close(ifd); | |
360 | close(ofd); | |
865f9d47 SL |
361 | ifd = Open(ddname, O_RDONLY); |
362 | Lseek(ifd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); | |
5638c7c2 | 363 | Read(ifd, (char *)&dumpsize, sizeof (dumpsize)); |
798fe693 SL |
364 | sprintf(cp, "vmcore.%d", bounds); |
365 | ofd = Create(path(cp), 0644); | |
865f9d47 | 366 | Lseek(ifd, (off_t)dumplo, L_SET); |
261f5d78 SL |
367 | printf("Saving %d bytes of image in vmcore.%d\n", NBPG*dumpsize, |
368 | bounds); | |
369 | while (dumpsize > 0) { | |
4d1d39b2 | 370 | n = Read(ifd, cp, |
865f9d47 | 371 | (dumpsize > BUFPAGES ? BUFPAGES : dumpsize) * NBPG); |
f53aabcb JB |
372 | if (n == 0) { |
373 | printf("WARNING: core may be incomplete\n"); | |
374 | break; | |
375 | } | |
d112e9ff | 376 | Write(ofd, cp, n); |
261f5d78 | 377 | dumpsize -= n/NBPG; |
d112e9ff BJ |
378 | } |
379 | close(ifd); | |
380 | close(ofd); | |
381 | fp = fopen(path("bounds"), "w"); | |
382 | fprintf(fp, "%d\n", bounds+1); | |
383 | fclose(fp); | |
4d1d39b2 | 384 | free(cp); |
d112e9ff BJ |
385 | } |
386 | ||
97d6da0b BJ |
387 | /* |
388 | * Versions of std routines that exit on error. | |
389 | */ | |
97d6da0b BJ |
390 | Open(name, rw) |
391 | char *name; | |
392 | int rw; | |
393 | { | |
394 | int fd; | |
395 | ||
865f9d47 SL |
396 | fd = open(name, rw); |
397 | if (fd < 0) { | |
398 | syslog(LOG_ERR, "%s: %m", name); | |
97d6da0b BJ |
399 | exit(1); |
400 | } | |
865f9d47 | 401 | return (fd); |
97d6da0b BJ |
402 | } |
403 | ||
404 | Read(fd, buff, size) | |
405 | int fd, size; | |
406 | char *buff; | |
407 | { | |
408 | int ret; | |
409 | ||
865f9d47 SL |
410 | ret = read(fd, buff, size); |
411 | if (ret < 0) { | |
412 | syslog(LOG_ERR, "read: %m"); | |
97d6da0b BJ |
413 | exit(1); |
414 | } | |
865f9d47 | 415 | return (ret); |
97d6da0b BJ |
416 | } |
417 | ||
418 | off_t | |
419 | Lseek(fd, off, flag) | |
420 | int fd, flag; | |
421 | long off; | |
422 | { | |
423 | long ret; | |
424 | ||
865f9d47 SL |
425 | ret = lseek(fd, off, flag); |
426 | if (ret == -1) { | |
427 | syslog(LOG_ERR, "lseek: %m"); | |
97d6da0b BJ |
428 | exit(1); |
429 | } | |
865f9d47 | 430 | return (ret); |
97d6da0b BJ |
431 | } |
432 | ||
433 | Create(file, mode) | |
434 | char *file; | |
435 | int mode; | |
436 | { | |
437 | register int fd; | |
438 | ||
865f9d47 SL |
439 | fd = creat(file, mode); |
440 | if (fd < 0) { | |
441 | syslog(LOG_ERR, "%s: %m", file); | |
97d6da0b BJ |
442 | exit(1); |
443 | } | |
865f9d47 | 444 | return (fd); |
97d6da0b BJ |
445 | } |
446 | ||
447 | Write(fd, buf, size) | |
448 | int fd, size; | |
449 | char *buf; | |
97d6da0b BJ |
450 | { |
451 | ||
452 | if (write(fd, buf, size) < size) { | |
865f9d47 | 453 | syslog(LOG_ERR, "write: %m"); |
97d6da0b BJ |
454 | exit(1); |
455 | } | |
456 | } |