savecore.c revision 1.13 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
35 static 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 static char sccsid[] = "@(#)savecore.c 8.3 (Berkeley) 1/2/94";
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <sys/mount.h>
47 #include <sys/syslog.h>
48 #include <sys/time.h>
49
50 #include <dirent.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <nlist.h>
54 #include <paths.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <tzfile.h>
59 #include <unistd.h>
60
61 #define ok(number) ((number) - KERNBASE)
62
63 struct nlist current_nl[] = { /* Namelist for currently running system. */
64 #define X_DUMPDEV 0
65 { "_dumpdev" },
66 #define X_DUMPLO 1
67 { "_dumplo" },
68 #define X_TIME 2
69 { "_time" },
70 #define X_DUMPSIZE 3
71 { "_dumpsize" },
72 #define X_VERSION 4
73 { "_version" },
74 #define X_PANICSTR 5
75 { "_panicstr" },
76 #define X_DUMPMAG 6
77 { "_dumpmag" },
78 { "" },
79 };
80 int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
81 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
82
83 struct nlist dump_nl[] = { /* Name list for dumped system. */
84 { "_dumpdev" }, /* Entries MUST be the same as */
85 { "_dumplo" }, /* those in current_nl[]. */
86 { "_time" },
87 { "_dumpsize" },
88 { "_version" },
89 { "_panicstr" },
90 { "_dumpmag" },
91 { "" },
92 };
93
94 /* Types match kernel declarations. */
95 long dumplo; /* where dump starts on dumpdev */
96 int dumpmag; /* magic number in dump */
97 int dumpsize; /* amount of memory dumped */
98
99 char *vmunix;
100 char *dirname; /* directory to save dumps in */
101 char *ddname; /* name of dump device */
102 dev_t dumpdev; /* dump device */
103 int dumpfd; /* read/write descriptor on block dev */
104 time_t now; /* current date */
105 char panic_mesg[1024];
106 int panicstr;
107 char vers[1024];
108
109 int clear, compress, force, verbose; /* flags */
110
111 void check_kmem __P((void));
112 int check_space __P((void));
113 void clear_dump __P((void));
114 int Create __P((char *, int));
115 int dump_exists __P((void));
116 char *find_dev __P((dev_t, int));
117 int get_crashtime __P((void));
118 void kmem_setup __P((void));
119 void log __P((int, char *, ...));
120 void Lseek __P((int, off_t, int));
121 int Open __P((char *, int rw));
122 int Read __P((int, void *, int));
123 char *rawname __P((char *s));
124 void save_core __P((void));
125 void usage __P((void));
126 void Write __P((int, void *, int));
127
128 int
129 main(argc, argv)
130 int argc;
131 char *argv[];
132 {
133 int ch;
134
135 openlog("savecore", LOG_PERROR, LOG_DAEMON);
136
137 while ((ch = getopt(argc, argv, "cdfNvz")) != EOF)
138 switch(ch) {
139 case 'c':
140 clear = 1;
141 break;
142 case 'd': /* Not documented. */
143 case 'v':
144 verbose = 1;
145 break;
146 case 'f':
147 force = 1;
148 break;
149 case 'N':
150 vmunix = optarg;
151 break;
152 case 'z':
153 compress = 1;
154 break;
155 case '?':
156 default:
157 usage();
158 }
159 argc -= optind;
160 argv += optind;
161
162 if (!clear) {
163 if (argc != 1 && argc != 2)
164 usage();
165 dirname = argv[0];
166 }
167 if (argc == 2)
168 vmunix = argv[1];
169
170 (void)time(&now);
171 kmem_setup();
172
173 if (clear) {
174 clear_dump();
175 exit(0);
176 }
177
178 if (!dump_exists() && !force)
179 exit(1);
180
181 check_kmem();
182
183 if (panicstr)
184 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
185 else
186 syslog(LOG_ALERT, "reboot");
187
188 if ((!get_crashtime() || !check_space()) && !force)
189 exit(1);
190
191 save_core();
192
193 clear_dump();
194 exit(0);
195 }
196
197 void
198 kmem_setup()
199 {
200 FILE *fp;
201 int kmem, i;
202 char *dump_sys;
203
204 /*
205 * Some names we need for the currently running system, others for
206 * the system that was running when the dump was made. The values
207 * obtained from the current system are used to look for things in
208 * /dev/kmem that cannot be found in the dump_sys namelist, but are
209 * presumed to be the same (since the disk partitions are probably
210 * the same!)
211 */
212 if ((nlist(_PATH_UNIX, current_nl)) == -1)
213 syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno));
214 for (i = 0; cursyms[i] != -1; i++)
215 if (current_nl[cursyms[i]].n_value == 0) {
216 syslog(LOG_ERR, "%s: %s not in namelist",
217 _PATH_UNIX, current_nl[cursyms[i]].n_name);
218 exit(1);
219 }
220
221 dump_sys = vmunix ? vmunix : _PATH_UNIX;
222 if ((nlist(dump_sys, dump_nl)) == -1)
223 syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno));
224 for (i = 0; dumpsyms[i] != -1; i++)
225 if (dump_nl[dumpsyms[i]].n_value == 0) {
226 syslog(LOG_ERR, "%s: %s not in namelist",
227 dump_sys, dump_nl[dumpsyms[i]].n_name);
228 exit(1);
229 }
230
231 kmem = Open(_PATH_KMEM, O_RDONLY);
232 Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET);
233 (void)Read(kmem, &dumpdev, sizeof(dumpdev));
234 if (dumpdev == NODEV) {
235 syslog(LOG_WARNING, "no core dump (no dumpdev)");
236 exit(1);
237 }
238 Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
239 (void)Read(kmem, &dumplo, sizeof(dumplo));
240 if (verbose)
241 (void)printf("dumplo = %d (%d * %d)\n",
242 dumplo, dumplo/DEV_BSIZE, DEV_BSIZE);
243 Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
244 (void)Read(kmem, &dumpmag, sizeof(dumpmag));
245 dumplo *= DEV_BSIZE;
246 ddname = find_dev(dumpdev, S_IFBLK);
247 dumpfd = Open(ddname, O_RDWR);
248 fp = fdopen(kmem, "r");
249 if (fp == NULL) {
250 syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM);
251 exit(1);
252 }
253 if (vmunix)
254 return;
255 (void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET);
256 (void)fgets(vers, sizeof(vers), fp);
257
258 /* Don't fclose(fp), we use dumpfd later. */
259 }
260
261 void
262 check_kmem()
263 {
264 register char *cp;
265 FILE *fp;
266 char core_vers[1024];
267
268 fp = fdopen(dumpfd, "r");
269 if (fp == NULL) {
270 syslog(LOG_ERR, "%s: fdopen: %m", ddname);
271 exit(1);
272 }
273 fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
274 fgets(core_vers, sizeof(core_vers), fp);
275 if (strcmp(vers, core_vers) && vmunix == 0)
276 syslog(LOG_WARNING,
277 "warning: %s version mismatch:\n\t%s\nand\t%s\n",
278 _PATH_UNIX, vers, core_vers);
279 (void)fseek(fp,
280 (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
281 (void)fread(&panicstr, sizeof(panicstr), 1, fp);
282 if (panicstr) {
283 (void)fseek(fp, dumplo + ok(panicstr), L_SET);
284 cp = panic_mesg;
285 do
286 *cp = getc(fp);
287 while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]);
288 }
289 /* Don't fclose(fp), we use dumpfd later. */
290 }
291
292 void
293 clear_dump()
294 {
295 long newdumplo;
296
297 newdumplo = 0;
298 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
299 Write(dumpfd, &newdumplo, sizeof(newdumplo));
300 }
301
302 int
303 dump_exists()
304 {
305 int newdumpmag;
306
307 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
308 (void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag));
309 if (newdumpmag != dumpmag) {
310 if (verbose)
311 syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
312 newdumpmag, dumpmag);
313 syslog(LOG_WARNING, "no core dump");
314 return (0);
315 }
316 return (1);
317 }
318
319 char buf[1024 * 1024];
320
321 void
322 save_core()
323 {
324 register FILE *fp;
325 register int bounds, ifd, nr, nw, ofd;
326 char *rawp, path[MAXPATHLEN];
327
328 /*
329 * Get the current number and update the bounds file. Do the update
330 * now, because may fail later and don't want to overwrite anything.
331 */
332 (void)snprintf(path, sizeof(path), "%s/bounds", dirname);
333 if ((fp = fopen(path, "r")) == NULL)
334 goto err1;
335 if (fgets(buf, sizeof(buf), fp) == NULL) {
336 if (ferror(fp))
337 err1: syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
338 bounds = 0;
339 } else
340 bounds = atoi(buf);
341 if (fp != NULL)
342 (void)fclose(fp);
343 if ((fp = fopen(path, "w")) == NULL)
344 syslog(LOG_ERR, "%s: %m", path);
345 else {
346 (void)fprintf(fp, "%d\n", bounds + 1);
347 (void)fclose(fp);
348 }
349 (void)fclose(fp);
350
351 /* Create the core file. */
352 (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
353 dirname, bounds, compress ? ".Z" : "");
354 if (compress) {
355 if ((fp = zopen(path, "w", 0)) == NULL) {
356 syslog(LOG_ERR, "%s: %s", path, strerror(errno));
357 exit(1);
358 }
359 } else
360 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
361
362 /* Open the raw device. */
363 rawp = rawname(ddname);
364 if ((ifd = open(rawp, O_RDONLY)) == -1) {
365 syslog(LOG_WARNING, "%s: %m; using block device", rawp);
366 ifd = dumpfd;
367 }
368
369 /* Read the dump size. */
370 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
371 (void)Read(dumpfd, &dumpsize, sizeof(dumpsize));
372
373 /* Seek to the start of the core. */
374 Lseek(ifd, (off_t)dumplo, L_SET);
375
376 /* Copy the core file. */
377 dumpsize *= NBPG;
378 syslog(LOG_NOTICE, "writing %score to %s",
379 compress ? "compressed " : "", path);
380 for (; dumpsize > 0; dumpsize -= nr) {
381 (void)printf("%6dK\r", dumpsize / 1024);
382 (void)fflush(stdout);
383 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
384 if (nr <= 0) {
385 if (nr == 0)
386 syslog(LOG_WARNING,
387 "WARNING: EOF on dump device");
388 else
389 syslog(LOG_ERR, "%s: %m", rawp);
390 goto err2;
391 }
392 if (compress)
393 nw = fwrite(buf, 1, nr, fp);
394 else
395 nw = write(ofd, buf, nr);
396 if (nw != nr) {
397 syslog(LOG_ERR, "%s: %s",
398 path, strerror(nw == 0 ? EIO : errno));
399 err2: syslog(LOG_WARNING,
400 "WARNING: vmcore may be incomplete");
401 (void)printf("\n");
402 exit(1);
403 }
404 }
405 (void)printf("\n");
406 (void)close(ifd);
407 if (compress)
408 (void)fclose(fp);
409 else
410 (void)close(ofd);
411
412 /* Copy the kernel. */
413 ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY);
414 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
415 dirname, bounds, compress ? ".Z" : "");
416 if (compress) {
417 if ((fp = zopen(path, "w", 0)) == NULL) {
418 syslog(LOG_ERR, "%s: %s", path, strerror(errno));
419 exit(1);
420 }
421 } else
422 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
423 syslog(LOG_NOTICE, "writing %skernel to %s",
424 compress ? "compressed " : "", path);
425 while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
426 if (compress)
427 nw = fwrite(buf, 1, nr, fp);
428 else
429 nw = write(ofd, buf, nr);
430 if (nw != nr) {
431 syslog(LOG_ERR, "%s: %s",
432 path, strerror(nw == 0 ? EIO : errno));
433 syslog(LOG_WARNING,
434 "WARNING: vmunix may be incomplete");
435 exit(1);
436 }
437 }
438 if (nr < 0) {
439 syslog(LOG_ERR, "%s: %s",
440 vmunix ? vmunix : _PATH_UNIX, strerror(errno));
441 syslog(LOG_WARNING,
442 "WARNING: vmunix may be incomplete");
443 exit(1);
444 }
445 if (compress)
446 (void)fclose(fp);
447 else
448 (void)close(ofd);
449 }
450
451 char *
452 find_dev(dev, type)
453 register dev_t dev;
454 register int type;
455 {
456 register DIR *dfd;
457 struct dirent *dir;
458 struct stat sb;
459 char *dp, devname[MAXPATHLEN + 1];
460
461 if ((dfd = opendir(_PATH_DEV)) == NULL) {
462 syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
463 exit(1);
464 }
465 (void)strcpy(devname, _PATH_DEV);
466 while ((dir = readdir(dfd))) {
467 (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
468 if (lstat(devname, &sb)) {
469 syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
470 continue;
471 }
472 if ((sb.st_mode & S_IFMT) != type)
473 continue;
474 if (dev == sb.st_rdev) {
475 closedir(dfd);
476 if ((dp = strdup(devname)) == NULL) {
477 syslog(LOG_ERR, "%s", strerror(errno));
478 exit(1);
479 }
480 return (dp);
481 }
482 }
483 closedir(dfd);
484 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
485 exit(1);
486 }
487
488 char *
489 rawname(s)
490 char *s;
491 {
492 char *sl, name[MAXPATHLEN];
493
494 if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') {
495 syslog(LOG_ERR,
496 "can't make raw dump device name from %s", s);
497 return (s);
498 }
499 (void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1);
500 if ((sl = strdup(name)) == NULL) {
501 syslog(LOG_ERR, "%s", strerror(errno));
502 exit(1);
503 }
504 return (sl);
505 }
506
507 int
508 get_crashtime()
509 {
510 time_t dumptime; /* Time the dump was taken. */
511
512 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
513 (void)Read(dumpfd, &dumptime, sizeof(dumptime));
514 if (dumptime == 0) {
515 if (verbose)
516 syslog(LOG_ERR, "dump time is zero");
517 return (0);
518 }
519 (void)printf("savecore: system went down at %s", ctime(&dumptime));
520 #define LEEWAY (7 * SECSPERDAY)
521 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
522 (void)printf("dump time is unreasonable\n");
523 return (0);
524 }
525 return (1);
526 }
527
528 int
529 check_space()
530 {
531 register FILE *fp;
532 char *tvmunix;
533 off_t minfree, spacefree, vmunixsize, needed;
534 struct stat st;
535 struct statfs fsbuf;
536 char buf[100], path[MAXPATHLEN];
537
538 tvmunix = vmunix ? vmunix : _PATH_UNIX;
539 if (stat(tvmunix, &st) < 0) {
540 syslog(LOG_ERR, "%s: %m", tvmunix);
541 exit(1);
542 }
543 vmunixsize = st.st_blocks * S_BLKSIZE;
544 if (statfs(dirname, &fsbuf) < 0) {
545 syslog(LOG_ERR, "%s: %m", dirname);
546 exit(1);
547 }
548 spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
549
550 (void)snprintf(path, sizeof(path), "%s/minfree", dirname);
551 if ((fp = fopen(path, "r")) == NULL)
552 minfree = 0;
553 else {
554 if (fgets(buf, sizeof(buf), fp) == NULL)
555 minfree = 0;
556 else
557 minfree = atoi(buf);
558 (void)fclose(fp);
559 }
560
561 needed = (dumpsize + vmunixsize) / 1024;
562 if (minfree > 0 && spacefree - needed < minfree) {
563 syslog(LOG_WARNING,
564 "no dump, not enough free space on device");
565 return (0);
566 }
567 if (spacefree - needed < minfree)
568 syslog(LOG_WARNING,
569 "dump performed, but free space threshold crossed");
570 return (1);
571 }
572
573 int
574 Open(name, rw)
575 char *name;
576 int rw;
577 {
578 int fd;
579
580 if ((fd = open(name, rw, 0)) < 0) {
581 syslog(LOG_ERR, "%s: %m", name);
582 exit(1);
583 }
584 return (fd);
585 }
586
587 int
588 Read(fd, bp, size)
589 int fd, size;
590 void *bp;
591 {
592 int nr;
593
594 nr = read(fd, bp, size);
595 if (nr != size) {
596 syslog(LOG_ERR, "read: %m");
597 exit(1);
598 }
599 return (nr);
600 }
601
602 void
603 Lseek(fd, off, flag)
604 int fd, flag;
605 off_t off;
606 {
607 off_t ret;
608
609 ret = lseek(fd, off, flag);
610 if (ret == -1) {
611 syslog(LOG_ERR, "lseek: %m");
612 exit(1);
613 }
614 }
615
616 int
617 Create(file, mode)
618 char *file;
619 int mode;
620 {
621 register int fd;
622
623 fd = creat(file, mode);
624 if (fd < 0) {
625 syslog(LOG_ERR, "%s: %m", file);
626 exit(1);
627 }
628 return (fd);
629 }
630
631 void
632 Write(fd, bp, size)
633 int fd, size;
634 void *bp;
635 {
636 int n;
637
638 if ((n = write(fd, bp, size)) < size) {
639 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
640 exit(1);
641 }
642 }
643
644 void
645 usage()
646 {
647 (void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
648 exit(1);
649 }
650