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