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