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