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