1 /* $NetBSD: savecore.c,v 1.80 2009/04/06 12:32:30 lukem Exp $ */
4 * Copyright (c) 1986, 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1986, 1992, 1993\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)savecore.c 8.5 (Berkeley) 4/28/95";
42 __RCSID("$NetBSD: savecore.c,v 1.80 2009/04/06 12:32:30 lukem Exp $");
46 #define _KSYMS_PRIVATE
50 #include <sys/param.h>
51 #include <sys/mount.h>
52 #include <sys/msgbuf.h>
53 #include <sys/syslog.h>
55 #include <sys/ksyms.h>
73 extern FILE *zopen(const char *fname
, const char *mode
);
75 #define KREAD(kd, addr, p)\
76 (kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p)))
78 struct nlist current_nl
[] = { /* Namelist for currently running system. */
80 { .n_name
= "_dumpdev" },
82 { .n_name
= "_dumplo" },
83 #define X_TIME_SECOND 2
84 { .n_name
= "_time_second" },
86 { .n_name
= "_time" },
88 { .n_name
= "_dumpsize" },
90 { .n_name
= "_version" },
92 { .n_name
= "_dumpmag" },
94 { .n_name
= "_panicstr" },
95 #define X_PANICSTART 8
96 { .n_name
= "_panicstart" },
98 { .n_name
= "_panicend" },
100 { .n_name
= "_msgbufp" },
101 #define X_DUMPCDEV 11
102 { .n_name
= "_dumpcdev" },
104 { .n_name
= "_ksyms_symsz" },
106 { .n_name
= "_ksyms_strsz" },
108 { .n_name
= "_ksyms_hdr" },
110 { .n_name
= "_ksyms_symtabs" },
113 int cursyms
[] = { X_DUMPDEV
, X_DUMPLO
, X_VERSION
, X_DUMPMAG
, X_DUMPCDEV
, -1 };
114 int dumpsyms
[] = { X_TIME_SECOND
, X_TIME
, X_DUMPSIZE
, X_VERSION
, X_PANICSTR
,
115 X_DUMPMAG
, X_SYMSZ
, X_STRSZ
, X_KHDR
, X_SYMTABS
, -1 };
117 struct nlist dump_nl
[] = { /* Name list for dumped system. */
118 { .n_name
= "_dumpdev" }, /* Entries MUST be the same as */
119 { .n_name
= "_dumplo" }, /* those in current_nl[]. */
120 { .n_name
= "_time_second" },
121 { .n_name
= "_time" },
122 { .n_name
= "_dumpsize" },
123 { .n_name
= "_version" },
124 { .n_name
= "_dumpmag" },
125 { .n_name
= "_panicstr" },
126 { .n_name
= "_panicstart" },
127 { .n_name
= "_panicend" },
128 { .n_name
= "_msgbufp" },
129 { .n_name
= "_dumpcdev" },
130 { .n_name
= "_ksyms_symsz" },
131 { .n_name
= "_ksyms_strsz" },
132 { .n_name
= "_ksyms_hdr" },
133 { .n_name
= "_ksyms_symtabs" },
137 /* Types match kernel declarations. */
138 off_t dumplo
; /* where dump starts on dumpdev */
139 u_int32_t dumpmag
; /* magic number in dump */
140 int dumpsize
; /* amount of memory dumped */
141 off_t dumpbytes
; /* in bytes */
143 const char *kernel
; /* name of used kernel */
144 char *dirname
; /* directory to save dumps in */
145 char *ddname
; /* name of dump device */
146 dev_t dumpdev
; /* dump device */
147 dev_t dumpcdev
= NODEV
; /* dump device (char equivalent) */
148 int dumpfd
; /* read/write descriptor on dev */
149 kvm_t
*kd_dump
; /* kvm descriptor on dev */
150 time_t now
; /* current date */
151 char panic_mesg
[1024];
156 static int clear
, compress
, force
, verbose
; /* flags */
158 void check_kmem(void);
159 int check_space(void);
160 void clear_dump(void);
161 int Create(char *, int);
162 int dump_exists(void);
163 char *find_dev(dev_t
, mode_t
);
164 int get_crashtime(void);
165 void kmem_setup(void);
166 void Lseek(int, off_t
, int);
167 int main(int, char *[]);
168 int Open(const char *, int rw
);
169 char *rawname(char *s
);
170 void save_core(void);
172 void Write(int, void *, int);
175 main(int argc
, char *argv
[])
177 int ch
, level
, testonly
;
182 level
= 1; /* default to fastest gzip compression */
186 openlog("savecore", LOG_PERROR
, LOG_DAEMON
);
188 while ((ch
= getopt(argc
, argv
, "cdfnN:vzZ:")) != -1)
193 case 'd': /* Not documented. */
210 level
= (int)strtol(optarg
, &ep
, 10);
211 if (level
< 0 || level
> 9) {
212 (void)syslog(LOG_ERR
, "invalid compression %s",
224 if (argc
!= ((clear
|| testonly
) ? 0 : 1))
227 gzmode
[1] = level
+ '0';
234 if (clear
&& !testonly
) {
239 if (!dump_exists() && !force
)
243 /* If -n was passed and there was a dump, exit at level 0 */
249 syslog(LOG_ALERT
, "reboot after panic: %s", panic_mesg
);
251 syslog(LOG_ALERT
, "reboot");
253 if ((!get_crashtime() || !check_space()) && !force
)
266 char errbuf
[_POSIX2_LINE_MAX
];
270 * Some names we need for the currently running system, others for
271 * the system that was running when the dump was made. The values
272 * obtained from the current system are used to look for things in
273 * /dev/kmem that cannot be found in the kernel namelist, but are
274 * presumed to be the same (since the disk partitions are probably
277 kd_kern
= kvm_openfiles(kernel
, NULL
, NULL
, O_RDONLY
, errbuf
);
278 if (kd_kern
== NULL
) {
279 syslog(LOG_ERR
, "%s: kvm_openfiles: %s", kernel
, errbuf
);
282 if (kvm_nlist(kd_kern
, current_nl
) == -1)
283 syslog(LOG_ERR
, "%s: kvm_nlist: %s", kernel
,
284 kvm_geterr(kd_kern
));
286 for (i
= 0; cursyms
[i
] != -1; i
++) {
287 if (current_nl
[cursyms
[i
]].n_value
!= 0)
289 switch (cursyms
[i
]) {
295 syslog(LOG_ERR
, "%s: %s not in namelist",
296 kernel
, current_nl
[cursyms
[i
]].n_name
);
301 if (KREAD(kd_kern
, current_nl
[X_DUMPDEV
].n_value
, &dumpdev
) != 0) {
303 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_kern
));
306 if (dumpdev
== NODEV
) {
307 syslog(LOG_WARNING
, "no core dump (no dumpdev)");
313 if (KREAD(kd_kern
, current_nl
[X_DUMPLO
].n_value
, &l_dumplo
) != 0) {
315 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_kern
));
318 if (l_dumplo
== -1) {
319 syslog(LOG_WARNING
, "no core dump (invalid dumplo)");
322 dumplo
= DEV_BSIZE
* (off_t
) l_dumplo
;
326 (void)printf("dumplo = %lld (%ld * %ld)\n",
327 (long long)dumplo
, (long)(dumplo
/ DEV_BSIZE
), (long)DEV_BSIZE
);
328 if (KREAD(kd_kern
, current_nl
[X_DUMPMAG
].n_value
, &dumpmag
) != 0) {
330 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_kern
));
334 (void)kvm_read(kd_kern
, current_nl
[X_VERSION
].n_value
, vers
,
336 vers
[sizeof(vers
) - 1] = '\0';
338 if (current_nl
[X_DUMPCDEV
].n_value
!= 0) {
339 if (KREAD(kd_kern
, current_nl
[X_DUMPCDEV
].n_value
,
342 syslog(LOG_WARNING
, "kvm_read: %s",
343 kvm_geterr(kd_kern
));
346 ddname
= find_dev(dumpcdev
, S_IFCHR
);
348 ddname
= find_dev(dumpdev
, S_IFBLK
);
349 if (strncmp(ddname
, "/dev/cons", 8) == 0 ||
350 strncmp(ddname
, "/dev/tty", 7) == 0 ||
351 strncmp(ddname
, "/dev/pty", 7) == 0 ||
352 strncmp(ddname
, "/dev/pts", 7) == 0) {
353 syslog(LOG_ERR
, "dumpdev %s is tty; override kernel", ddname
);
356 dumpfd
= Open(ddname
, O_RDWR
);
358 kd_dump
= kvm_openfiles(kernel
, ddname
, NULL
, O_RDWR
, errbuf
);
359 if (kd_dump
== NULL
) {
360 syslog(LOG_ERR
, "%s: kvm_openfiles: %s", kernel
, errbuf
);
364 if (kvm_nlist(kd_dump
, dump_nl
) == -1)
365 syslog(LOG_ERR
, "%s: kvm_nlist: %s", kernel
,
366 kvm_geterr(kd_dump
));
368 for (i
= 0; dumpsyms
[i
] != -1; i
++)
369 if (dump_nl
[dumpsyms
[i
]].n_value
== 0 &&
370 dumpsyms
[i
] != X_TIME_SECOND
&&
371 dumpsyms
[i
] != X_TIME
) {
372 syslog(LOG_ERR
, "%s: %s not in namelist",
373 kernel
, dump_nl
[dumpsyms
[i
]].n_name
);
376 hdrsz
= kvm_dump_mkheader(kd_dump
, dumplo
);
379 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
380 * checks, ergo no dump is present...
383 syslog(LOG_WARNING
, "no core dump");
387 syslog(LOG_ERR
, "%s: kvm_dump_mkheader: %s", kernel
,
388 kvm_geterr(kd_dump
));
399 struct kern_msgbuf msgbuf
, *bufp
;
400 long panicloc
, panicstart
, panicend
;
401 char core_vers
[1024];
403 (void)kvm_read(kd_dump
, dump_nl
[X_VERSION
].n_value
, core_vers
,
405 core_vers
[sizeof(core_vers
) - 1] = '\0';
407 if (strcmp(vers
, core_vers
) != 0)
409 "warning: %s version mismatch:\n\t%s\nand\t%s\n",
410 kernel
, vers
, core_vers
);
412 panicstart
= panicend
= 0;
413 if (KREAD(kd_dump
, dump_nl
[X_PANICSTART
].n_value
, &panicstart
) != 0) {
415 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
418 if (KREAD(kd_dump
, dump_nl
[X_PANICEND
].n_value
, &panicend
) != 0) {
420 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
423 if (panicstart
!= 0 && panicend
!= 0) {
424 if (KREAD(kd_dump
, dump_nl
[X_MSGBUF
].n_value
, &bufp
)) {
426 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
429 if (kvm_read(kd_dump
, (long)bufp
, &msgbuf
,
430 offsetof(struct kern_msgbuf
, msg_bufc
)) !=
431 offsetof(struct kern_msgbuf
, msg_bufc
)) {
433 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
436 if (msgbuf
.msg_magic
!= MSG_MAGIC
) {
438 syslog(LOG_WARNING
, "msgbuf magic incorrect");
441 bufdata
= malloc(msgbuf
.msg_bufs
);
442 if (bufdata
== NULL
) {
444 syslog(LOG_WARNING
, "couldn't allocate space for msgbuf data");
447 if (kvm_read(kd_dump
, (long)&bufp
->msg_bufc
, bufdata
,
448 msgbuf
.msg_bufs
) != msgbuf
.msg_bufs
) {
450 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
455 while (panicstart
!= panicend
&& cp
< &panic_mesg
[sizeof(panic_mesg
)-1]) {
456 *cp
++ = bufdata
[panicstart
];
458 if (panicstart
>= msgbuf
.msg_bufs
)
461 /* Don't end in a new-line */
462 cp
= &panic_mesg
[strlen(panic_mesg
)] - 1;
465 panic_mesg
[sizeof(panic_mesg
) - 1] = '\0';
468 panicstr
= 1; /* anything not zero */
472 if (KREAD(kd_dump
, dump_nl
[X_PANICSTR
].n_value
, &panicstr
) != 0) {
474 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
481 if (KREAD(kd_dump
, panicloc
, cp
) != 0) {
483 syslog(LOG_WARNING
, "kvm_read: %s",
484 kvm_geterr(kd_dump
));
488 } while (*cp
++ && cp
< &panic_mesg
[sizeof(panic_mesg
)-1]);
489 panic_mesg
[sizeof(panic_mesg
) - 1] = '\0';
496 u_int32_t newdumpmag
;
498 if (KREAD(kd_dump
, dump_nl
[X_DUMPMAG
].n_value
, &newdumpmag
) != 0) {
500 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
504 /* Read the dump size. */
505 if (KREAD(kd_dump
, dump_nl
[X_DUMPSIZE
].n_value
, &dumpsize
) != 0) {
507 syslog(LOG_WARNING
, "kvm_read: %s", kvm_geterr(kd_dump
));
510 dumpbytes
= (off_t
)dumpsize
* getpagesize();
513 * Return zero if core dump doesn't seem to be there, and note
514 * it for syslog. This check and return happens after the dump size
515 * is read, so dumpsize is whether or not the core is valid (for -f).
517 if (newdumpmag
!= dumpmag
) {
520 "magic number mismatch (0x%x != 0x%x)",
521 newdumpmag
, dumpmag
);
522 syslog(LOG_WARNING
, "no core dump");
531 if (kvm_dump_inval(kd_dump
) == -1)
532 syslog(LOG_ERR
, "%s: kvm_dump_inval: %s", ddname
,
533 kvm_geterr(kd_dump
));
537 char buf
[1024 * 1024];
540 save_kernel(int ofd
, FILE *fp
, char *path
)
544 ifd
= Open(kernel
, O_RDONLY
);
545 while ((nr
= read(ifd
, buf
, sizeof(buf
))) > 0) {
547 nw
= fwrite(buf
, 1, nr
, fp
);
549 nw
= write(ofd
, buf
, nr
);
551 syslog(LOG_ERR
, "%s: %s",
552 path
, strerror(nw
== 0 ? EIO
: errno
));
554 "WARNING: kernel may be incomplete");
559 syslog(LOG_ERR
, "%s: %m", kernel
);
560 syslog(LOG_WARNING
, "WARNING: kernel may be incomplete");
566 ksymsget(u_long addr
, void *ptr
, size_t size
)
569 if ((size_t)kvm_read(kd_dump
, addr
, ptr
, size
) != size
) {
571 syslog(LOG_WARNING
, "kvm_read: %s",
572 kvm_geterr(kd_dump
));
579 save_ksyms(int ofd
, FILE *fp
, char *path
)
581 struct ksyms_hdr khdr
;
582 int nw
, symsz
, strsz
;
583 TAILQ_HEAD(, ksyms_symtab
) symtabs
;
584 struct ksyms_symtab st
, *stptr
;
587 /* Get basic info and ELF headers, check if ksyms was on. */
588 if (ksymsget(dump_nl
[X_KHDR
].n_value
, &khdr
, sizeof(khdr
)))
590 if (ksymsget(dump_nl
[X_SYMSZ
].n_value
, &symsz
, sizeof(symsz
)))
592 if (ksymsget(dump_nl
[X_STRSZ
].n_value
, &strsz
, sizeof(strsz
)))
594 if (symsz
== 0 || strsz
== 0)
597 /* Update the ELF section headers for symbols/strings. */
598 khdr
.kh_shdr
[SYMTAB
].sh_size
= symsz
;
599 khdr
.kh_shdr
[SYMTAB
].sh_info
= symsz
/ sizeof(Elf_Sym
);
600 khdr
.kh_shdr
[STRTAB
].sh_offset
= symsz
+
601 khdr
.kh_shdr
[SYMTAB
].sh_offset
;
602 khdr
.kh_shdr
[STRTAB
].sh_size
= strsz
;
604 /* Write out the ELF headers. */
606 nw
= fwrite(&khdr
, 1, sizeof(khdr
), fp
);
608 nw
= write(ofd
, &khdr
, sizeof(khdr
));
609 if (nw
!= sizeof(khdr
)) {
610 syslog(LOG_ERR
, "%s: %s",
611 path
, strerror(nw
== 0 ? EIO
: errno
));
613 "WARNING: kernel may be incomplete");
617 /* Dump symbol table. */
618 if (ksymsget(dump_nl
[X_SYMTABS
].n_value
, &symtabs
, sizeof(symtabs
)))
620 stptr
= TAILQ_FIRST(&symtabs
);
621 while (stptr
!= NULL
) {
622 if (ksymsget((u_long
)stptr
, &st
, sizeof(st
)))
624 stptr
= TAILQ_NEXT(&st
, sd_queue
);
625 if ((p
= malloc(st
.sd_symsize
)) == NULL
)
627 if (ksymsget((u_long
)st
.sd_symstart
, p
, st
.sd_symsize
)) {
632 nw
= fwrite(p
, 1, st
.sd_symsize
, fp
);
634 nw
= write(ofd
, p
, st
.sd_symsize
);
636 if (nw
!= st
.sd_symsize
) {
637 syslog(LOG_ERR
, "%s: %s",
638 path
, strerror(nw
== 0 ? EIO
: errno
));
640 "WARNING: kernel may be incomplete");
645 /* Dump string table. */
646 if (ksymsget(dump_nl
[X_SYMTABS
].n_value
, &symtabs
, sizeof(symtabs
)))
648 stptr
= TAILQ_FIRST(&symtabs
);
649 while (stptr
!= NULL
) {
650 if (ksymsget((u_long
)stptr
, &st
, sizeof(st
)))
652 stptr
= TAILQ_NEXT(&st
, sd_queue
);
653 if ((p
= malloc(st
.sd_symsize
)) == NULL
)
655 if (ksymsget((u_long
)st
.sd_strstart
, p
, st
.sd_strsize
)) {
660 nw
= fwrite(p
, 1, st
.sd_strsize
, fp
);
662 nw
= write(ofd
, p
, st
.sd_strsize
);
664 if (nw
!= st
.sd_strsize
) {
665 syslog(LOG_ERR
, "%s: %s",
666 path
, strerror(nw
== 0 ? EIO
: errno
));
668 "WARNING: kernel may be incomplete");
680 int bounds
, ifd
, nr
, nw
, ofd
, tryksyms
;
681 char *rawp
, path
[MAXPATHLEN
];
685 * Get the current number and update the bounds file. Do the update
686 * now, because may fail later and don't want to overwrite anything.
689 (void)snprintf(path
, sizeof(path
), "%s/bounds", dirname
);
690 if ((fp
= fopen(path
, "r")) == NULL
)
692 if (fgets(buf
, sizeof(buf
), fp
) == NULL
) {
694 err1
: syslog(LOG_WARNING
, "%s: %m", path
);
700 if ((fp
= fopen(path
, "w")) == NULL
)
701 syslog(LOG_ERR
, "%s: %m", path
);
703 (void)fprintf(fp
, "%d\n", bounds
+ 1);
707 /* Create the core file. */
708 (void)snprintf(path
, sizeof(path
), "%s/netbsd.%d.core%s",
709 dirname
, bounds
, compress
? ".gz" : "");
711 if ((fp
= zopen(path
, gzmode
)) == NULL
) {
712 syslog(LOG_ERR
, "%s: %m", path
);
716 ofd
= Create(path
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
717 fp
= fdopen(ofd
, "w");
719 syslog(LOG_ERR
, "%s: fdopen: %m", path
);
724 if (dumpcdev
== NODEV
) {
725 /* Open the raw device. */
726 rawp
= rawname(ddname
);
727 if ((ifd
= open(rawp
, O_RDONLY
)) == -1) {
728 syslog(LOG_WARNING
, "%s: %m; using block device",
737 /* Seek to the start of the core. */
738 Lseek(ifd
, dumplo
, SEEK_SET
);
740 if (kvm_dump_wrtheader(kd_dump
, fp
, (int32_t)dumpbytes
) == -1) {
741 syslog(LOG_ERR
, "kvm_dump_wrtheader: %s : %s", path
,
742 kvm_geterr(kd_dump
));
746 /* Copy the core file. */
747 syslog(LOG_NOTICE
, "writing %score to %s",
748 compress
? "compressed " : "", path
);
749 for (; dumpbytes
> (off_t
)0; dumpbytes
-= (off_t
)nr
) {
751 humanize_number(nbuf
, 7, dumpbytes
, "", HN_AUTOSCALE
, 0);
752 (void)printf("%7s\r", nbuf
);
753 (void)fflush(stdout
);
754 nr
= read(ifd
, buf
, MIN(dumpbytes
, (off_t
)sizeof(buf
)));
758 "WARNING: EOF on dump device");
760 syslog(LOG_ERR
, "%s: %m", rawp
);
763 nw
= fwrite(buf
, 1, nr
, fp
);
765 syslog(LOG_ERR
, "%s: %s",
766 path
, strerror(nw
== 0 ? EIO
: errno
));
767 err2
: syslog(LOG_WARNING
,
768 "WARNING: core may be incomplete");
773 if (dumpcdev
== NODEV
)
777 /* Create a kernel. */
778 (void)snprintf(path
, sizeof(path
), "%s/netbsd.%d%s",
779 dirname
, bounds
, compress
? ".gz" : "");
780 syslog(LOG_NOTICE
, "writing %skernel to %s",
781 compress
? "compressed " : "", path
);
782 for (tryksyms
= 1;; tryksyms
= 0) {
784 if ((fp
= zopen(path
, gzmode
)) == NULL
) {
785 syslog(LOG_ERR
, "%s: %m", path
);
789 ofd
= Create(path
, S_IRUSR
| S_IWUSR
);
791 if (!save_ksyms(ofd
, fp
, path
))
799 save_kernel(ofd
, fp
, path
);
809 * For development systems where the crash occurs during boot
819 find_dev(dev_t dev
, mode_t type
)
824 char *dp
, device
[MAXPATHLEN
+ 1], *p
;
827 if ((dfd
= opendir(_PATH_DEV
)) == NULL
) {
828 syslog(LOG_ERR
, "%s: %m", _PATH_DEV
);
831 strlcpy(device
, _PATH_DEV
, sizeof(device
));
832 p
= &device
[strlen(device
)];
833 l
= sizeof(device
) - strlen(device
);
834 while ((dir
= readdir(dfd
))) {
835 strlcpy(p
, dir
->d_name
, l
);
836 if (lstat(device
, &sb
)) {
837 syslog(LOG_ERR
, "%s: %m", device
);
840 if ((sb
.st_mode
& S_IFMT
) != type
)
842 if (dev
== sb
.st_rdev
) {
844 if ((dp
= strdup(device
)) == NULL
) {
845 syslog(LOG_ERR
, "%m");
852 syslog(LOG_ERR
, "can't find device %lld/%lld",
853 (long long)major(dev
), (long long)minor(dev
));
861 char name
[MAXPATHLEN
];
863 if ((sl
= strrchr(s
, '/')) == NULL
|| sl
[1] == '0') {
865 "can't make raw dump device name from %s", s
);
868 (void)snprintf(name
, sizeof(name
), "%.*s/r%s", (int)(sl
- s
), s
,
870 if ((sl
= strdup(name
)) == NULL
) {
871 syslog(LOG_ERR
, "%m");
880 time_t dumptime
; /* Time the dump was taken. */
881 struct timeval dtime
;
883 if (KREAD(kd_dump
, dump_nl
[X_TIME_SECOND
].n_value
, &dumptime
) != 0) {
884 if (KREAD(kd_dump
, dump_nl
[X_TIME
].n_value
, &dtime
) != 0) {
886 syslog(LOG_WARNING
, "kvm_read: %s (and _time_second is not defined also)", kvm_geterr(kd_dump
));
889 dumptime
= dtime
.tv_sec
;
893 syslog(LOG_ERR
, "dump time is zero");
896 (void)printf("savecore: system went down at %s", ctime(&dumptime
));
897 #define LEEWAY (60 * SECSPERDAY)
898 if (dumptime
< now
- LEEWAY
|| dumptime
> now
+ LEEWAY
) {
899 (void)printf("dump time is unreasonable\n");
909 off_t minfree
, spacefree
, kernelsize
, needed
;
911 struct statvfs fsbuf
;
912 char mbuf
[100], path
[MAXPATHLEN
];
914 /* XXX assume a reasonable default, unless we find a kernel. */
915 kernelsize
= 20 * 1024 * 1024;
916 if (!stat(kernel
, &st
)) kernelsize
= st
.st_blocks
* S_BLKSIZE
;
917 if (statvfs(dirname
, &fsbuf
) < 0) {
918 syslog(LOG_ERR
, "%s: %m", dirname
);
921 spacefree
= fsbuf
.f_bavail
;
922 spacefree
*= fsbuf
.f_frsize
;
925 (void)snprintf(path
, sizeof(path
), "%s/minfree", dirname
);
926 if ((fp
= fopen(path
, "r")) == NULL
)
929 if (fgets(mbuf
, sizeof(mbuf
), fp
) == NULL
)
932 minfree
= atoi(mbuf
);
936 needed
= (dumpbytes
+ kernelsize
) / 1024;
937 if (minfree
> 0 && spacefree
- needed
< minfree
) {
939 "no dump, not enough free space in %s", dirname
);
942 if (spacefree
- needed
< minfree
)
944 "dump performed, but free space threshold crossed");
949 Open(const char *name
, int rw
)
953 if ((fd
= open(name
, rw
, 0)) < 0) {
954 syslog(LOG_ERR
, "%s: %m", name
);
961 Lseek(int fd
, off_t off
, int flag
)
965 ret
= lseek(fd
, off
, flag
);
967 syslog(LOG_ERR
, "lseek: %m");
973 Create(char *file
, int mode
)
977 fd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
, mode
);
979 syslog(LOG_ERR
, "%s: %m", file
);
986 Write(int fd
, void *bp
, int size
)
990 if ((n
= write(fd
, bp
, size
)) < size
) {
991 syslog(LOG_ERR
, "write: %s", strerror(n
== -1 ? errno
: EIO
));
999 (void)syslog(LOG_ERR
,
1000 "usage: savecore [-cfnvz] [-N system] [-Z level] directory");