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