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