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