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