Commit | Line | Data |
---|---|---|
d112e9ff | 1 | /* |
f0326588 | 2 | * savecore.c 4.3 81/04/14 |
d112e9ff BJ |
3 | * savecore dirname |
4 | * Written by Michael Toy (UCB) | |
5 | * Program meant to be called from the /etc/rc file for saving the | |
6 | * dump of a crashed system. If the core file has not already been saved | |
7 | * then save it in dirname (if there is at least minfree blocks on the | |
8 | * device the directory is on.) | |
9 | * 1) Make certain "dirname" exists | |
10 | * 2) Get dumpdev and dumplo from vmunix/kmem | |
11 | * 3) Find dump device name get time from core image | |
12 | * 4) Look in "dirname" generate a name se | |
13 | * vmunix.n | |
14 | * vmcore.n | |
15 | * 5) Check in "dirname"/minfree to be certain there is space | |
16 | * 6) Make entry in shutdown log with date and cause of crash | |
17 | */ | |
18 | ||
19 | #include <stdio.h> | |
20 | #include <nlist.h> | |
21 | #include <sys/param.h> | |
22 | #include <sys/dir.h> | |
23 | #include <sys/stat.h> | |
24 | #include <sys/filsys.h> | |
25 | #include <time.h> | |
26 | ||
27 | #define LEEWAY (60L*60L*24L*3L) /* Maximum reasonable dump age diff (3 days )*/ | |
28 | #define eq(a,b) (strcmp(a,b)==0) | |
29 | #define ok(number) (number&0x7fffffff) | |
30 | #define SHUTDOWNLOG "/usr/adm/shutdownlog" | |
31 | #define TRUE (1) | |
32 | #define FALSE (0) | |
33 | ||
34 | struct nlist nl[] = { | |
35 | #define X_DUMPDEV 0 | |
36 | { "_dumpdev" }, | |
37 | #define X_DUMPLO 1 | |
38 | { "_dumplo" }, | |
39 | #define X_TIME 2 | |
40 | { "_time" }, | |
41 | #define X_PHYSMEM 3 | |
42 | { "_physmem" }, | |
43 | #define X_VERSION 4 | |
44 | { "_version" }, | |
45 | #define X_PANICSTR 5 | |
46 | { "_panicstr" }, | |
47 | { 0 }, | |
48 | }; | |
49 | ||
50 | char *dirname; /* Directory to save dumps in */ | |
51 | char *ddname; /* Name of dump device */ | |
52 | char *find_dev(); | |
53 | int minfree; /* Minimum free blocks on device */ | |
54 | dev_t dumpdev; /* Dump device */ | |
55 | time_t dumptime; /* Time the dump was taken */ | |
56 | int dumplo; /* Where dump starts on dumpdev */ | |
57 | int physmem; /* Amount of memory in machine */ | |
58 | time_t now; /* Current date */ | |
59 | char *path(), *malloc(); | |
60 | char vers[80], core_vers[80]; | |
61 | char panic_mesg[80]; | |
62 | int panicstr; | |
63 | int do_the_dump = TRUE; | |
64 | ||
65 | main(argc, argv) | |
66 | char **argv; | |
67 | int argc; | |
68 | { | |
69 | if (argc != 2) | |
70 | { | |
71 | fprintf(stderr, "usage: savecore dirname\n"); | |
72 | exit(1); | |
73 | } | |
74 | dirname = argv[1]; | |
75 | if (access(dirname, 2) < 0) | |
76 | { | |
77 | perror(dirname); | |
78 | exit(4); | |
79 | } | |
80 | /* | |
81 | * Now invoke the local dieties so that things get done | |
82 | */ | |
83 | time(&now); | |
84 | read_kmem(); | |
85 | log_entry(); | |
86 | if (do_the_dump && get_crashtime() && check_space()) | |
87 | save_core(); | |
88 | } | |
89 | ||
90 | /* | |
91 | * find_dev | |
92 | * Lookup a dev in the /dev directory, return the dev name | |
93 | */ | |
94 | ||
95 | char *find_dev(dev, type) | |
96 | register dev_t dev; | |
97 | register int type; | |
98 | { | |
99 | register int dfd = Open("/dev", 0); | |
100 | struct direct dir; | |
101 | struct stat statb; | |
102 | static char devname[DIRSIZ + 1]; | |
f0326588 | 103 | char *dp; |
d112e9ff BJ |
104 | |
105 | strcpy(devname, "/dev/"); | |
106 | while(Read(dfd, &dir, sizeof dir) > 0) | |
107 | { | |
108 | if (dir.d_ino == 0) | |
109 | continue; | |
110 | strncpy(devname + 5, dir.d_name, DIRSIZ); | |
111 | devname[DIRSIZ] = '\0'; | |
112 | if (stat(devname, &statb)) | |
113 | perror(devname); | |
114 | else | |
115 | { | |
116 | if ((statb.st_mode&S_IFMT) != type) | |
117 | continue; | |
118 | if (dev == statb.st_rdev) | |
119 | { | |
120 | close(dfd); | |
f0326588 BJ |
121 | dp = malloc(strlen(devname)+1); |
122 | strcpy(dp, devname); | |
123 | return dp; | |
d112e9ff BJ |
124 | } |
125 | } | |
126 | } | |
127 | close(dfd); | |
128 | fprintf(stderr, "Can't find device %d,%d\n", major(dev), minor(dev)); | |
129 | exit(7); | |
130 | } | |
131 | ||
132 | /* | |
133 | * Open | |
134 | * Open and exit if open fails | |
135 | */ | |
136 | ||
137 | Open(name, rw) | |
138 | char *name; | |
139 | int rw; | |
140 | { | |
141 | int fd; | |
142 | ||
143 | if ((fd = open(name, rw)) < 0) | |
144 | { | |
145 | perror(name); | |
146 | exit(2); | |
147 | } | |
148 | return fd; | |
149 | } | |
150 | ||
151 | /* | |
152 | * Read, like read but checks bad return codes | |
153 | */ | |
154 | ||
155 | Read(fd, buff, size) | |
156 | int fd, size; | |
157 | char *buff; | |
158 | { | |
159 | int ret; | |
160 | ||
161 | if ((ret = read(fd, buff, size)) < 0) | |
162 | { | |
163 | perror("reading"); | |
164 | exit(3); | |
165 | } | |
166 | return ret; | |
167 | } | |
168 | ||
169 | /* | |
170 | * Lseek | |
171 | * A "safe" lseek | |
172 | */ | |
173 | ||
174 | long Lseek(fd, off, flag) | |
175 | int fd, flag; | |
176 | long off; | |
177 | { | |
178 | long ret; | |
179 | ||
180 | if ((ret = lseek(fd, off, flag)) == -1L) | |
181 | { | |
182 | perror("lseek"); | |
183 | exit(5); | |
184 | } | |
185 | return ret; | |
186 | } | |
187 | ||
188 | Create(file, mode) | |
189 | char *file; | |
190 | int mode; | |
191 | { | |
192 | register int fd; | |
193 | ||
194 | if ((fd = creat(file, mode)) < 0) | |
195 | { | |
196 | perror(file); | |
197 | exit(9); | |
198 | } | |
199 | return fd; | |
200 | } | |
201 | ||
202 | Write(fd, buf, size) | |
203 | int fd, size; | |
204 | char *buf; | |
205 | { | |
206 | if (write(fd, buf, size) < size) | |
207 | { | |
208 | perror("Writing"); | |
209 | exit(10); | |
210 | } | |
211 | } | |
212 | ||
213 | /* | |
214 | * Get dumpdev and dumplo from kmem/vmunix | |
215 | */ | |
216 | ||
217 | read_kmem() | |
218 | { | |
219 | int kmem; | |
220 | FILE *fp; | |
221 | register char *cp; | |
222 | ||
223 | nlist("/vmunix", nl); | |
224 | if (nl[X_DUMPDEV].n_value == 0) | |
225 | { | |
226 | fprintf(stderr, "savecore: dumpdev not in namelist\n"); | |
227 | exit(6); | |
228 | } | |
229 | if (nl[X_DUMPLO].n_value == 0) | |
230 | { | |
231 | fprintf(stderr, "savecore: dumplo not in namelist\n"); | |
232 | exit(6); | |
233 | } | |
234 | if (nl[X_TIME].n_value == 0) | |
235 | { | |
236 | fprintf(stderr, "savecore: time not in namelist\n"); | |
237 | exit(6); | |
238 | } | |
239 | if (nl[X_PHYSMEM].n_value == 0) | |
240 | { | |
241 | fprintf("savecore: physmem not in namelist\n"); | |
242 | exit(6); | |
243 | } | |
244 | if (nl[X_VERSION].n_value == 0) | |
245 | { | |
246 | fprintf("savecore: version not in namelist\n"); | |
247 | exit(6); | |
248 | } | |
249 | if (nl[X_PANICSTR].n_value == 0) | |
250 | { | |
251 | fprintf("savecore: panicstr not in namelist\n"); | |
252 | exit(6); | |
253 | } | |
254 | kmem = Open("/dev/kmem", 0); | |
255 | Lseek(kmem, nl[X_DUMPDEV].n_value, 0); | |
256 | Read(kmem, &dumpdev, sizeof dumpdev); | |
257 | Lseek(kmem, nl[X_DUMPLO].n_value, 0); | |
258 | Read(kmem, &dumplo, sizeof dumplo); | |
259 | Lseek(kmem, nl[X_PHYSMEM].n_value, 0); | |
260 | Read(kmem, &physmem, sizeof physmem); | |
261 | Lseek(kmem, nl[X_PANICSTR].n_value, 0); | |
262 | Read(kmem, &panicstr, sizeof panicstr); | |
263 | dumplo *= 512L; | |
264 | ddname = find_dev(dumpdev, S_IFBLK); | |
265 | /* | |
266 | * Check for version mismatch | |
267 | */ | |
268 | if ((fp = fdopen(kmem, "r")) == NULL) | |
269 | { | |
270 | fprintf(stderr, "Couldn't fdopen kmem\n"); | |
271 | exit(11); | |
272 | } | |
273 | fseek(fp, nl[X_VERSION].n_value, 0); | |
274 | fgets(vers, sizeof vers, fp); | |
275 | fclose(fp); | |
276 | if ((fp = fopen(ddname, "r")) == NULL) | |
277 | { | |
278 | perror(ddname); | |
279 | exit(12); | |
280 | } | |
281 | fseek(fp, dumplo+ok(nl[X_VERSION].n_value), 0); | |
282 | fgets(core_vers, sizeof core_vers, fp); | |
283 | fclose(fp); | |
284 | if (!eq(vers, core_vers)) | |
285 | { | |
286 | fprintf(stderr, "Version mismatch:\n\t%sand\n\t%s", | |
287 | vers,core_vers); | |
288 | fprintf(stderr, "Core not saved\n"); | |
289 | do_the_dump = FALSE; | |
290 | return; | |
291 | } | |
292 | /* | |
293 | * Now check the panic string | |
294 | */ | |
295 | if (panicstr) | |
296 | { | |
297 | fp = fopen(ddname, "r"); | |
298 | fseek(fp, dumplo + ok(panicstr)); | |
299 | cp = panic_mesg; | |
300 | do | |
301 | *cp = getc(fp); | |
302 | while (*cp++); | |
303 | fclose(fp); | |
304 | } | |
305 | } | |
306 | ||
307 | /* | |
308 | * Now get the time of the crash | |
309 | */ | |
310 | ||
311 | get_crashtime() | |
312 | { | |
313 | int dumpfd; | |
20f89b8d | 314 | time_t clobber = (time_t)0; |
d112e9ff | 315 | |
20f89b8d | 316 | dumpfd = Open(ddname, 2); |
d112e9ff BJ |
317 | Lseek(dumpfd, dumplo + ok(nl[X_TIME].n_value), 0); |
318 | Read(dumpfd, &dumptime, sizeof dumptime); | |
20f89b8d BJ |
319 | Lseek(dumpfd, dumplo + ok(nl[X_TIME].n_value), 0); |
320 | Write(dumpfd, &clobber, sizeof clobber); | |
d112e9ff | 321 | close(dumpfd); |
f0326588 BJ |
322 | if (dumptime == 0) |
323 | return FALSE; | |
d112e9ff BJ |
324 | printf("System went down at %s", ctime(&dumptime)); |
325 | if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { | |
326 | printf("Dump time is unreasonable\n"); | |
327 | return FALSE; | |
328 | } else { | |
d112e9ff BJ |
329 | return TRUE; |
330 | } | |
331 | } | |
332 | ||
333 | /* | |
334 | * Put a file name in the proper perspective | |
335 | */ | |
336 | ||
337 | char *path(file) | |
338 | { | |
339 | register char *cp = malloc(strlen(file) + strlen(dirname) + 2); | |
340 | ||
341 | strcpy(cp, dirname); | |
342 | strcat(cp, "/"); | |
343 | strcat(cp, file); | |
344 | return cp; | |
345 | } | |
346 | ||
347 | /* | |
348 | * Check to make certain that there is enough space for this dump | |
349 | */ | |
350 | ||
351 | check_space() | |
352 | { | |
353 | struct stat dsb; | |
354 | register char *ddev; | |
355 | register int dfd; | |
356 | struct filsys sblk; | |
357 | ||
358 | /* | |
359 | * First find the number of free blocks | |
360 | */ | |
361 | stat(dirname, &dsb); | |
362 | ddev = find_dev(dsb.st_dev, S_IFBLK); | |
363 | dfd = Open(ddev, 0); | |
364 | Lseek(dfd, 1L<<BSHIFT, 0); | |
365 | Read(dfd, &sblk, sizeof sblk); | |
366 | close(dfd); | |
367 | /* | |
368 | * Now check against maximum allowed | |
369 | */ | |
370 | if (read_number("minfree") > sblk.s_tfree) | |
371 | { | |
372 | fprintf(stderr, "*** Dump not done, not enough space ***\n"); | |
373 | return FALSE; | |
374 | } | |
375 | else | |
376 | return TRUE; | |
377 | } | |
378 | ||
379 | /* | |
380 | * Read a number from a file | |
381 | */ | |
382 | ||
383 | read_number(fn) | |
384 | char *fn; | |
385 | { | |
386 | char lin[80]; | |
387 | register FILE *fp; | |
388 | ||
389 | if ((fp = fopen(path(fn), "r")) == NULL) | |
390 | return 0; | |
391 | else | |
392 | { | |
393 | if (fgets(lin, 80, fp) == NULL) | |
394 | { | |
395 | fclose(fp); | |
396 | return 0; | |
397 | } | |
398 | else | |
399 | { | |
400 | fclose(fp); | |
401 | return atoi(lin); | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | save_core() | |
407 | { | |
408 | register int n; | |
409 | char buffer[BUFSIZ]; | |
410 | register char *cp = buffer; | |
411 | register int ifd, ofd, bounds; | |
412 | register FILE *fp; | |
413 | ||
414 | bounds = read_number("bounds"); | |
415 | /* | |
416 | * Copy the vmunix file | |
417 | */ | |
418 | ifd = Open("/vmunix", 0); | |
419 | ofd = Create(path(sprintf(cp, "vmunix.%d", bounds)), 0666); | |
420 | while((n = Read(ifd, cp, BUFSIZ)) > 0) | |
421 | Write(ofd, cp, n); | |
422 | close(ifd); | |
423 | close(ofd); | |
424 | /* | |
425 | * Make the core file | |
426 | */ | |
427 | ifd = Open(ddname, 0); | |
428 | ofd = Create(path(sprintf(cp, "vmcore.%d", bounds)), 0666); | |
429 | Lseek(ifd, dumplo, 0); | |
f0326588 BJ |
430 | printf("Saving %d bytes of image in vmcore.%d\n", |
431 | NBPG * physmem, bounds); | |
d112e9ff BJ |
432 | while(physmem > 0) |
433 | { | |
434 | n = Read(ifd, cp, BUFSIZ); | |
435 | Write(ofd, cp, n); | |
436 | physmem -= n/NBPG; | |
437 | } | |
438 | close(ifd); | |
439 | close(ofd); | |
440 | fp = fopen(path("bounds"), "w"); | |
441 | fprintf(fp, "%d\n", bounds+1); | |
442 | fclose(fp); | |
443 | } | |
444 | ||
445 | char *days[] = { | |
446 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
447 | }; | |
448 | ||
449 | char *months[] = { | |
450 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", | |
451 | "Oct", "Nov", "Dec" | |
452 | }; | |
453 | ||
454 | log_entry() | |
455 | { | |
456 | FILE *fp; | |
457 | struct tm *tm, *localtime(); | |
458 | ||
459 | tm = localtime(&now); | |
460 | fp = fopen(SHUTDOWNLOG, "a"); | |
461 | fseek(fp, 0L, 2); | |
462 | fprintf(fp, "%02d:%02d %s %s %2d, %4d. Reboot", tm->tm_hour, | |
463 | tm->tm_min, days[tm->tm_wday], months[tm->tm_mon], | |
464 | tm->tm_mday, tm->tm_year + 1900); | |
465 | if (panicstr) | |
466 | fprintf(fp, " -- panic %s\n", panic_mesg); | |
467 | else | |
468 | putc('\n', fp); | |
469 | fclose(fp); | |
470 | } |