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