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