Bring in a transport-independent RPC (TI-RPC).
[dragonfly.git] / sbin / savecore / savecore.c
1 /*-
2  * Copyright (c) 1986, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1986, 1992, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)savecore.c       8.3 (Berkeley) 1/2/94
35  * $FreeBSD: src/sbin/savecore/savecore.c,v 1.28.2.14 2005/01/05 09:14:34 maxim Exp $
36  * $DragonFly: src/sbin/savecore/savecore.c,v 1.15 2007/05/19 06:54:29 dillon Exp $
37  */
38
39 #define _KERNEL_STRUCTURES
40
41 #include <sys/param.h>
42
43 #undef _KERNEL_STRUCTURES
44
45 #include <sys/stat.h>
46 #include <sys/mount.h>
47 #include <sys/syslog.h>
48 #include <sys/sysctl.h>
49
50 #include <vm/vm.h>
51 #include <vm/vm_param.h>
52 #include <vm/pmap.h>
53
54 #include <dirent.h>
55 #include <fcntl.h>
56 #include <nlist.h>
57 #include <paths.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <err.h>
63
64 extern FILE *zopen(const char *fname, const char *mode);
65
66 #define ok(number) ((number) - kernbase)
67
68 struct nlist current_nl[] = {   /* Namelist for currently running system. */
69 #define X_DUMPLO        0
70         { "_dumplo64", 0, 0, 0, 0 },
71 #define X_TIME          1
72         { "_time_second", 0, 0, 0, 0 },
73 #define X_DUMPSIZE      2
74         { "_dumpsize", 0, 0, 0, 0 },
75 #define X_VERSION       3
76         { "_version", 0, 0, 0, 0 },
77 #define X_PANICSTR      4
78         { "_panicstr", 0, 0, 0, 0 },
79 #define X_DUMPMAG       5
80         { "_dumpmag", 0, 0, 0, 0 },
81 #define X_KERNBASE      6
82         { "_kernbase", 0, 0, 0, 0 },
83 #define X_MAXMEM        7
84         { "_Maxmem", 0, 0, 0, 0 },
85         { "", 0, 0, 0, 0 },
86 };
87 int cursyms[] = { X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
88 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
89
90 struct nlist dump_nl[] = {               /* Name list for dumped system. */
91         { "_dumplo64", 0, 0, 0, 0 },     /* Entries MUST be the same as  */
92         { "_time_second", 0, 0, 0, 0 },  /* those in current_nl[].       */
93         { "_dumpsize", 0, 0, 0, 0 },
94         { "_version", 0, 0, 0, 0 },
95         { "_panicstr", 0, 0, 0, 0 },
96         { "_dumpmag", 0, 0, 0, 0 },
97         { "_kernbase", 0, 0, 0, 0 },
98         { "_Maxmem", 0, 0, 0, 0 },
99         { "", 0, 0, 0, 0 },
100 };
101
102 /* Types match kernel declarations. */
103 u_long  dumpmag;                        /* magic number in dump */
104
105 /* Based on kernel variables, but with more convenient types. */
106 off_t   dumplo;                         /* where dump starts on dumpdev */
107 off_t   dumpsize;                       /* amount of memory dumped */
108
109 char    *kernel;                        /* user-specified kernel */
110 char    *savedir;                       /* directory to save dumps in */
111 char    ddname[MAXPATHLEN];             /* name of dump device */
112 dev_t   dumpdev;                        /* dump device */
113 int     dumpfd;                         /* read/write descriptor on char dev */
114 time_t  now;                            /* current date */
115 char    panic_mesg[1024];               /* panic message */
116 int     panicstr;                       /* flag: dump was caused by panic */
117 char    vers[1024];                     /* version of kernel that crashed */
118 char    *physmem;                       /* physmem value used with dumped session */
119 u_int64_t dkdumplo;                     /* directly specified kernel dumplo value */
120
121 u_long  kernbase;                       /* offset of kvm to core file */
122
123 static int      clear, compress, force, verbose, directdumplo;  /* flags */
124 static int      keep;                   /* keep dump on device */
125
126 static void     check_kmem(void);
127 static int      check_space(void);
128 static void     clear_dump(void);
129 static void     DumpRead(int fd, void *bp, int size, off_t off, int flag);
130 static void     DumpWrite(int fd, void *bp, int size, off_t off, int flag);
131 static int      dump_exists(void);
132 static void     find_dev(dev_t);
133 static int      get_crashtime(void);
134 static void     get_dumpsize(void);
135 static void     kmem_setup(void);
136 static void     Lseek(int, off_t, int);
137 static int      Open(const char *, int rw);
138 static int      Read(int, void *, int);
139 static void     save_core(void);
140 static void     usage(void);
141 static int      verify_dev(char *, dev_t);
142 static void     Write(int, void *, int);
143 static void     kdumplo_adjust(char *cp, int kmem, u_int64_t *kdumplop);
144
145 int
146 main(int argc, char **argv)
147 {
148         int ch;
149         char *ep;
150
151         openlog("savecore", LOG_PERROR, LOG_DAEMON);
152
153         while ((ch = getopt(argc, argv, "cD:dfkN:vzP:B:")) != -1)
154                 switch(ch) {
155                 case 'c':
156                         clear = 1;
157                         break;
158                 case 'D':
159                         strncpy(ddname, optarg, sizeof(ddname));
160                         break;
161                 case 'd':               /* Not documented. */
162                 case 'v':
163                         verbose = 1;
164                         break;
165                 case 'f':
166                         force = 1;
167                         break;
168                 case 'k':
169                         keep = 1;
170                         break;
171                 case 'N':
172                         kernel = optarg;
173                         break;
174                 case 'z':
175                         compress = 1;
176                         break;
177                 case 'P':
178                         physmem = optarg;
179                         break;
180                 case 'B':
181                         directdumplo = 1;
182                         dkdumplo = strtouq(optarg, &ep, 10);
183                         if (*ep != '\0')
184                                 errx(1, "invalid offset: '%s'", optarg);
185                         break;
186                 case '?':
187                 default:
188                         usage();
189                 }
190         argc -= optind;
191         argv += optind;
192
193         if (!clear) {
194                 if (argc != 1 && argc != 2)
195                         usage();
196                 savedir = argv[0];
197         }
198         if (argc == 2)
199                 kernel = argv[1];
200
201         time(&now);
202         kmem_setup();
203
204         if (clear) {
205                 clear_dump();
206                 exit(0);
207         }
208
209         if (!dump_exists() && !force)
210                 exit(1);
211
212         check_kmem();
213
214         if (panicstr)
215                 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
216         else
217                 syslog(LOG_ALERT, "reboot");
218
219         get_dumpsize();
220
221         if ((!get_crashtime() || !check_space()) && !force)
222                 exit(1);
223
224         save_core();
225
226         if (!keep)
227                 clear_dump();
228         
229         exit(0);
230 }
231
232 static void
233 kmem_setup(void)
234 {
235         int kmem, i;
236         const char *dump_sys;
237         size_t len;
238         u_int64_t kdumplo;      /* block number where dump starts on dumpdev */
239         char *p;
240
241         /*
242          * Some names we need for the currently running system, others for
243          * the system that was running when the dump was made.  The values
244          * obtained from the current system are used to look for things in
245          * /dev/kmem that cannot be found in the dump_sys namelist, but are
246          * presumed to be the same (since the disk partitions are probably
247          * the same!)
248          */
249         if ((nlist(getbootfile(), current_nl)) == -1)
250                 syslog(LOG_ERR, "%s: nlist: %m", getbootfile());
251         for (i = 0; cursyms[i] != -1; i++)
252                 if (current_nl[cursyms[i]].n_value == 0) {
253                         syslog(LOG_ERR, "%s: %s not in namelist",
254                             getbootfile(), current_nl[cursyms[i]].n_name);
255                         exit(1);
256                 }
257
258         dump_sys = kernel ? kernel : getbootfile();
259         if ((nlist(dump_sys, dump_nl)) == -1)
260                 syslog(LOG_ERR, "%s: nlist: %m", dump_sys);
261         for (i = 0; dumpsyms[i] != -1; i++)
262                 if (dump_nl[dumpsyms[i]].n_value == 0) {
263                         syslog(LOG_ERR, "%s: %s not in namelist",
264                             dump_sys, dump_nl[dumpsyms[i]].n_name);
265                         exit(1);
266                 }
267
268         if (dump_nl[X_KERNBASE].n_value != 0)
269                 kernbase = dump_nl[X_KERNBASE].n_value;
270         else
271                 kernbase = KERNBASE;
272
273         if (*ddname == 0) {
274                 len = sizeof dumpdev;
275                 if (sysctlbyname("kern.dumpdev", &dumpdev, &len, NULL, 0) == -1) {
276                         syslog(LOG_ERR, "sysctl: kern.dumpdev: %m");
277                         exit(1);
278                 }
279                 if (dumpdev == NODEV) {
280                         syslog(LOG_WARNING, "no core dump (no dumpdev)");
281                         exit(1);
282                 }
283                 find_dev(dumpdev);
284         }
285
286         kmem = Open(_PATH_KMEM, O_RDONLY);
287         if (directdumplo) {
288                 kdumplo = dkdumplo;
289         } else {
290                 Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
291                 Read(kmem, &kdumplo, sizeof(kdumplo));
292                 if (physmem)
293                         kdumplo_adjust(physmem, kmem, &kdumplo);
294         }
295                 dumplo = kdumplo * DEV_BSIZE;
296         if (verbose)
297                 printf("dumplo = %lld (%lld * %d)\n",
298                     (long long)dumplo, kdumplo, DEV_BSIZE);
299         Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
300         Read(kmem, &dumpmag, sizeof(dumpmag));
301         dumpfd = Open(ddname, O_RDWR);
302         if (kernel)
303                 return;
304
305         lseek(kmem, (off_t)current_nl[X_VERSION].n_value, SEEK_SET);
306         Read(kmem, vers, sizeof(vers));
307         vers[sizeof(vers) - 1] = '\0';
308         p = strchr(vers, '\n');
309         if (p)
310                 p[1] = '\0';
311
312         /* Don't fclose(fp), we use kmem later. */
313 }
314
315 static void
316 check_kmem(void)
317 {
318         char core_vers[1024], *p;
319
320         DumpRead(dumpfd, core_vers, sizeof(core_vers),
321             (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
322         core_vers[sizeof(core_vers) - 1] = '\0';
323         p = strchr(core_vers, '\n');
324         if (p)
325                 p[1] = '\0';
326         if (strcmp(vers, core_vers) && kernel == 0)
327                 syslog(LOG_WARNING,
328                     "warning: %s version mismatch:\n\t\"%s\"\nand\t\"%s\"\n",
329                     getbootfile(), vers, core_vers);
330         DumpRead(dumpfd, &panicstr, sizeof(panicstr),
331             (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
332         if (panicstr) {
333                 DumpRead(dumpfd, panic_mesg, sizeof(panic_mesg),
334                     (off_t)(dumplo + ok(panicstr)), L_SET);
335         }
336 }
337
338 /*
339  * Clear the magic number in the dump header.
340  */
341 static void
342 clear_dump(void)
343 {
344         u_long newdumpmag;
345
346         newdumpmag = 0;
347         DumpWrite(dumpfd, &newdumpmag, sizeof(newdumpmag),
348             (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
349         close(dumpfd);
350 }
351
352 /*
353  * Check if a dump exists by looking for a magic number in the dump
354  * header.
355  */
356 static int
357 dump_exists(void)
358 {
359         u_long newdumpmag;
360
361         DumpRead(dumpfd, &newdumpmag, sizeof(newdumpmag),
362             (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
363         if (newdumpmag != dumpmag) {
364                 if (verbose)
365                         syslog(LOG_WARNING, "magic number mismatch (%lx != %lx)",
366                             newdumpmag, dumpmag);
367                 syslog(LOG_WARNING, "no core dump");
368                 return (0);
369         }
370         return (1);
371 }
372
373 char buf[1024 * 1024];
374 #define BLOCKSIZE (1<<12)
375 #define BLOCKMASK (~(BLOCKSIZE-1))
376
377 /*
378  * Save the core dump.
379  */
380 static void
381 save_core(void)
382 {
383         FILE *fp;
384         int bounds, ifd, nr, nw;
385         int hs, he = 0;         /* start and end of hole */
386         char path[MAXPATHLEN];
387         mode_t oumask;
388
389         /*
390          * Get the current number and update the bounds file.  Do the update
391          * now, because may fail later and don't want to overwrite anything.
392          */
393         snprintf(path, sizeof(path), "%s/bounds", savedir);
394         if ((fp = fopen(path, "r")) == NULL)
395                 goto err1;
396         if (fgets(buf, sizeof(buf), fp) == NULL) {
397                 if (ferror(fp))
398 err1:                   syslog(LOG_WARNING, "%s: %m", path);
399                 bounds = 0;
400         } else
401                 bounds = atoi(buf);
402         if (fp != NULL)
403                 fclose(fp);
404         if ((fp = fopen(path, "w")) == NULL)
405                 syslog(LOG_ERR, "%s: %m", path);
406         else {
407                 fprintf(fp, "%d\n", bounds + 1);
408                 fclose(fp);
409         }
410
411         /* Create the core file. */
412         oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
413         snprintf(path, sizeof(path), "%s/vmcore.%d%s",
414             savedir, bounds, compress ? ".gz" : "");
415         if (compress)
416                 fp = zopen(path, "w");
417         else
418                 fp = fopen(path, "w");
419         if (fp == NULL) {
420                 syslog(LOG_ERR, "%s: %m", path);
421                 exit(1);
422         }
423         umask(oumask);
424
425         /* Seek to the start of the core. */
426         Lseek(dumpfd, (off_t)dumplo, L_SET);
427
428         /* Copy the core file. */
429         syslog(LOG_NOTICE, "writing %score to %s",
430             compress ? "compressed " : "", path);
431         for (; dumpsize > 0; dumpsize -= nr) {
432                 printf("%6ldK\r", (long)(dumpsize / 1024));
433                 fflush(stdout);
434                 nr = read(dumpfd, buf, MIN(dumpsize, sizeof(buf)));
435                 if (nr <= 0) {
436                         if (nr == 0)
437                                 syslog(LOG_WARNING,
438                                     "WARNING: EOF on dump device");
439                         else
440                                 syslog(LOG_ERR, "%s: %m", ddname);
441                         goto err2;
442                 }
443                 if (compress) {
444                         nw = fwrite(buf, 1, nr, fp);
445                 } else {
446                 for (nw = 0; nw < nr; nw = he) {
447                         /* find a contiguous block of zeroes */
448                         for (hs = nw; hs < nr; hs += BLOCKSIZE) {
449                                 for (he = hs; he < nr && buf[he] == 0; ++he)
450                                         /* nothing */ ;
451
452                                 /* is the hole long enough to matter? */
453                                 if (he >= hs + BLOCKSIZE)
454                                         break;
455                         }
456                         
457                         /* back down to a block boundary */
458                         he &= BLOCKMASK;
459
460                         /*
461                          * 1) Don't go beyond the end of the buffer.
462                          * 2) If the end of the buffer is less than
463                          *    BLOCKSIZE bytes away, we're at the end
464                          *    of the file, so just grab what's left.
465                          */
466                         if (hs + BLOCKSIZE > nr)
467                                 hs = he = nr;
468                         
469                         /*
470                          * At this point, we have a partial ordering:
471                          *     nw <= hs <= he <= nr
472                          * If hs > nw, buf[nw..hs] contains non-zero data.
473                          * If he > hs, buf[hs..he] is all zeroes.
474                          */
475                         if (hs > nw)
476                                 if (fwrite(buf + nw, hs - nw, 1, fp) != 1)
477                                         break;
478                         if (he > hs)
479                                 if (fseeko(fp, he - hs, SEEK_CUR) == -1)
480                                         break;
481                         }
482                 }
483                 if (nw != nr) {
484                         syslog(LOG_ERR, "%s: %m", path);
485 err2:                   syslog(LOG_WARNING,
486                             "WARNING: vmcore may be incomplete");
487                         printf("\n");
488                         exit(1);
489                 }
490         }
491
492         fclose(fp);
493
494         /* Copy the kernel. */
495         ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY);
496         snprintf(path, sizeof(path), "%s/kernel.%d%s",
497             savedir, bounds, compress ? ".gz" : "");
498         if (compress)
499                 fp = zopen(path, "w");
500         else
501                 fp = fopen(path, "w");
502         if (fp == NULL) {
503                 syslog(LOG_ERR, "%s: %m", path);
504                 exit(1);
505         }
506         syslog(LOG_NOTICE, "writing %skernel to %s",
507             compress ? "compressed " : "", path);
508         while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
509                 nw = fwrite(buf, 1, nr, fp);
510                 if (nw != nr) {
511                         syslog(LOG_ERR, "%s: %m", path);
512                         syslog(LOG_WARNING,
513                             "WARNING: kernel may be incomplete");
514                         exit(1);
515                 }
516         }
517         if (nr < 0) {
518                 syslog(LOG_ERR, "%s: %m", kernel ? kernel : getbootfile());
519                 syslog(LOG_WARNING,
520                     "WARNING: kernel may be incomplete");
521                 exit(1);
522         }
523         fclose(fp);
524         close(ifd);
525 }
526
527 /*
528  * Verify that the specified device node exists and matches the
529  * specified device.
530  */
531 static int
532 verify_dev(char *name, dev_t dev)
533 {
534         struct stat sb;
535
536         if (lstat(name, &sb) == -1)
537                 return (-1);
538         if (!S_ISCHR(sb.st_mode) || sb.st_rdev != dev)
539                 return (-1);
540         return (0);
541 }
542
543 /*
544  * Find the dump device.
545  *
546  *  1) try devname(3); see if it returns something sensible
547  *  2) scan /dev for the desired node
548  *  3) as a last resort, try to create the node we need
549  */
550 static void
551 find_dev(dev_t dev)
552 {
553         struct dirent *ent;
554         char *dn, *dnp;
555         DIR *d;
556
557         strcpy(ddname, _PATH_DEV);
558         dnp = ddname + sizeof _PATH_DEV - 1;
559         if ((dn = devname(dev, S_IFCHR)) != NULL) {
560                 strcpy(dnp, dn);
561                 if (verify_dev(ddname, dev) == 0)
562                         return;
563         }
564         if ((d = opendir(_PATH_DEV)) != NULL) {
565                 while ((ent = readdir(d))) {
566                         strcpy(dnp, ent->d_name);
567                         if (verify_dev(ddname, dev) == 0) {
568                                 closedir(d);
569                                 return;
570                         }
571                 }
572                 closedir(d);
573         }
574         strcpy(dnp, "dump");
575         if (mknod(ddname, S_IFCHR|S_IRUSR|S_IWUSR, dev) == 0)
576                 return;
577         syslog(LOG_ERR, "can't find device %d/%#x", major(dev), minor(dev));
578         exit(1);
579 }
580
581 /*
582  * Extract the date and time of the crash from the dump header, and
583  * make sure it looks sane (within one week of current date and time).
584  */
585 static int
586 get_crashtime(void)
587 {
588         time_t dumptime;                        /* Time the dump was taken. */
589
590         DumpRead(dumpfd, &dumptime, sizeof(dumptime),
591             (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
592         if (dumptime == 0) {
593                 if (verbose)
594                         syslog(LOG_ERR, "dump time is zero");
595                 return (0);
596         }
597         printf("savecore: system went down at %s", ctime(&dumptime));
598 #define LEEWAY  (7 * 86400)
599         if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
600                 printf("dump time is unreasonable\n");
601                 return (0);
602         }
603         return (1);
604 }
605
606 /*
607  * Extract the size of the dump from the dump header.
608  */
609 static void
610 get_dumpsize(void)
611 {
612         int kdumpsize;  /* Number of pages in dump. */
613
614         /* Read the dump size. */
615         DumpRead(dumpfd, &kdumpsize, sizeof(kdumpsize),
616             (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
617         dumpsize = (off_t)kdumpsize * getpagesize();
618 }
619
620 /*
621  * Check that sufficient space is available on the disk that holds the
622  * save directory.
623  */
624 static int
625 check_space(void)
626 {
627         FILE *fp;
628         const char *tkernel;
629         off_t minfree, spacefree, totfree, kernelsize, needed;
630         struct stat st;
631         struct statfs fsbuf;
632         char mybuf[100], path[MAXPATHLEN];
633
634         tkernel = kernel ? kernel : getbootfile();
635         if (stat(tkernel, &st) < 0) {
636                 syslog(LOG_ERR, "%s: %m", tkernel);
637                 exit(1);
638         }
639         kernelsize = st.st_blocks * S_BLKSIZE;
640
641         if (statfs(savedir, &fsbuf) < 0) {
642                 syslog(LOG_ERR, "%s: %m", savedir);
643                 exit(1);
644         }
645         spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
646         totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
647
648         snprintf(path, sizeof(path), "%s/minfree", savedir);
649         if ((fp = fopen(path, "r")) == NULL)
650                 minfree = 0;
651         else {
652                 if (fgets(mybuf, sizeof(mybuf), fp) == NULL)
653                         minfree = 0;
654                 else
655                         minfree = atoi(mybuf);
656                 fclose(fp);
657         }
658
659         needed = (dumpsize + kernelsize) / 1024;
660         if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
661                 syslog(LOG_WARNING,
662         "no dump, not enough free space on device (%lld available, need %lld)",
663                     (long long)(minfree > 0 ? spacefree : totfree),
664                     (long long)needed);
665                 return (0);
666         }
667         if (spacefree - needed < 0)
668                 syslog(LOG_WARNING,
669                     "dump performed, but free space threshold crossed");
670         return (1);
671 }
672
673 static int
674 Open(const char *name, int rw)
675 {
676         int fd;
677
678         if ((fd = open(name, rw, 0)) < 0) {
679                 syslog(LOG_ERR, "%s: %m", name);
680                 exit(1);
681         }
682         return (fd);
683 }
684
685 static int
686 Read(int fd, void *bp, int size)
687 {
688         int nr;
689
690         nr = read(fd, bp, size);
691         if (nr != size) {
692                 syslog(LOG_ERR, "read: %m");
693                 exit(1);
694         }
695         return (nr);
696 }
697
698 static void
699 Lseek(int fd, off_t off, int flag)
700 {
701         off_t ret;
702
703         ret = lseek(fd, off, flag);
704         if (ret == -1) {
705                 syslog(LOG_ERR, "lseek: %m");
706                 exit(1);
707         }
708 }
709
710 /*
711  * DumpWrite and DumpRead block io requests to the * dump device.
712  */
713 #define DUMPBUFSIZE     8192
714 static void
715 DumpWrite(int fd, void *bp, int size, off_t off, int flag)
716 {
717         unsigned char mybuf[DUMPBUFSIZE], *p, *q;
718         off_t pos;
719         int i, j;
720         
721         if (flag != L_SET) {
722                 syslog(LOG_ERR, "lseek: not LSET");
723                 exit(2);
724         }
725         q = bp;
726         while (size) {
727                 pos = off & ~(DUMPBUFSIZE - 1);
728                 Lseek(fd, pos, flag);
729                 Read(fd, mybuf, sizeof(mybuf));
730                 j = off & (DUMPBUFSIZE - 1);
731                 p = mybuf + j;
732                 i = size;
733                 if (i > DUMPBUFSIZE - j)
734                         i = DUMPBUFSIZE - j;
735                 memcpy(p, q, i);
736                 Lseek(fd, pos, flag);
737                 Write(fd, mybuf, sizeof(mybuf));
738                 size -= i;
739                 q += i;
740                 off += i;
741         }
742 }
743
744 static void
745 DumpRead(int fd, void *bp, int size, off_t off, int flag)
746 {
747         unsigned char mybuf[DUMPBUFSIZE], *p, *q;
748         off_t pos;
749         int i, j;
750         
751         if (flag != L_SET) {
752                 syslog(LOG_ERR, "lseek: not LSET");
753                 exit(2);
754         }
755         q = bp;
756         while (size) {
757                 pos = off & ~(DUMPBUFSIZE - 1);
758                 Lseek(fd, pos, flag);
759                 Read(fd, mybuf, sizeof(mybuf));
760                 j = off & (DUMPBUFSIZE - 1);
761                 p = mybuf + j;
762                 i = size;
763                 if (i > DUMPBUFSIZE - j)
764                         i = DUMPBUFSIZE - j;
765                 memcpy(q, p, i);
766                 size -= i;
767                 q += i;
768                 off += i;
769         }
770 }
771
772 static void
773 Write(int fd, void *bp, int size)
774 {
775         int n;
776
777         if ((n = write(fd, bp, size)) < size) {
778                 syslog(LOG_ERR, "write: %m");
779                 exit(1);
780         }
781 }
782
783 static void
784 kdumplo_adjust(char *cp, int kmem, u_int64_t *kdumplop)
785 {
786         uint64_t AllowMem, sanity, Maxmem, CurrMaxmem;
787         char *ep;
788
789         /* based on getmemsize() in i386/i386/machdep.c */ 
790         sanity = AllowMem = strtouq(cp, &ep, 0);
791         if ((ep != cp) && (*ep != 0)) {
792                 switch(*ep) {
793                 case 'g':
794                 case 'G':
795                         AllowMem <<= 10;
796                 case 'm':
797                 case 'M':
798                         AllowMem <<= 10;
799                 case 'k':
800                 case 'K':
801                         AllowMem <<= 10;
802                         break;
803                 default:
804                         AllowMem = 0;
805                 }
806                 if (AllowMem < sanity)
807                         AllowMem = 0;
808         }
809         if (AllowMem == 0)
810                 errx(1, "invalid memory size: '%s'\n", cp);
811         else
812                 Maxmem = atop(AllowMem);
813         
814         Lseek(kmem, (off_t)current_nl[X_MAXMEM].n_value, L_SET);
815         Read(kmem, &CurrMaxmem, sizeof(CurrMaxmem));
816
817         /* based on setdumpdev() in kern_shutdown.c */
818         *kdumplop += (u_int64_t)CurrMaxmem * PAGE_SIZE / DEV_BSIZE;
819         *kdumplop -= (u_int64_t)Maxmem * PAGE_SIZE / DEV_BSIZE;
820 }
821
822 static void
823 usage(void)
824 {
825         syslog(LOG_ERR,
826                "usage: savecore [-cfkvz] [-N system] [-P physmem|-B blkno] directory");
827         exit(1);
828 }