savecore.c revision 1.57.2.1 1 /* $NetBSD: savecore.c,v 1.57.2.1 2004/07/23 15:03:58 tron 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.2.1 2004/07/23 15:03:58 tron 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 Lseek(int, off_t, int);
145 int main(int, char *[]);
146 int Open(const char *, int rw);
147 char *rawname(char *s);
148 void save_core(void);
149 void usage(void);
150 void Write(int, void *, int);
151
152 int
153 main(int argc, char *argv[])
154 {
155 int ch, level;
156 char *ep;
157
158 dirname = NULL;
159 kernel = NULL;
160 level = 1; /* default to fastest gzip compression */
161 gzmode[0] = 'w';
162
163 openlog("savecore", LOG_PERROR, LOG_DAEMON);
164
165 while ((ch = getopt(argc, argv, "cdfN:vzZ:")) != -1)
166 switch(ch) {
167 case 'c':
168 clear = 1;
169 break;
170 case 'd': /* Not documented. */
171 case 'v':
172 verbose = 1;
173 break;
174 case 'f':
175 force = 1;
176 break;
177 case 'N':
178 kernel = optarg;
179 break;
180 case 'z':
181 compress = 1;
182 break;
183 case 'Z':
184 level = (int)strtol(optarg, &ep, 10);
185 if (level < 0 || level > 9) {
186 (void)syslog(LOG_ERR, "invalid compression %s",
187 optarg);
188 usage();
189 }
190 break;
191 case '?':
192 default:
193 usage();
194 }
195 argc -= optind;
196 argv += optind;
197
198 if (argc != (clear ? 0 : 1))
199 usage();
200
201 gzmode[1] = level + '0';
202 if (!clear)
203 dirname = argv[0];
204
205 if (kernel == NULL) {
206 kernel = getbootfile();
207 }
208
209 (void)time(&now);
210 kmem_setup();
211
212 if (clear) {
213 clear_dump();
214 exit(0);
215 }
216
217 if (!dump_exists() && !force)
218 exit(1);
219
220 check_kmem();
221
222 if (panicstr)
223 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
224 else
225 syslog(LOG_ALERT, "reboot");
226
227 if ((!get_crashtime() || !check_space()) && !force)
228 exit(1);
229
230 save_core();
231
232 clear_dump();
233 exit(0);
234 }
235
236 void
237 kmem_setup(void)
238 {
239 kvm_t *kd_kern;
240 char errbuf[_POSIX2_LINE_MAX];
241 int i, hdrsz;
242
243 /*
244 * Some names we need for the currently running system, others for
245 * the system that was running when the dump was made. The values
246 * obtained from the current system are used to look for things in
247 * /dev/kmem that cannot be found in the kernel namelist, but are
248 * presumed to be the same (since the disk partitions are probably
249 * the same!)
250 */
251 kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf);
252 if (kd_kern == NULL) {
253 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
254 exit(1);
255 }
256 if (kvm_nlist(kd_kern, current_nl) == -1)
257 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
258 kvm_geterr(kd_kern));
259
260 for (i = 0; cursyms[i] != -1; i++)
261 if (current_nl[cursyms[i]].n_value == 0) {
262 syslog(LOG_ERR, "%s: %s not in namelist",
263 kernel, current_nl[cursyms[i]].n_name);
264 exit(1);
265 }
266
267 if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) {
268 if (verbose)
269 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
270 exit(1);
271 }
272 if (dumpdev == NODEV) {
273 syslog(LOG_WARNING, "no core dump (no dumpdev)");
274 exit(1);
275 }
276 {
277 long l_dumplo;
278
279 if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &l_dumplo) != 0) {
280 if (verbose)
281 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
282 exit(1);
283 }
284 if (l_dumplo == -1) {
285 syslog(LOG_WARNING, "no core dump (invalid dumplo)");
286 exit(1);
287 }
288 dumplo = DEV_BSIZE * (off_t) l_dumplo;
289 }
290
291 if (verbose)
292 (void)printf("dumplo = %lld (%ld * %ld)\n",
293 (long long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE);
294 if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) {
295 if (verbose)
296 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
297 exit(1);
298 }
299
300 (void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers,
301 sizeof(vers));
302 vers[sizeof(vers) - 1] = '\0';
303
304 ddname = find_dev(dumpdev, S_IFBLK);
305 dumpfd = Open(ddname, O_RDWR);
306
307 kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
308 if (kd_dump == NULL) {
309 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
310 exit(1);
311 }
312
313 if (kvm_nlist(kd_dump, dump_nl) == -1)
314 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
315 kvm_geterr(kd_dump));
316
317 for (i = 0; dumpsyms[i] != -1; i++)
318 if (dump_nl[dumpsyms[i]].n_value == 0) {
319 syslog(LOG_ERR, "%s: %s not in namelist",
320 kernel, dump_nl[dumpsyms[i]].n_name);
321 exit(1);
322 }
323 hdrsz = kvm_dump_mkheader(kd_dump, dumplo);
324
325 /*
326 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
327 * checks, ergo no dump is present...
328 */
329 if (hdrsz == 0) {
330 syslog(LOG_WARNING, "no core dump");
331 exit(1);
332 }
333 if (hdrsz == -1) {
334 syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel,
335 kvm_geterr(kd_dump));
336 exit(1);
337 }
338 dumplo += hdrsz;
339 kvm_close(kd_kern);
340 }
341
342 void
343 check_kmem(void)
344 {
345 char *cp, *bufdata;
346 struct kern_msgbuf msgbuf, *bufp;
347 long panicloc, panicstart, panicend;
348 char core_vers[1024];
349
350 (void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
351 sizeof(core_vers));
352 core_vers[sizeof(core_vers) - 1] = '\0';
353
354 if (strcmp(vers, core_vers) != 0)
355 syslog(LOG_WARNING,
356 "warning: %s version mismatch:\n\t%s\nand\t%s\n",
357 kernel, vers, core_vers);
358
359 panicstart = panicend = 0;
360 if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) {
361 if (verbose)
362 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
363 goto nomsguf;
364 }
365 if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) {
366 if (verbose)
367 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
368 goto nomsguf;
369 }
370 if (panicstart != 0 && panicend != 0) {
371 if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) {
372 if (verbose)
373 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
374 goto nomsguf;
375 }
376 if (kvm_read(kd_dump, (long)bufp, &msgbuf,
377 offsetof(struct kern_msgbuf, msg_bufc)) !=
378 offsetof(struct kern_msgbuf, msg_bufc)) {
379 if (verbose)
380 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
381 goto nomsguf;
382 }
383 if (msgbuf.msg_magic != MSG_MAGIC) {
384 if (verbose)
385 syslog(LOG_WARNING, "msgbuf magic incorrect");
386 goto nomsguf;
387 }
388 bufdata = malloc(msgbuf.msg_bufs);
389 if (bufdata == NULL) {
390 if (verbose)
391 syslog(LOG_WARNING, "couldn't allocate space for msgbuf data");
392 goto nomsguf;
393 }
394 if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata,
395 msgbuf.msg_bufs) != msgbuf.msg_bufs) {
396 if (verbose)
397 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
398 goto nomsguf;
399 }
400 cp = panic_mesg;
401 while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) {
402 *cp++ = bufdata[panicstart];
403 panicstart++;
404 if (panicstart >= msgbuf.msg_bufs)
405 panicstart = 0;
406 }
407 /* Don't end in a new-line */
408 cp = &panic_mesg[strlen(panic_mesg)] - 1;
409 if (*cp == '\n')
410 *cp = '\0';
411 panic_mesg[sizeof(panic_mesg) - 1] = '\0';
412
413 panicstr = 1; /* anything not zero */
414 return;
415 }
416 nomsguf:
417 if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
418 if (verbose)
419 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
420 return;
421 }
422 if (panicstr) {
423 cp = panic_mesg;
424 panicloc = panicstr;
425 do {
426 if (KREAD(kd_dump, panicloc, cp) != 0) {
427 if (verbose)
428 syslog(LOG_WARNING, "kvm_read: %s",
429 kvm_geterr(kd_dump));
430 break;
431 }
432 panicloc++;
433 } while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
434 panic_mesg[sizeof(panic_mesg) - 1] = '\0';
435 }
436 }
437
438 int
439 dump_exists(void)
440 {
441 u_int32_t newdumpmag;
442
443 if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
444 if (verbose)
445 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
446 return (0);
447 }
448
449 /* Read the dump size. */
450 if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
451 if (verbose)
452 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
453 return (0);
454 }
455 dumpsize *= getpagesize();
456
457 /*
458 * Return zero if core dump doesn't seem to be there, and note
459 * it for syslog. This check and return happens after the dump size
460 * is read, so dumpsize is whether or not the core is valid (for -f).
461 */
462 if (newdumpmag != dumpmag) {
463 if (verbose)
464 syslog(LOG_WARNING,
465 "magic number mismatch (0x%x != 0x%x)",
466 newdumpmag, dumpmag);
467 syslog(LOG_WARNING, "no core dump");
468 return (0);
469 }
470 return (1);
471 }
472
473 void
474 clear_dump(void)
475 {
476 if (kvm_dump_inval(kd_dump) == -1)
477 syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
478 kvm_geterr(kd_dump));
479
480 }
481
482 char buf[1024 * 1024];
483
484 void
485 save_core(void)
486 {
487 FILE *fp;
488 int bounds, ifd, nr, nw, ofd;
489 char *rawp, path[MAXPATHLEN];
490
491 ofd = -1;
492 /*
493 * Get the current number and update the bounds file. Do the update
494 * now, because may fail later and don't want to overwrite anything.
495 */
496 umask(066);
497 (void)snprintf(path, sizeof(path), "%s/bounds", dirname);
498 if ((fp = fopen(path, "r")) == NULL)
499 goto err1;
500 if (fgets(buf, sizeof(buf), fp) == NULL) {
501 if (ferror(fp))
502 err1: syslog(LOG_WARNING, "%s: %m", path);
503 bounds = 0;
504 } else
505 bounds = atoi(buf);
506 if (fp != NULL)
507 (void)fclose(fp);
508 if ((fp = fopen(path, "w")) == NULL)
509 syslog(LOG_ERR, "%s: %m", path);
510 else {
511 (void)fprintf(fp, "%d\n", bounds + 1);
512 (void)fclose(fp);
513 }
514
515 /* Create the core file. */
516 (void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
517 dirname, bounds, compress ? ".gz" : "");
518 if (compress) {
519 if ((fp = zopen(path, gzmode)) == NULL) {
520 syslog(LOG_ERR, "%s: %m", path);
521 exit(1);
522 }
523 } else {
524 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
525 fp = fdopen(ofd, "w");
526 if (fp == NULL) {
527 syslog(LOG_ERR, "%s: fdopen: %m", path);
528 exit(1);
529 }
530 }
531
532 /* Open the raw device. */
533 rawp = rawname(ddname);
534 if ((ifd = open(rawp, O_RDONLY)) == -1) {
535 syslog(LOG_WARNING, "%s: %m; using block device", rawp);
536 ifd = dumpfd;
537 }
538
539 /* Seek to the start of the core. */
540 Lseek(ifd, dumplo, SEEK_SET);
541
542 if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) {
543 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
544 kvm_geterr(kd_dump));
545 exit(1);
546 }
547
548 /* Copy the core file. */
549 syslog(LOG_NOTICE, "writing %score to %s",
550 compress ? "compressed " : "", path);
551 for (; dumpsize > 0; dumpsize -= nr) {
552 (void)printf("%8dK\r", dumpsize / 1024);
553 (void)fflush(stdout);
554 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
555 if (nr <= 0) {
556 if (nr == 0)
557 syslog(LOG_WARNING,
558 "WARNING: EOF on dump device");
559 else
560 syslog(LOG_ERR, "%s: %m", rawp);
561 goto err2;
562 }
563 nw = fwrite(buf, 1, nr, fp);
564 if (nw != nr) {
565 syslog(LOG_ERR, "%s: %s",
566 path, strerror(nw == 0 ? EIO : errno));
567 err2: syslog(LOG_WARNING,
568 "WARNING: core may be incomplete");
569 (void)printf("\n");
570 exit(1);
571 }
572 }
573 (void)close(ifd);
574 (void)fclose(fp);
575
576 /* Copy the kernel. */
577 ifd = Open(kernel, O_RDONLY);
578 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
579 dirname, bounds, compress ? ".gz" : "");
580 if (compress) {
581 if ((fp = zopen(path, gzmode)) == NULL) {
582 syslog(LOG_ERR, "%s: %m", path);
583 exit(1);
584 }
585 } else
586 ofd = Create(path, S_IRUSR | S_IWUSR);
587 syslog(LOG_NOTICE, "writing %skernel to %s",
588 compress ? "compressed " : "", path);
589 while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
590 if (compress)
591 nw = fwrite(buf, 1, nr, fp);
592 else
593 nw = write(ofd, buf, nr);
594 if (nw != nr) {
595 syslog(LOG_ERR, "%s: %s",
596 path, strerror(nw == 0 ? EIO : errno));
597 syslog(LOG_WARNING,
598 "WARNING: kernel may be incomplete");
599 exit(1);
600 }
601 }
602 if (nr < 0) {
603 syslog(LOG_ERR, "%s: %m", kernel);
604 syslog(LOG_WARNING, "WARNING: kernel may be incomplete");
605 exit(1);
606 }
607 if (compress)
608 (void)fclose(fp);
609 else
610 (void)close(ofd);
611 }
612
613 char *
614 find_dev(dev_t dev, int type)
615 {
616 DIR *dfd;
617 struct dirent *dir;
618 struct stat sb;
619 char *dp, device[MAXPATHLEN + 1];
620
621 if ((dfd = opendir(_PATH_DEV)) == NULL) {
622 syslog(LOG_ERR, "%s: %m", _PATH_DEV);
623 exit(1);
624 }
625 (void)strcpy(device, _PATH_DEV);
626 while ((dir = readdir(dfd))) {
627 (void)strcpy(device + sizeof(_PATH_DEV) - 1, dir->d_name);
628 if (lstat(device, &sb)) {
629 syslog(LOG_ERR, "%s: %m", device);
630 continue;
631 }
632 if ((sb.st_mode & S_IFMT) != type)
633 continue;
634 if (dev == sb.st_rdev) {
635 closedir(dfd);
636 if ((dp = strdup(device)) == NULL) {
637 syslog(LOG_ERR, "%m");
638 exit(1);
639 }
640 return (dp);
641 }
642 }
643 closedir(dfd);
644 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
645 exit(1);
646 }
647
648 char *
649 rawname(char *s)
650 {
651 char *sl;
652 char name[MAXPATHLEN];
653
654 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
655 syslog(LOG_ERR,
656 "can't make raw dump device name from %s", s);
657 return (s);
658 }
659 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
660 sl + 1);
661 if ((sl = strdup(name)) == NULL) {
662 syslog(LOG_ERR, "%m");
663 exit(1);
664 }
665 return (sl);
666 }
667
668 int
669 get_crashtime(void)
670 {
671 struct timeval dtime;
672 time_t dumptime; /* Time the dump was taken. */
673
674 if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) {
675 if (verbose)
676 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
677 return (0);
678 }
679 dumptime = dtime.tv_sec;
680 if (dumptime == 0) {
681 if (verbose)
682 syslog(LOG_ERR, "dump time is zero");
683 return (0);
684 }
685 (void)printf("savecore: system went down at %s", ctime(&dumptime));
686 #define LEEWAY (7 * SECSPERDAY)
687 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
688 (void)printf("dump time is unreasonable\n");
689 return (0);
690 }
691 return (1);
692 }
693
694 int
695 check_space(void)
696 {
697 FILE *fp;
698 off_t minfree, spacefree, kernelsize, needed;
699 struct stat st;
700 struct statfs fsbuf;
701 char mbuf[100], path[MAXPATHLEN];
702
703 #ifdef __GNUC__
704 (void) &minfree;
705 #endif
706
707 if (stat(kernel, &st) < 0) {
708 syslog(LOG_ERR, "%s: %m", kernel);
709 exit(1);
710 }
711 kernelsize = st.st_blocks * S_BLKSIZE;
712 if (statfs(dirname, &fsbuf) < 0) {
713 syslog(LOG_ERR, "%s: %m", dirname);
714 exit(1);
715 }
716 spacefree = fsbuf.f_bavail;
717 spacefree *= fsbuf.f_bsize;
718 spacefree /= 1024;
719
720 (void)snprintf(path, sizeof(path), "%s/minfree", dirname);
721 if ((fp = fopen(path, "r")) == NULL)
722 minfree = 0;
723 else {
724 if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
725 minfree = 0;
726 else
727 minfree = atoi(mbuf);
728 (void)fclose(fp);
729 }
730
731 needed = (dumpsize + kernelsize) / 1024;
732 if (minfree > 0 && spacefree - needed < minfree) {
733 syslog(LOG_WARNING,
734 "no dump, not enough free space in %s", dirname);
735 return (0);
736 }
737 if (spacefree - needed < minfree)
738 syslog(LOG_WARNING,
739 "dump performed, but free space threshold crossed");
740 return (1);
741 }
742
743 int
744 Open(const char *name, int rw)
745 {
746 int fd;
747
748 if ((fd = open(name, rw, 0)) < 0) {
749 syslog(LOG_ERR, "%s: %m", name);
750 exit(1);
751 }
752 return (fd);
753 }
754
755 void
756 Lseek(int fd, off_t off, int flag)
757 {
758 off_t ret;
759
760 ret = lseek(fd, off, flag);
761 if (ret == -1) {
762 syslog(LOG_ERR, "lseek: %m");
763 exit(1);
764 }
765 }
766
767 int
768 Create(char *file, int mode)
769 {
770 int fd;
771
772 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
773 if (fd < 0) {
774 syslog(LOG_ERR, "%s: %m", file);
775 exit(1);
776 }
777 return (fd);
778 }
779
780 void
781 Write(int fd, void *bp, int size)
782 {
783 int n;
784
785 if ((n = write(fd, bp, size)) < size) {
786 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
787 exit(1);
788 }
789 }
790
791 void
792 usage(void)
793 {
794 (void)syslog(LOG_ERR,
795 "usage: savecore [-cfvz] [-N system] [-Z level] directory");
796 exit(1);
797 }
798