savecore.c revision 1.57 1 /* $NetBSD: savecore.c,v 1.57 2002/03/19 18:55:03 christos 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.57 2002/03/19 18:55:03 christos 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 (void)printf("%8dK\r", dumpsize / 1024);
554 (void)fflush(stdout);
555 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
556 if (nr <= 0) {
557 if (nr == 0)
558 syslog(LOG_WARNING,
559 "WARNING: EOF on dump device");
560 else
561 syslog(LOG_ERR, "%s: %m", rawp);
562 goto err2;
563 }
564 nw = fwrite(buf, 1, nr, fp);
565 if (nw != nr) {
566 syslog(LOG_ERR, "%s: %s",
567 path, strerror(nw == 0 ? EIO : errno));
568 err2: syslog(LOG_WARNING,
569 "WARNING: core may be incomplete");
570 (void)printf("\n");
571 exit(1);
572 }
573 }
574 (void)close(ifd);
575 (void)fclose(fp);
576
577 /* Copy the kernel. */
578 ifd = Open(kernel, O_RDONLY);
579 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
580 dirname, bounds, compress ? ".gz" : "");
581 if (compress) {
582 if ((fp = zopen(path, gzmode)) == NULL) {
583 syslog(LOG_ERR, "%s: %m", path);
584 exit(1);
585 }
586 } else
587 ofd = Create(path, S_IRUSR | S_IWUSR);
588 syslog(LOG_NOTICE, "writing %skernel to %s",
589 compress ? "compressed " : "", path);
590 while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
591 if (compress)
592 nw = fwrite(buf, 1, nr, fp);
593 else
594 nw = write(ofd, buf, nr);
595 if (nw != nr) {
596 syslog(LOG_ERR, "%s: %s",
597 path, strerror(nw == 0 ? EIO : errno));
598 syslog(LOG_WARNING,
599 "WARNING: kernel may be incomplete");
600 exit(1);
601 }
602 }
603 if (nr < 0) {
604 syslog(LOG_ERR, "%s: %m", kernel);
605 syslog(LOG_WARNING, "WARNING: kernel may be incomplete");
606 exit(1);
607 }
608 if (compress)
609 (void)fclose(fp);
610 else
611 (void)close(ofd);
612 }
613
614 char *
615 find_dev(dev_t dev, int type)
616 {
617 DIR *dfd;
618 struct dirent *dir;
619 struct stat sb;
620 char *dp, device[MAXPATHLEN + 1];
621
622 if ((dfd = opendir(_PATH_DEV)) == NULL) {
623 syslog(LOG_ERR, "%s: %m", _PATH_DEV);
624 exit(1);
625 }
626 (void)strcpy(device, _PATH_DEV);
627 while ((dir = readdir(dfd))) {
628 (void)strcpy(device + sizeof(_PATH_DEV) - 1, dir->d_name);
629 if (lstat(device, &sb)) {
630 syslog(LOG_ERR, "%s: %m", device);
631 continue;
632 }
633 if ((sb.st_mode & S_IFMT) != type)
634 continue;
635 if (dev == sb.st_rdev) {
636 closedir(dfd);
637 if ((dp = strdup(device)) == NULL) {
638 syslog(LOG_ERR, "%m");
639 exit(1);
640 }
641 return (dp);
642 }
643 }
644 closedir(dfd);
645 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
646 exit(1);
647 }
648
649 char *
650 rawname(char *s)
651 {
652 char *sl;
653 char name[MAXPATHLEN];
654
655 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
656 syslog(LOG_ERR,
657 "can't make raw dump device name from %s", s);
658 return (s);
659 }
660 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
661 sl + 1);
662 if ((sl = strdup(name)) == NULL) {
663 syslog(LOG_ERR, "%m");
664 exit(1);
665 }
666 return (sl);
667 }
668
669 int
670 get_crashtime(void)
671 {
672 struct timeval dtime;
673 time_t dumptime; /* Time the dump was taken. */
674
675 if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) {
676 if (verbose)
677 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
678 return (0);
679 }
680 dumptime = dtime.tv_sec;
681 if (dumptime == 0) {
682 if (verbose)
683 syslog(LOG_ERR, "dump time is zero");
684 return (0);
685 }
686 (void)printf("savecore: system went down at %s", ctime(&dumptime));
687 #define LEEWAY (7 * SECSPERDAY)
688 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
689 (void)printf("dump time is unreasonable\n");
690 return (0);
691 }
692 return (1);
693 }
694
695 int
696 check_space(void)
697 {
698 FILE *fp;
699 off_t minfree, spacefree, kernelsize, needed;
700 struct stat st;
701 struct statfs fsbuf;
702 char mbuf[100], path[MAXPATHLEN];
703
704 #ifdef __GNUC__
705 (void) &minfree;
706 #endif
707
708 if (stat(kernel, &st) < 0) {
709 syslog(LOG_ERR, "%s: %m", kernel);
710 exit(1);
711 }
712 kernelsize = st.st_blocks * S_BLKSIZE;
713 if (statfs(dirname, &fsbuf) < 0) {
714 syslog(LOG_ERR, "%s: %m", dirname);
715 exit(1);
716 }
717 spacefree = fsbuf.f_bavail;
718 spacefree *= fsbuf.f_bsize;
719 spacefree /= 1024;
720
721 (void)snprintf(path, sizeof(path), "%s/minfree", dirname);
722 if ((fp = fopen(path, "r")) == NULL)
723 minfree = 0;
724 else {
725 if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
726 minfree = 0;
727 else
728 minfree = atoi(mbuf);
729 (void)fclose(fp);
730 }
731
732 needed = (dumpsize + kernelsize) / 1024;
733 if (minfree > 0 && spacefree - needed < minfree) {
734 syslog(LOG_WARNING,
735 "no dump, not enough free space in %s", dirname);
736 return (0);
737 }
738 if (spacefree - needed < minfree)
739 syslog(LOG_WARNING,
740 "dump performed, but free space threshold crossed");
741 return (1);
742 }
743
744 int
745 Open(const char *name, int rw)
746 {
747 int fd;
748
749 if ((fd = open(name, rw, 0)) < 0) {
750 syslog(LOG_ERR, "%s: %m", name);
751 exit(1);
752 }
753 return (fd);
754 }
755
756 void
757 Lseek(int fd, off_t off, int flag)
758 {
759 off_t ret;
760
761 ret = lseek(fd, off, flag);
762 if (ret == -1) {
763 syslog(LOG_ERR, "lseek: %m");
764 exit(1);
765 }
766 }
767
768 int
769 Create(char *file, int mode)
770 {
771 int fd;
772
773 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
774 if (fd < 0) {
775 syslog(LOG_ERR, "%s: %m", file);
776 exit(1);
777 }
778 return (fd);
779 }
780
781 void
782 Write(int fd, void *bp, int size)
783 {
784 int n;
785
786 if ((n = write(fd, bp, size)) < size) {
787 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
788 exit(1);
789 }
790 }
791
792 void
793 usage(void)
794 {
795 (void)syslog(LOG_ERR,
796 "usage: savecore [-cfvz] [-N system] [-Z level] directory");
797 exit(1);
798 }
799