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