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