fix botch in |prog form
[unix-history] / usr / src / sbin / savecore / savecore.c
CommitLineData
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
34struct 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
50char *dirname; /* Directory to save dumps in */
51char *ddname; /* Name of dump device */
52char *find_dev();
53int minfree; /* Minimum free blocks on device */
54dev_t dumpdev; /* Dump device */
55time_t dumptime; /* Time the dump was taken */
56int dumplo; /* Where dump starts on dumpdev */
57int physmem; /* Amount of memory in machine */
58time_t now; /* Current date */
59char *path(), *malloc();
60char vers[80], core_vers[80];
61char panic_mesg[80];
62int panicstr;
63int do_the_dump = TRUE;
64
65main(argc, argv)
66char **argv;
67int 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
95char *find_dev(dev, type)
96register dev_t dev;
97register 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
137Open(name, rw)
138char *name;
139int 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
155Read(fd, buff, size)
156int fd, size;
157char *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
174long Lseek(fd, off, flag)
175int fd, flag;
176long 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
188Create(file, mode)
189char *file;
190int 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
202Write(fd, buf, size)
203int fd, size;
204char *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
217read_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
337char *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
351check_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
383read_number(fn)
384char *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
406save_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
445char *days[] = {
446 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
447};
448
449char *months[] = {
450 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
451 "Oct", "Nov", "Dec"
452};
453
454log_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}