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