savecore.c revision 1.67 1 /* $NetBSD: savecore.c,v 1.67 2006/06/07 20:56:19 kardel 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.67 2006/06/07 20:56:19 kardel 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 free(bufdata);
413 goto nomsguf;
414 }
415 cp = panic_mesg;
416 while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) {
417 *cp++ = bufdata[panicstart];
418 panicstart++;
419 if (panicstart >= msgbuf.msg_bufs)
420 panicstart = 0;
421 }
422 /* Don't end in a new-line */
423 cp = &panic_mesg[strlen(panic_mesg)] - 1;
424 if (*cp == '\n')
425 *cp = '\0';
426 panic_mesg[sizeof(panic_mesg) - 1] = '\0';
427 free(bufdata);
428
429 panicstr = 1; /* anything not zero */
430 return;
431 }
432 nomsguf:
433 if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
434 if (verbose)
435 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
436 return;
437 }
438 if (panicstr) {
439 cp = panic_mesg;
440 panicloc = panicstr;
441 do {
442 if (KREAD(kd_dump, panicloc, cp) != 0) {
443 if (verbose)
444 syslog(LOG_WARNING, "kvm_read: %s",
445 kvm_geterr(kd_dump));
446 break;
447 }
448 panicloc++;
449 } while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
450 panic_mesg[sizeof(panic_mesg) - 1] = '\0';
451 }
452 }
453
454 int
455 dump_exists(void)
456 {
457 u_int32_t newdumpmag;
458
459 if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
460 if (verbose)
461 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
462 return (0);
463 }
464
465 /* Read the dump size. */
466 if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
467 if (verbose)
468 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
469 return (0);
470 }
471 dumpbytes = (off_t)dumpsize * getpagesize();
472
473 /*
474 * Return zero if core dump doesn't seem to be there, and note
475 * it for syslog. This check and return happens after the dump size
476 * is read, so dumpsize is whether or not the core is valid (for -f).
477 */
478 if (newdumpmag != dumpmag) {
479 if (verbose)
480 syslog(LOG_WARNING,
481 "magic number mismatch (0x%x != 0x%x)",
482 newdumpmag, dumpmag);
483 syslog(LOG_WARNING, "no core dump");
484 return (0);
485 }
486 return (1);
487 }
488
489 void
490 clear_dump(void)
491 {
492 if (kvm_dump_inval(kd_dump) == -1)
493 syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
494 kvm_geterr(kd_dump));
495
496 }
497
498 char buf[1024 * 1024];
499
500 void
501 save_core(void)
502 {
503 FILE *fp;
504 int bounds, ifd, nr, nw, ofd;
505 char *rawp, path[MAXPATHLEN];
506
507 ofd = -1;
508 /*
509 * Get the current number and update the bounds file. Do the update
510 * now, because may fail later and don't want to overwrite anything.
511 */
512 umask(066);
513 (void)snprintf(path, sizeof(path), "%s/bounds", dirname);
514 if ((fp = fopen(path, "r")) == NULL)
515 goto err1;
516 if (fgets(buf, sizeof(buf), fp) == NULL) {
517 if (ferror(fp))
518 err1: syslog(LOG_WARNING, "%s: %m", path);
519 bounds = 0;
520 } else
521 bounds = atoi(buf);
522 if (fp != NULL)
523 (void)fclose(fp);
524 if ((fp = fopen(path, "w")) == NULL)
525 syslog(LOG_ERR, "%s: %m", path);
526 else {
527 (void)fprintf(fp, "%d\n", bounds + 1);
528 (void)fclose(fp);
529 }
530
531 /* Create the core file. */
532 (void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
533 dirname, bounds, compress ? ".gz" : "");
534 if (compress) {
535 if ((fp = zopen(path, gzmode)) == NULL) {
536 syslog(LOG_ERR, "%s: %m", path);
537 exit(1);
538 }
539 } else {
540 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
541 fp = fdopen(ofd, "w");
542 if (fp == NULL) {
543 syslog(LOG_ERR, "%s: fdopen: %m", path);
544 exit(1);
545 }
546 }
547
548 /* Open the raw device. */
549 rawp = rawname(ddname);
550 if ((ifd = open(rawp, O_RDONLY)) == -1) {
551 syslog(LOG_WARNING, "%s: %m; using block device", rawp);
552 ifd = dumpfd;
553 }
554
555 /* Seek to the start of the core. */
556 Lseek(ifd, dumplo, SEEK_SET);
557
558 if (kvm_dump_wrtheader(kd_dump, fp, (int32_t)dumpbytes) == -1) {
559 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
560 kvm_geterr(kd_dump));
561 exit(1);
562 }
563
564 /* Copy the core file. */
565 syslog(LOG_NOTICE, "writing %score to %s",
566 compress ? "compressed " : "", path);
567 for (; dumpbytes > (off_t)0; dumpbytes -= (off_t)nr) {
568 char nbuf[7];
569 humanize_number(nbuf, 7, dumpbytes, "", HN_AUTOSCALE, 0);
570 (void)printf("%7s\r", nbuf);
571 (void)fflush(stdout);
572 nr = read(ifd, buf, MIN(dumpbytes, sizeof(buf)));
573 if (nr <= 0) {
574 if (nr == 0)
575 syslog(LOG_WARNING,
576 "WARNING: EOF on dump device");
577 else
578 syslog(LOG_ERR, "%s: %m", rawp);
579 goto err2;
580 }
581 nw = fwrite(buf, 1, nr, fp);
582 if (nw != nr) {
583 syslog(LOG_ERR, "%s: %s",
584 path, strerror(nw == 0 ? EIO : errno));
585 err2: syslog(LOG_WARNING,
586 "WARNING: core may be incomplete");
587 (void)printf("\n");
588 exit(1);
589 }
590 }
591 (void)close(ifd);
592 (void)fclose(fp);
593
594 /* Copy the kernel. */
595 ifd = Open(kernel, O_RDONLY);
596 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
597 dirname, bounds, compress ? ".gz" : "");
598 if (compress) {
599 if ((fp = zopen(path, gzmode)) == NULL) {
600 syslog(LOG_ERR, "%s: %m", path);
601 exit(1);
602 }
603 } else
604 ofd = Create(path, S_IRUSR | S_IWUSR);
605 syslog(LOG_NOTICE, "writing %skernel to %s",
606 compress ? "compressed " : "", path);
607 while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
608 if (compress)
609 nw = fwrite(buf, 1, nr, fp);
610 else
611 nw = write(ofd, buf, nr);
612 if (nw != nr) {
613 syslog(LOG_ERR, "%s: %s",
614 path, strerror(nw == 0 ? EIO : errno));
615 syslog(LOG_WARNING,
616 "WARNING: kernel may be incomplete");
617 exit(1);
618 }
619 }
620 if (nr < 0) {
621 syslog(LOG_ERR, "%s: %m", kernel);
622 syslog(LOG_WARNING, "WARNING: kernel may be incomplete");
623 exit(1);
624 }
625 if (compress)
626 (void)fclose(fp);
627 else
628 (void)close(ofd);
629 }
630
631 char *
632 find_dev(dev_t dev, int type)
633 {
634 DIR *dfd;
635 struct dirent *dir;
636 struct stat sb;
637 char *dp, device[MAXPATHLEN + 1], *p;
638 size_t l;
639
640 if ((dfd = opendir(_PATH_DEV)) == NULL) {
641 syslog(LOG_ERR, "%s: %m", _PATH_DEV);
642 exit(1);
643 }
644 strlcpy(device, _PATH_DEV, sizeof(device));
645 p = &device[strlen(device)];
646 l = sizeof(device) - strlen(device);
647 while ((dir = readdir(dfd))) {
648 strlcpy(p, dir->d_name, l);
649 if (lstat(device, &sb)) {
650 syslog(LOG_ERR, "%s: %m", device);
651 continue;
652 }
653 if ((sb.st_mode & S_IFMT) != type)
654 continue;
655 if (dev == sb.st_rdev) {
656 closedir(dfd);
657 if ((dp = strdup(device)) == NULL) {
658 syslog(LOG_ERR, "%m");
659 exit(1);
660 }
661 return (dp);
662 }
663 }
664 closedir(dfd);
665 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
666 exit(1);
667 }
668
669 char *
670 rawname(char *s)
671 {
672 char *sl;
673 char name[MAXPATHLEN];
674
675 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
676 syslog(LOG_ERR,
677 "can't make raw dump device name from %s", s);
678 return (s);
679 }
680 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
681 sl + 1);
682 if ((sl = strdup(name)) == NULL) {
683 syslog(LOG_ERR, "%m");
684 exit(1);
685 }
686 return (sl);
687 }
688
689 int
690 get_crashtime(void)
691 {
692 time_t dumptime; /* Time the dump was taken. */
693 #ifndef __HAVE_TIMECOUNTER
694 struct timeval dtime;
695 #endif
696
697 #ifdef __HAVE_TIMECOUNTER
698 if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dumptime) != 0) {
699 if (verbose)
700 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
701 return (0);
702 }
703 #else
704 if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dtime) != 0) {
705 if (verbose)
706 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
707 return (0);
708 }
709 dumptime = dtime.tv_sec;
710 #endif
711 if (dumptime == 0) {
712 if (verbose)
713 syslog(LOG_ERR, "dump time is zero");
714 return (0);
715 }
716 (void)printf("savecore: system went down at %s", ctime(&dumptime));
717 #define LEEWAY (60 * SECSPERDAY)
718 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
719 (void)printf("dump time is unreasonable\n");
720 return (0);
721 }
722 return (1);
723 }
724
725 int
726 check_space(void)
727 {
728 FILE *fp;
729 off_t minfree, spacefree, kernelsize, needed;
730 struct stat st;
731 struct statvfs fsbuf;
732 char mbuf[100], path[MAXPATHLEN];
733
734 #ifdef __GNUC__
735 (void) &minfree;
736 #endif
737
738 if (stat(kernel, &st) < 0) {
739 syslog(LOG_ERR, "%s: %m", kernel);
740 exit(1);
741 }
742 kernelsize = st.st_blocks * S_BLKSIZE;
743 if (statvfs(dirname, &fsbuf) < 0) {
744 syslog(LOG_ERR, "%s: %m", dirname);
745 exit(1);
746 }
747 spacefree = fsbuf.f_bavail;
748 spacefree *= fsbuf.f_frsize;
749 spacefree /= 1024;
750
751 (void)snprintf(path, sizeof(path), "%s/minfree", dirname);
752 if ((fp = fopen(path, "r")) == NULL)
753 minfree = 0;
754 else {
755 if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
756 minfree = 0;
757 else
758 minfree = atoi(mbuf);
759 (void)fclose(fp);
760 }
761
762 needed = (dumpbytes + kernelsize) / 1024;
763 if (minfree > 0 && spacefree - needed < minfree) {
764 syslog(LOG_WARNING,
765 "no dump, not enough free space in %s", dirname);
766 return (0);
767 }
768 if (spacefree - needed < minfree)
769 syslog(LOG_WARNING,
770 "dump performed, but free space threshold crossed");
771 return (1);
772 }
773
774 int
775 Open(const char *name, int rw)
776 {
777 int fd;
778
779 if ((fd = open(name, rw, 0)) < 0) {
780 syslog(LOG_ERR, "%s: %m", name);
781 exit(1);
782 }
783 return (fd);
784 }
785
786 void
787 Lseek(int fd, off_t off, int flag)
788 {
789 off_t ret;
790
791 ret = lseek(fd, off, flag);
792 if (ret == -1) {
793 syslog(LOG_ERR, "lseek: %m");
794 exit(1);
795 }
796 }
797
798 int
799 Create(char *file, int mode)
800 {
801 int fd;
802
803 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
804 if (fd < 0) {
805 syslog(LOG_ERR, "%s: %m", file);
806 exit(1);
807 }
808 return (fd);
809 }
810
811 void
812 Write(int fd, void *bp, int size)
813 {
814 int n;
815
816 if ((n = write(fd, bp, size)) < size) {
817 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
818 exit(1);
819 }
820 }
821
822 void
823 usage(void)
824 {
825 (void)syslog(LOG_ERR,
826 "usage: savecore [-cfnvz] [-N system] [-Z level] directory");
827 exit(1);
828 }
829