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