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