savecore.c revision 47095
1/*-
2 * Copyright (c) 1986, 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1986, 1992, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)savecore.c	8.3 (Berkeley) 1/2/94";
43#endif
44static const char rcsid[] =
45	"$Id: savecore.c,v 1.22 1999/03/12 14:46:00 gallatin Exp $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/stat.h>
50#include <sys/mount.h>
51#include <sys/syslog.h>
52#include <sys/sysctl.h>
53
54#include <vm/vm.h>
55#include <vm/vm_param.h>
56#include <vm/pmap.h>
57
58#include <dirent.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <nlist.h>
62#include <paths.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <unistd.h>
67#include "zopen.h"
68
69#ifdef __alpha__
70#define ok(number) ALPHA_K0SEG_TO_PHYS(number)
71#endif
72
73#ifdef __i386__
74#define ok(number) ((number) - KERNBASE)
75#endif
76
77struct nlist current_nl[] = {	/* Namelist for currently running system. */
78#define X_DUMPLO	0
79	{ "_dumplo" },
80#define X_TIME		1
81	{ "_time_second" },
82#define	X_DUMPSIZE	2
83	{ "_dumpsize" },
84#define X_VERSION	3
85	{ "_version" },
86#define X_PANICSTR	4
87	{ "_panicstr" },
88#define	X_DUMPMAG	5
89	{ "_dumpmag" },
90	{ "" },
91};
92int cursyms[] = { X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
93int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
94
95struct nlist dump_nl[] = {	/* Name list for dumped system. */
96	{ "_dumplo" },		/* Entries MUST be the same as */
97	{ "_time_second" },	/*	those in current_nl[].  */
98	{ "_dumpsize" },
99	{ "_version" },
100	{ "_panicstr" },
101	{ "_dumpmag" },
102	{ "" },
103};
104
105/* Types match kernel declarations. */
106long	dumplo;				/* where dump starts on dumpdev */
107int	dumpmag;			/* magic number in dump */
108int	dumpsize;			/* amount of memory dumped */
109
110char	*kernel;
111char	*dirname;			/* directory to save dumps in */
112char	*ddname;			/* name of dump device */
113dev_t	dumpdev;			/* dump device */
114int	dumpfd;				/* read/write descriptor on block dev */
115time_t	now;				/* current date */
116char	panic_mesg[1024];
117int	panicstr;
118char	vers[1024];
119
120int	clear, compress, force, verbose;	/* flags */
121
122void	 check_kmem __P((void));
123int	 check_space __P((void));
124void	 clear_dump __P((void));
125int	 Create __P((char *, int));
126int	 dump_exists __P((void));
127char	*find_dev __P((dev_t, int));
128int	 get_crashtime __P((void));
129void	 get_dumpsize __P((void));
130void	 kmem_setup __P((void));
131void	 log __P((int, char *, ...));
132void	 Lseek __P((int, off_t, int));
133int	 Open __P((const char *, int rw));
134int	 Read __P((int, void *, int));
135char	*rawname __P((char *s));
136void	 save_core __P((void));
137void	 usage __P((void));
138void	 Write __P((int, void *, int));
139
140int
141main(argc, argv)
142	int argc;
143	char *argv[];
144{
145	int ch;
146
147	openlog("savecore", LOG_PERROR, LOG_DAEMON);
148
149	while ((ch = getopt(argc, argv, "cdfN:vz")) != -1)
150		switch(ch) {
151		case 'c':
152			clear = 1;
153			break;
154		case 'd':		/* Not documented. */
155		case 'v':
156			verbose = 1;
157			break;
158		case 'f':
159			force = 1;
160			break;
161		case 'N':
162			kernel = optarg;
163			break;
164		case 'z':
165			compress = 1;
166			break;
167		case '?':
168		default:
169			usage();
170		}
171	argc -= optind;
172	argv += optind;
173
174	if (!clear) {
175		if (argc != 1 && argc != 2)
176			usage();
177		dirname = argv[0];
178	}
179	if (argc == 2)
180		kernel = argv[1];
181
182	(void)time(&now);
183	kmem_setup();
184
185	if (clear) {
186		clear_dump();
187		exit(0);
188	}
189
190	if (!dump_exists() && !force)
191		exit(1);
192
193	check_kmem();
194
195	if (panicstr)
196		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
197	else
198		syslog(LOG_ALERT, "reboot");
199
200	get_dumpsize();
201
202	if ((!get_crashtime() || !check_space()) && !force)
203		exit(1);
204
205	save_core();
206
207	clear_dump();
208	exit(0);
209}
210
211void
212kmem_setup()
213{
214	FILE *fp;
215	int kmem, i;
216	const char *dump_sys;
217	int mib[2];
218	size_t len;
219
220	/*
221	 * Some names we need for the currently running system, others for
222	 * the system that was running when the dump was made.  The values
223	 * obtained from the current system are used to look for things in
224	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
225	 * presumed to be the same (since the disk partitions are probably
226	 * the same!)
227	 */
228	if ((nlist(getbootfile(), current_nl)) == -1)
229		syslog(LOG_ERR, "%s: nlist: %s", getbootfile(),
230		       strerror(errno));
231	for (i = 0; cursyms[i] != -1; i++)
232		if (current_nl[cursyms[i]].n_value == 0) {
233			syslog(LOG_ERR, "%s: %s not in namelist",
234			    getbootfile(), current_nl[cursyms[i]].n_name);
235			exit(1);
236		}
237
238	dump_sys = kernel ? kernel : getbootfile();
239	if ((nlist(dump_sys, dump_nl)) == -1)
240		syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno));
241	for (i = 0; dumpsyms[i] != -1; i++)
242		if (dump_nl[dumpsyms[i]].n_value == 0) {
243			syslog(LOG_ERR, "%s: %s not in namelist",
244			    dump_sys, dump_nl[dumpsyms[i]].n_name);
245			exit(1);
246		}
247
248	mib[0] = CTL_KERN;
249	mib[1] = KERN_DUMPDEV;
250	len = sizeof dumpdev;
251	if (sysctl(mib, 2, &dumpdev, &len, NULL, 0) == -1) {
252		syslog(LOG_ERR, "sysctl: kern.dumpdev: %m");
253		exit(1);
254	}
255	if (dumpdev == NODEV) {
256		syslog(LOG_WARNING, "no core dump (no dumpdev)");
257		exit(1);
258	}
259
260	kmem = Open(_PATH_KMEM, O_RDONLY);
261	Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
262	(void)Read(kmem, &dumplo, sizeof(dumplo));
263	if (verbose)
264		(void)printf("dumplo = %ld (%ld * %d)\n",
265		    dumplo, dumplo/DEV_BSIZE, DEV_BSIZE);
266	Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
267	(void)Read(kmem, &dumpmag, sizeof(dumpmag));
268	dumplo *= DEV_BSIZE;
269	ddname = find_dev(dumpdev, S_IFBLK);
270	dumpfd = Open(ddname, O_RDWR);
271	fp = fdopen(kmem, "r");
272	if (fp == NULL) {
273		syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM);
274		exit(1);
275	}
276	if (kernel)
277		return;
278	(void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET);
279	(void)fgets(vers, sizeof(vers), fp);
280
281	/* Don't fclose(fp), we use dumpfd later. */
282}
283
284void
285check_kmem()
286{
287	register char *cp;
288	FILE *fp;
289	char core_vers[1024];
290
291	fp = fdopen(dumpfd, "r");
292	if (fp == NULL) {
293		syslog(LOG_ERR, "%s: fdopen: %m", ddname);
294		exit(1);
295	}
296	fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
297	fgets(core_vers, sizeof(core_vers), fp);
298	if (strcmp(vers, core_vers) && kernel == 0)
299		syslog(LOG_WARNING,
300		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
301		    getbootfile(), vers, core_vers);
302	(void)fseek(fp,
303	    (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
304	(void)fread(&panicstr, sizeof(panicstr), 1, fp);
305	if (panicstr) {
306		(void)fseek(fp, dumplo + ok(panicstr), L_SET);
307		cp = panic_mesg;
308		do
309			*cp = getc(fp);
310		while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]);
311	}
312	/* Don't fclose(fp), we use dumpfd later. */
313}
314
315void
316clear_dump()
317{
318	long newdumplo;
319
320	newdumplo = 0;
321	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
322	Write(dumpfd, &newdumplo, sizeof(newdumplo));
323}
324
325int
326dump_exists()
327{
328	int newdumpmag;
329
330	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
331	(void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag));
332	if (newdumpmag != dumpmag) {
333		if (verbose)
334			syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
335			    newdumpmag, dumpmag);
336		syslog(LOG_WARNING, "no core dump");
337		return (0);
338	}
339	return (1);
340}
341
342char buf[1024 * 1024];
343
344void
345save_core()
346{
347	register FILE *fp;
348	register int bounds, ifd, nr, nw, ofd;
349	char *rawp, path[MAXPATHLEN];
350	mode_t oumask;
351
352	/*
353	 * Get the current number and update the bounds file.  Do the update
354	 * now, because may fail later and don't want to overwrite anything.
355	 */
356	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
357	if ((fp = fopen(path, "r")) == NULL)
358		goto err1;
359	if (fgets(buf, sizeof(buf), fp) == NULL) {
360		if (ferror(fp))
361err1:			syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
362		bounds = 0;
363	} else
364		bounds = atoi(buf);
365	if (fp != NULL)
366		(void)fclose(fp);
367	if ((fp = fopen(path, "w")) == NULL)
368		syslog(LOG_ERR, "%s: %m", path);
369	else {
370		(void)fprintf(fp, "%d\n", bounds + 1);
371		(void)fclose(fp);
372	}
373
374	/* Create the core file. */
375	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
376	(void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
377	    dirname, bounds, compress ? ".Z" : "");
378	if (compress) {
379		if ((fp = zopen(path, "w", 0)) == NULL) {
380			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
381			exit(1);
382		}
383	} else
384		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
385	(void)umask(oumask);
386
387	/* Open the raw device. */
388	rawp = rawname(ddname);
389	if ((ifd = open(rawp, O_RDONLY)) == -1) {
390		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
391		ifd = dumpfd;
392	}
393
394	/* Seek to the start of the core. */
395	Lseek(ifd, (off_t)dumplo, L_SET);
396
397	/* Copy the core file. */
398	syslog(LOG_NOTICE, "writing %score to %s",
399	    compress ? "compressed " : "", path);
400	for (; dumpsize > 0; dumpsize -= nr) {
401		(void)printf("%6dK\r", dumpsize / 1024);
402		(void)fflush(stdout);
403		nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
404		if (nr <= 0) {
405			if (nr == 0)
406				syslog(LOG_WARNING,
407				    "WARNING: EOF on dump device");
408			else
409				syslog(LOG_ERR, "%s: %m", rawp);
410			goto err2;
411		}
412		if (compress)
413			nw = fwrite(buf, 1, nr, fp);
414		else
415			nw = write(ofd, buf, nr);
416		if (nw != nr) {
417			syslog(LOG_ERR, "%s: %s",
418			    path, strerror(nw == 0 ? EIO : errno));
419err2:			syslog(LOG_WARNING,
420			    "WARNING: vmcore may be incomplete");
421			(void)printf("\n");
422			exit(1);
423		}
424	}
425	(void)close(ifd);
426	if (compress)
427		(void)fclose(fp);
428	else
429		(void)close(ofd);
430
431	/* Copy the kernel. */
432	ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY);
433	(void)snprintf(path, sizeof(path), "%s/kernel.%d%s",
434	    dirname, bounds, compress ? ".Z" : "");
435	if (compress) {
436		if ((fp = zopen(path, "w", 0)) == NULL) {
437			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
438			exit(1);
439		}
440	} else
441		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
442	syslog(LOG_NOTICE, "writing %skernel to %s",
443	    compress ? "compressed " : "", path);
444	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
445		if (compress)
446			nw = fwrite(buf, 1, nr, fp);
447		else
448			nw = write(ofd, buf, nr);
449		if (nw != nr) {
450			syslog(LOG_ERR, "%s: %s",
451			    path, strerror(nw == 0 ? EIO : errno));
452			syslog(LOG_WARNING,
453			    "WARNING: kernel may be incomplete");
454			exit(1);
455		}
456	}
457	if (nr < 0) {
458		syslog(LOG_ERR, "%s: %s",
459		    kernel ? kernel : getbootfile(), strerror(errno));
460		syslog(LOG_WARNING,
461		    "WARNING: kernel may be incomplete");
462		exit(1);
463	}
464	if (compress)
465		(void)fclose(fp);
466	else
467		(void)close(ofd);
468}
469
470char *
471find_dev(dev, type)
472	register dev_t dev;
473	register int type;
474{
475	register DIR *dfd;
476	struct dirent *dir;
477	struct stat sb;
478	char *dp, devname[MAXPATHLEN + 1];
479
480	if ((dfd = opendir(_PATH_DEV)) == NULL) {
481		syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
482		exit(1);
483	}
484	(void)strcpy(devname, _PATH_DEV);
485	while ((dir = readdir(dfd))) {
486		(void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
487		if (lstat(devname, &sb)) {
488			syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
489			continue;
490		}
491		if ((sb.st_mode & S_IFMT) != type)
492			continue;
493		if (dev == sb.st_rdev) {
494			closedir(dfd);
495			if ((dp = strdup(devname)) == NULL) {
496				syslog(LOG_ERR, "%s", strerror(errno));
497				exit(1);
498			}
499			return (dp);
500		}
501	}
502	closedir(dfd);
503	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
504	exit(1);
505}
506
507char *
508rawname(s)
509	char *s;
510{
511	char *sl, name[MAXPATHLEN];
512
513	if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') {
514		syslog(LOG_ERR,
515		    "can't make raw dump device name from %s", s);
516		return (s);
517	}
518	(void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1);
519	if ((sl = strdup(name)) == NULL) {
520		syslog(LOG_ERR, "%s", strerror(errno));
521		exit(1);
522	}
523	return (sl);
524}
525
526int
527get_crashtime()
528{
529	time_t dumptime;			/* Time the dump was taken. */
530
531	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
532	(void)Read(dumpfd, &dumptime, sizeof(dumptime));
533	if (dumptime == 0) {
534		if (verbose)
535			syslog(LOG_ERR, "dump time is zero");
536		return (0);
537	}
538	(void)printf("savecore: system went down at %s", ctime(&dumptime));
539#define	LEEWAY	(7 * 86400)
540	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
541		(void)printf("dump time is unreasonable\n");
542		return (0);
543	}
544	return (1);
545}
546
547void
548get_dumpsize()
549{
550	/* Read the dump size. */
551	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
552	(void)Read(dumpfd, &dumpsize, sizeof(dumpsize));
553	dumpsize *= getpagesize();
554}
555
556int
557check_space()
558{
559	register FILE *fp;
560	const char *tkernel;
561	off_t minfree, spacefree, totfree, kernelsize, needed;
562	struct stat st;
563	struct statfs fsbuf;
564	char buf[100], path[MAXPATHLEN];
565
566	tkernel = kernel ? kernel : getbootfile();
567	if (stat(tkernel, &st) < 0) {
568		syslog(LOG_ERR, "%s: %m", tkernel);
569		exit(1);
570	}
571	kernelsize = st.st_blocks * S_BLKSIZE;
572
573	if (statfs(dirname, &fsbuf) < 0) {
574		syslog(LOG_ERR, "%s: %m", dirname);
575		exit(1);
576	}
577 	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
578	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
579
580	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
581	if ((fp = fopen(path, "r")) == NULL)
582		minfree = 0;
583	else {
584		if (fgets(buf, sizeof(buf), fp) == NULL)
585			minfree = 0;
586		else
587			minfree = atoi(buf);
588		(void)fclose(fp);
589	}
590
591	needed = (dumpsize + kernelsize) / 1024;
592 	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
593		syslog(LOG_WARNING,
594		    "no dump, not enough free space on device");
595		return (0);
596	}
597	if (spacefree - needed < 0)
598		syslog(LOG_WARNING,
599		    "dump performed, but free space threshold crossed");
600	return (1);
601}
602
603int
604Open(name, rw)
605	const char *name;
606	int rw;
607{
608	int fd;
609
610	if ((fd = open(name, rw, 0)) < 0) {
611		syslog(LOG_ERR, "%s: %m", name);
612		exit(1);
613	}
614	return (fd);
615}
616
617int
618Read(fd, bp, size)
619	int fd, size;
620	void *bp;
621{
622	int nr;
623
624	nr = read(fd, bp, size);
625	if (nr != size) {
626		syslog(LOG_ERR, "read: %m");
627		exit(1);
628	}
629	return (nr);
630}
631
632void
633Lseek(fd, off, flag)
634	int fd, flag;
635	off_t off;
636{
637	off_t ret;
638
639	ret = lseek(fd, off, flag);
640	if (ret == -1) {
641		syslog(LOG_ERR, "lseek: %m");
642		exit(1);
643	}
644}
645
646int
647Create(file, mode)
648	char *file;
649	int mode;
650{
651	register int fd;
652
653	fd = creat(file, mode);
654	if (fd < 0) {
655		syslog(LOG_ERR, "%s: %m", file);
656		exit(1);
657	}
658	return (fd);
659}
660
661void
662Write(fd, bp, size)
663	int fd, size;
664	void *bp;
665{
666	int n;
667
668	if ((n = write(fd, bp, size)) < size) {
669		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
670		exit(1);
671	}
672}
673
674void
675usage()
676{
677	(void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
678	exit(1);
679}
680