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