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