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