savecore.c revision 265075
112732Sfyuan/*- 212732Sfyuan * Copyright (c) 2002 Poul-Henning Kamp 312732Sfyuan * Copyright (c) 2002 Networks Associates Technology, Inc. 412732Sfyuan * All rights reserved. 512732Sfyuan * 612732Sfyuan * This software was developed for the FreeBSD Project by Poul-Henning Kamp 712732Sfyuan * and NAI Labs, the Security Research Division of Network Associates, Inc. 812732Sfyuan * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 912732Sfyuan * DARPA CHATS research program. 1012732Sfyuan * 1112732Sfyuan * Redistribution and use in source and binary forms, with or without 1212732Sfyuan * modification, are permitted provided that the following conditions 1312732Sfyuan * are met: 1412732Sfyuan * 1. Redistributions of source code must retain the above copyright 1512732Sfyuan * notice, this list of conditions and the following disclaimer. 1612732Sfyuan * 2. Redistributions in binary form must reproduce the above copyright 1712732Sfyuan * notice, this list of conditions and the following disclaimer in the 1812732Sfyuan * documentation and/or other materials provided with the distribution. 1912732Sfyuan * 3. The names of the authors may not be used to endorse or promote 2012732Sfyuan * products derived from this software without specific prior written 2112732Sfyuan * permission. 2212732Sfyuan * 2312732Sfyuan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2412732Sfyuan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2512732Sfyuan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2612732Sfyuan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2712732Sfyuan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2812732Sfyuan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2912732Sfyuan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3012732Sfyuan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3112732Sfyuan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3212732Sfyuan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3312732Sfyuan * SUCH DAMAGE. 3412732Sfyuan * 3512732Sfyuan * Copyright (c) 1986, 1992, 1993 3612732Sfyuan * The Regents of the University of California. All rights reserved. 3712732Sfyuan * 3812732Sfyuan * Redistribution and use in source and binary forms, with or without 3912732Sfyuan * modification, are permitted provided that the following conditions 4012732Sfyuan * are met: 4112732Sfyuan * 1. Redistributions of source code must retain the above copyright 4212732Sfyuan * notice, this list of conditions and the following disclaimer. 4312732Sfyuan * 2. Redistributions in binary form must reproduce the above copyright 4412732Sfyuan * notice, this list of conditions and the following disclaimer in the 4512732Sfyuan * documentation and/or other materials provided with the distribution. 4612732Sfyuan * 4. Neither the name of the University nor the names of its contributors 4712732Sfyuan * may be used to endorse or promote products derived from this software 4812732Sfyuan * without specific prior written permission. 4912732Sfyuan * 5012732Sfyuan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5112732Sfyuan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5212732Sfyuan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5312732Sfyuan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5412732Sfyuan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5512732Sfyuan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5612732Sfyuan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5712732Sfyuan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5812732Sfyuan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5912732Sfyuan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6012732Sfyuan * SUCH DAMAGE. 6112732Sfyuan */ 6212732Sfyuan 6312732Sfyuan#include <sys/cdefs.h> 6412732Sfyuan__FBSDID("$FreeBSD: stable/10/sbin/savecore/savecore.c 265075 2014-04-29 03:49:40Z markj $"); 6512732Sfyuan 6612732Sfyuan#include <sys/param.h> 6712732Sfyuan#include <sys/disk.h> 6812732Sfyuan#include <sys/kerneldump.h> 6912732Sfyuan#include <sys/mount.h> 7012732Sfyuan#include <sys/stat.h> 7112732Sfyuan#include <errno.h> 7212732Sfyuan#include <fcntl.h> 7312732Sfyuan#include <fstab.h> 7412732Sfyuan#include <paths.h> 7512732Sfyuan#include <signal.h> 7612732Sfyuan#include <stdarg.h> 7712732Sfyuan#include <stdio.h> 7812732Sfyuan#include <stdlib.h> 7912732Sfyuan#include <string.h> 8012732Sfyuan#include <syslog.h> 8112732Sfyuan#include <time.h> 8212732Sfyuan#include <unistd.h> 8312732Sfyuan 8412732Sfyuan/* The size of the buffer used for I/O. */ 8512732Sfyuan#define BUFFERSIZE (1024*1024) 8612732Sfyuan 8712732Sfyuan#define STATUS_BAD 0 8812732Sfyuan#define STATUS_GOOD 1 8912732Sfyuan#define STATUS_UNKNOWN 2 9012732Sfyuan 9112732Sfyuanstatic int checkfor, compress, clear, force, keep, verbose; /* flags */ 9212732Sfyuanstatic int nfound, nsaved, nerr; /* statistics */ 9312732Sfyuanstatic int maxdumps; 9412732Sfyuan 9512732Sfyuanextern FILE *zopen(const char *, const char *); 9612732Sfyuan 9712732Sfyuanstatic sig_atomic_t got_siginfo; 9812732Sfyuanstatic void infohandler(int); 9912732Sfyuan 10012732Sfyuanstatic void 10112732Sfyuanprintheader(FILE *f, const struct kerneldumpheader *h, const char *device, 10212732Sfyuan int bounds, const int status) 10312732Sfyuan{ 10412732Sfyuan uint64_t dumplen; 10512732Sfyuan time_t t; 10612732Sfyuan const char *stat_str; 10712732Sfyuan 10812732Sfyuan fprintf(f, "Dump header from device %s\n", device); 10912732Sfyuan fprintf(f, " Architecture: %s\n", h->architecture); 11012732Sfyuan fprintf(f, " Architecture Version: %u\n", 11112732Sfyuan dtoh32(h->architectureversion)); 11212732Sfyuan dumplen = dtoh64(h->dumplength); 11312732Sfyuan fprintf(f, " Dump Length: %lldB (%lld MB)\n", (long long)dumplen, 11412732Sfyuan (long long)(dumplen >> 20)); 11512732Sfyuan fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 11612732Sfyuan t = dtoh64(h->dumptime); 11712732Sfyuan fprintf(f, " Dumptime: %s", ctime(&t)); 11812732Sfyuan fprintf(f, " Hostname: %s\n", h->hostname); 11912732Sfyuan fprintf(f, " Magic: %s\n", h->magic); 12012732Sfyuan fprintf(f, " Version String: %s", h->versionstring); 12112732Sfyuan fprintf(f, " Panic String: %s\n", h->panicstring); 12212732Sfyuan fprintf(f, " Dump Parity: %u\n", h->parity); 12312732Sfyuan fprintf(f, " Bounds: %d\n", bounds); 12412732Sfyuan 12512732Sfyuan switch(status) { 12612732Sfyuan case STATUS_BAD: 12712732Sfyuan stat_str = "bad"; 12812732Sfyuan break; 12912732Sfyuan case STATUS_GOOD: 13012732Sfyuan stat_str = "good"; 13112732Sfyuan break; 13212732Sfyuan default: 13312732Sfyuan stat_str = "unknown"; 13412732Sfyuan } 13512732Sfyuan fprintf(f, " Dump Status: %s\n", stat_str); 13612732Sfyuan fflush(f); 13712732Sfyuan} 13812732Sfyuan 13912732Sfyuanstatic int 14012732Sfyuangetbounds(void) { 14112732Sfyuan FILE *fp; 14212732Sfyuan char buf[6]; 14312732Sfyuan int ret; 14412732Sfyuan 14512732Sfyuan ret = 0; 14612732Sfyuan 14712732Sfyuan if ((fp = fopen("bounds", "r")) == NULL) { 14812732Sfyuan if (verbose) 14912732Sfyuan printf("unable to open bounds file, using 0\n"); 15012732Sfyuan return (ret); 15112732Sfyuan } 15212732Sfyuan 15312732Sfyuan if (fgets(buf, sizeof buf, fp) == NULL) { 15412732Sfyuan syslog(LOG_WARNING, "unable to read from bounds, using 0"); 15512732Sfyuan fclose(fp); 15612732Sfyuan return (ret); 15712732Sfyuan } 15812732Sfyuan 15912732Sfyuan errno = 0; 16012732Sfyuan ret = (int)strtol(buf, NULL, 10); 16112732Sfyuan if (ret == 0 && (errno == EINVAL || errno == ERANGE)) 16212732Sfyuan syslog(LOG_WARNING, "invalid value found in bounds, using 0"); 16312732Sfyuan return (ret); 16412732Sfyuan} 16512732Sfyuan 16612732Sfyuanstatic void 16712732Sfyuanwritebounds(int bounds) { 16812732Sfyuan FILE *fp; 16912732Sfyuan 17012732Sfyuan if ((fp = fopen("bounds", "w")) == NULL) { 17112732Sfyuan syslog(LOG_WARNING, "unable to write to bounds file: %m"); 17212732Sfyuan return; 17312732Sfyuan } 17412732Sfyuan 17512732Sfyuan if (verbose) 17612732Sfyuan printf("bounds number: %d\n", bounds); 17712732Sfyuan 17812732Sfyuan fprintf(fp, "%d\n", bounds); 17912732Sfyuan fclose(fp); 18012732Sfyuan} 18112732Sfyuan 18212732Sfyuanstatic off_t 18312732Sfyuanfile_size(const char *path) 18412732Sfyuan{ 18512732Sfyuan struct stat sb; 18612732Sfyuan 18712732Sfyuan /* Ignore all errors, those file may not exists. */ 18812732Sfyuan if (stat(path, &sb) == -1) 18912732Sfyuan return (0); 19012732Sfyuan return (sb.st_size); 19112732Sfyuan} 19212732Sfyuan 19312732Sfyuanstatic off_t 19412732Sfyuansaved_dump_size(int bounds) 19512732Sfyuan{ 19612732Sfyuan static char path[PATH_MAX]; 19712732Sfyuan off_t dumpsize; 19812732Sfyuan 19912732Sfyuan dumpsize = 0; 20012732Sfyuan 20112732Sfyuan (void)snprintf(path, sizeof(path), "info.%d", bounds); 20212732Sfyuan dumpsize += file_size(path); 20312732Sfyuan (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 20412732Sfyuan dumpsize += file_size(path); 20512732Sfyuan (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 20612732Sfyuan dumpsize += file_size(path); 20712732Sfyuan (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 20812732Sfyuan dumpsize += file_size(path); 20912732Sfyuan (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 21012732Sfyuan dumpsize += file_size(path); 21112732Sfyuan 21212732Sfyuan return (dumpsize); 21312732Sfyuan} 21412732Sfyuan 21512732Sfyuanstatic void 21612732Sfyuansaved_dump_remove(int bounds) 21712732Sfyuan{ 21812732Sfyuan static char path[PATH_MAX]; 21912732Sfyuan 22012732Sfyuan (void)snprintf(path, sizeof(path), "info.%d", bounds); 22112732Sfyuan (void)unlink(path); 22212732Sfyuan (void)snprintf(path, sizeof(path), "vmcore.%d", bounds); 22312732Sfyuan (void)unlink(path); 22412732Sfyuan (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds); 22512732Sfyuan (void)unlink(path); 22612732Sfyuan (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds); 22712732Sfyuan (void)unlink(path); 22812732Sfyuan (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds); 22912732Sfyuan (void)unlink(path); 23012732Sfyuan} 23112732Sfyuan 23212732Sfyuanstatic void 23312732Sfyuansymlinks_remove(void) 23412732Sfyuan{ 23512732Sfyuan 23612732Sfyuan (void)unlink("info.last"); 23712732Sfyuan (void)unlink("vmcore.last"); 23812732Sfyuan (void)unlink("vmcore.last.gz"); 23912732Sfyuan (void)unlink("textdump.tar.last"); 24012732Sfyuan (void)unlink("textdump.tar.last.gz"); 24112732Sfyuan} 24212732Sfyuan 24312732Sfyuan/* 24412732Sfyuan * Check that sufficient space is available on the disk that holds the 24512732Sfyuan * save directory. 24612732Sfyuan */ 24712732Sfyuanstatic int 24812732Sfyuancheck_space(const char *savedir, off_t dumpsize, int bounds) 24912732Sfyuan{ 25012732Sfyuan FILE *fp; 25112732Sfyuan off_t minfree, spacefree, totfree, needed; 25212732Sfyuan struct statfs fsbuf; 25312732Sfyuan char buf[100]; 25412732Sfyuan 25512732Sfyuan if (statfs(".", &fsbuf) < 0) { 25612732Sfyuan syslog(LOG_ERR, "%s: %m", savedir); 25712732Sfyuan exit(1); 25812732Sfyuan } 25912732Sfyuan spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 26012732Sfyuan totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 26112732Sfyuan 26212732Sfyuan if ((fp = fopen("minfree", "r")) == NULL) 26312732Sfyuan minfree = 0; 26412732Sfyuan else { 26512732Sfyuan if (fgets(buf, sizeof(buf), fp) == NULL) 26612732Sfyuan minfree = 0; 26712732Sfyuan else 26812732Sfyuan minfree = atoi(buf); 26912732Sfyuan (void)fclose(fp); 27012732Sfyuan } 27112732Sfyuan 27212732Sfyuan needed = dumpsize / 1024 + 2; /* 2 for info file */ 27312732Sfyuan needed -= saved_dump_size(bounds); 27412732Sfyuan if ((minfree > 0 ? spacefree : totfree) - needed < minfree) { 27512732Sfyuan syslog(LOG_WARNING, 27612732Sfyuan "no dump, not enough free space on device (%lld available, need %lld)", 27712732Sfyuan (long long)(minfree > 0 ? spacefree : totfree), 27812732Sfyuan (long long)needed); 27912732Sfyuan return (0); 28012732Sfyuan } 28112732Sfyuan if (spacefree - needed < 0) 28212732Sfyuan syslog(LOG_WARNING, 28312732Sfyuan "dump performed, but free space threshold crossed"); 28412732Sfyuan return (1); 28512732Sfyuan} 28612732Sfyuan 28712732Sfyuan#define BLOCKSIZE (1<<12) 28812732Sfyuan#define BLOCKMASK (~(BLOCKSIZE-1)) 28912732Sfyuan 29012732Sfyuanstatic int 29112732SfyuanDoRegularFile(int fd, off_t dumpsize, char *buf, const char *device, 29212732Sfyuan const char *filename, FILE *fp) 29312732Sfyuan{ 29412732Sfyuan int he, hs, nr, nw, wl; 29512732Sfyuan off_t dmpcnt, origsize; 29612732Sfyuan 29712732Sfyuan dmpcnt = 0; 29812732Sfyuan origsize = dumpsize; 29912732Sfyuan he = 0; 30012732Sfyuan while (dumpsize > 0) { 30112732Sfyuan wl = BUFFERSIZE; 30212732Sfyuan if (wl > dumpsize) 30312732Sfyuan wl = dumpsize; 30412732Sfyuan nr = read(fd, buf, wl); 30512732Sfyuan if (nr != wl) { 30612732Sfyuan if (nr == 0) 30712732Sfyuan syslog(LOG_WARNING, 30812732Sfyuan "WARNING: EOF on dump device"); 30912732Sfyuan else 31012732Sfyuan syslog(LOG_ERR, "read error on %s: %m", device); 31112732Sfyuan nerr++; 31212732Sfyuan return (-1); 31312732Sfyuan } 31412732Sfyuan if (compress) { 31512732Sfyuan nw = fwrite(buf, 1, wl, fp); 31612732Sfyuan } else { 31712732Sfyuan for (nw = 0; nw < nr; nw = he) { 31812732Sfyuan /* find a contiguous block of zeroes */ 31912732Sfyuan for (hs = nw; hs < nr; hs += BLOCKSIZE) { 32012732Sfyuan for (he = hs; he < nr && buf[he] == 0; 32112732Sfyuan ++he) 32212732Sfyuan /* nothing */ ; 32312732Sfyuan /* is the hole long enough to matter? */ 32412732Sfyuan if (he >= hs + BLOCKSIZE) 32512732Sfyuan break; 32612732Sfyuan } 32712732Sfyuan 32812732Sfyuan /* back down to a block boundary */ 32912732Sfyuan he &= BLOCKMASK; 33012732Sfyuan 33112732Sfyuan /* 33212732Sfyuan * 1) Don't go beyond the end of the buffer. 33312732Sfyuan * 2) If the end of the buffer is less than 33412732Sfyuan * BLOCKSIZE bytes away, we're at the end 33512732Sfyuan * of the file, so just grab what's left. 33612732Sfyuan */ 33712732Sfyuan if (hs + BLOCKSIZE > nr) 33812732Sfyuan hs = he = nr; 33912732Sfyuan 34012732Sfyuan /* 34112732Sfyuan * At this point, we have a partial ordering: 34212732Sfyuan * nw <= hs <= he <= nr 34312732Sfyuan * If hs > nw, buf[nw..hs] contains non-zero data. 34412732Sfyuan * If he > hs, buf[hs..he] is all zeroes. 34512732Sfyuan */ 34612732Sfyuan if (hs > nw) 34712732Sfyuan if (fwrite(buf + nw, hs - nw, 1, fp) 34812732Sfyuan != 1) 34912732Sfyuan break; 35012732Sfyuan if (he > hs) 35112732Sfyuan if (fseeko(fp, he - hs, SEEK_CUR) == -1) 35212732Sfyuan break; 35312732Sfyuan } 35412732Sfyuan } 35512732Sfyuan if (nw != wl) { 35612732Sfyuan syslog(LOG_ERR, 35712732Sfyuan "write error on %s file: %m", filename); 35812732Sfyuan syslog(LOG_WARNING, 35912732Sfyuan "WARNING: vmcore may be incomplete"); 36012732Sfyuan nerr++; 36112732Sfyuan return (-1); 36212732Sfyuan } 36312732Sfyuan if (verbose) { 36412732Sfyuan dmpcnt += wl; 36512732Sfyuan printf("%llu\r", (unsigned long long)dmpcnt); 36612732Sfyuan fflush(stdout); 36712732Sfyuan } 36812732Sfyuan dumpsize -= wl; 36912732Sfyuan if (got_siginfo) { 37012732Sfyuan printf("%s %.1lf%%\n", filename, (100.0 - (100.0 * 37112732Sfyuan (double)dumpsize / (double)origsize))); 37212732Sfyuan got_siginfo = 0; 37312732Sfyuan } 37412732Sfyuan } 37512732Sfyuan return (0); 37612732Sfyuan} 37712732Sfyuan 37812732Sfyuan/* 37912732Sfyuan * Specialized version of dump-reading logic for use with textdumps, which 38012732Sfyuan * are written backwards from the end of the partition, and must be reversed 38112732Sfyuan * before being written to the file. Textdumps are small, so do a bit less 38212732Sfyuan * work to optimize/sparsify. 38312732Sfyuan */ 38412732Sfyuanstatic int 38512732SfyuanDoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf, 38612732Sfyuan const char *device, const char *filename, FILE *fp) 38712732Sfyuan{ 38812732Sfyuan int nr, nw, wl; 38912732Sfyuan off_t dmpcnt, totsize; 39012732Sfyuan 39112732Sfyuan totsize = dumpsize; 39212732Sfyuan dmpcnt = 0; 39312732Sfyuan wl = 512; 39412732Sfyuan if ((dumpsize % wl) != 0) { 39512732Sfyuan syslog(LOG_ERR, "textdump uneven multiple of 512 on %s", 39612732Sfyuan device); 39712732Sfyuan nerr++; 39812732Sfyuan return (-1); 39912732Sfyuan } 40012732Sfyuan while (dumpsize > 0) { 40112732Sfyuan nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl); 40212732Sfyuan if (nr != wl) { 40312732Sfyuan if (nr == 0) 40412732Sfyuan syslog(LOG_WARNING, 40512732Sfyuan "WARNING: EOF on dump device"); 40612732Sfyuan else 40712732Sfyuan syslog(LOG_ERR, "read error on %s: %m", device); 40812732Sfyuan nerr++; 40912732Sfyuan return (-1); 41012732Sfyuan } 41112732Sfyuan nw = fwrite(buf, 1, wl, fp); 41212732Sfyuan if (nw != wl) { 41312732Sfyuan syslog(LOG_ERR, 41412732Sfyuan "write error on %s file: %m", filename); 41512732Sfyuan syslog(LOG_WARNING, 41612732Sfyuan "WARNING: textdump may be incomplete"); 41712732Sfyuan nerr++; 41812732Sfyuan return (-1); 41912732Sfyuan } 42012732Sfyuan if (verbose) { 42112732Sfyuan dmpcnt += wl; 42212732Sfyuan printf("%llu\r", (unsigned long long)dmpcnt); 42312732Sfyuan fflush(stdout); 42412732Sfyuan } 42512732Sfyuan dumpsize -= wl; 42612732Sfyuan } 42712732Sfyuan return (0); 42812732Sfyuan} 42912732Sfyuan 43012732Sfyuanstatic void 43112732SfyuanDoFile(const char *savedir, const char *device) 43212732Sfyuan{ 43312732Sfyuan static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; 43412732Sfyuan static char *buf = NULL; 43512732Sfyuan struct kerneldumpheader kdhf, kdhl; 43612732Sfyuan off_t mediasize, dumpsize, firsthd, lasthd; 43712732Sfyuan FILE *info, *fp; 43812732Sfyuan mode_t oumask; 43912732Sfyuan int fd, fdinfo, error; 44012732Sfyuan int bounds, status; 44112732Sfyuan u_int sectorsize; 44212732Sfyuan int istextdump; 44312732Sfyuan 44412732Sfyuan bounds = getbounds(); 44512732Sfyuan mediasize = 0; 44612732Sfyuan status = STATUS_UNKNOWN; 44712732Sfyuan 44812732Sfyuan if (maxdumps > 0 && bounds == maxdumps) 44912732Sfyuan bounds = 0; 45012732Sfyuan 45112732Sfyuan if (buf == NULL) { 45212732Sfyuan buf = malloc(BUFFERSIZE); 45312732Sfyuan if (buf == NULL) { 45412732Sfyuan syslog(LOG_ERR, "%m"); 45512732Sfyuan return; 45612732Sfyuan } 45712732Sfyuan } 45812732Sfyuan 45912732Sfyuan if (verbose) 46012732Sfyuan printf("checking for kernel dump on device %s\n", device); 46112732Sfyuan 46212732Sfyuan fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR); 46312732Sfyuan if (fd < 0) { 46412732Sfyuan syslog(LOG_ERR, "%s: %m", device); 46512732Sfyuan return; 46612732Sfyuan } 46712732Sfyuan 46812732Sfyuan error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 46912732Sfyuan if (!error) 47012732Sfyuan error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 47112732Sfyuan if (error) { 47212732Sfyuan syslog(LOG_ERR, 47312732Sfyuan "couldn't find media and/or sector size of %s: %m", device); 47412732Sfyuan goto closefd; 47512732Sfyuan } 47612732Sfyuan 47712732Sfyuan if (verbose) { 47812732Sfyuan printf("mediasize = %lld\n", (long long)mediasize); 47912732Sfyuan printf("sectorsize = %u\n", sectorsize); 48012732Sfyuan } 48112732Sfyuan 48212732Sfyuan lasthd = mediasize - sectorsize; 48312732Sfyuan lseek(fd, lasthd, SEEK_SET); 48412732Sfyuan error = read(fd, &kdhl, sizeof kdhl); 48512732Sfyuan if (error != sizeof kdhl) { 48612732Sfyuan syslog(LOG_ERR, 48712732Sfyuan "error reading last dump header at offset %lld in %s: %m", 48812732Sfyuan (long long)lasthd, device); 48912732Sfyuan goto closefd; 49012732Sfyuan } 49112732Sfyuan istextdump = 0; 49212732Sfyuan if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) { 49312732Sfyuan if (verbose) 49412732Sfyuan printf("textdump magic on last dump header on %s\n", 49512732Sfyuan device); 49612732Sfyuan istextdump = 1; 49712732Sfyuan if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) { 49812732Sfyuan syslog(LOG_ERR, 49912732Sfyuan "unknown version (%d) in last dump header on %s", 50012732Sfyuan dtoh32(kdhl.version), device); 50112732Sfyuan 50212732Sfyuan status = STATUS_BAD; 50312732Sfyuan if (force == 0) 50412732Sfyuan goto closefd; 50512732Sfyuan } 50612732Sfyuan } else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) == 50712732Sfyuan 0) { 50812732Sfyuan if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 50912732Sfyuan syslog(LOG_ERR, 51012732Sfyuan "unknown version (%d) in last dump header on %s", 51112732Sfyuan dtoh32(kdhl.version), device); 51212732Sfyuan 51312732Sfyuan status = STATUS_BAD; 51412732Sfyuan if (force == 0) 51512732Sfyuan goto closefd; 51612732Sfyuan } 51712732Sfyuan } else { 51812732Sfyuan if (verbose) 51912732Sfyuan printf("magic mismatch on last dump header on %s\n", 52012732Sfyuan device); 52112732Sfyuan 52212732Sfyuan status = STATUS_BAD; 52312732Sfyuan if (force == 0) 52412732Sfyuan goto closefd; 52512732Sfyuan 52612732Sfyuan if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED, 52712732Sfyuan sizeof kdhl.magic) == 0) { 52812732Sfyuan if (verbose) 52912732Sfyuan printf("forcing magic on %s\n", device); 53012732Sfyuan memcpy(kdhl.magic, KERNELDUMPMAGIC, 53112732Sfyuan sizeof kdhl.magic); 53212732Sfyuan } else { 53312732Sfyuan syslog(LOG_ERR, "unable to force dump - bad magic"); 53412732Sfyuan goto closefd; 53512732Sfyuan } 53612732Sfyuan if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 53712732Sfyuan syslog(LOG_ERR, 53812732Sfyuan "unknown version (%d) in last dump header on %s", 539 dtoh32(kdhl.version), device); 540 541 status = STATUS_BAD; 542 if (force == 0) 543 goto closefd; 544 } 545 } 546 547 nfound++; 548 if (clear) 549 goto nuke; 550 551 if (kerneldump_parity(&kdhl)) { 552 syslog(LOG_ERR, 553 "parity error on last dump header on %s", device); 554 nerr++; 555 status = STATUS_BAD; 556 if (force == 0) 557 goto closefd; 558 } 559 dumpsize = dtoh64(kdhl.dumplength); 560 firsthd = lasthd - dumpsize - sizeof kdhf; 561 lseek(fd, firsthd, SEEK_SET); 562 error = read(fd, &kdhf, sizeof kdhf); 563 if (error != sizeof kdhf) { 564 syslog(LOG_ERR, 565 "error reading first dump header at offset %lld in %s: %m", 566 (long long)firsthd, device); 567 nerr++; 568 goto closefd; 569 } 570 571 if (verbose >= 2) { 572 printf("First dump headers:\n"); 573 printheader(stdout, &kdhf, device, bounds, -1); 574 575 printf("\nLast dump headers:\n"); 576 printheader(stdout, &kdhl, device, bounds, -1); 577 printf("\n"); 578 } 579 580 if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 581 syslog(LOG_ERR, 582 "first and last dump headers disagree on %s", device); 583 nerr++; 584 status = STATUS_BAD; 585 if (force == 0) 586 goto closefd; 587 } else { 588 status = STATUS_GOOD; 589 } 590 591 if (checkfor) { 592 printf("A dump exists on %s\n", device); 593 close(fd); 594 exit(0); 595 } 596 597 if (kdhl.panicstring[0]) 598 syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring); 599 else 600 syslog(LOG_ALERT, "reboot"); 601 602 if (verbose) 603 printf("Checking for available free space\n"); 604 605 if (!check_space(savedir, dumpsize, bounds)) { 606 nerr++; 607 goto closefd; 608 } 609 610 writebounds(bounds + 1); 611 612 saved_dump_remove(bounds); 613 614 snprintf(infoname, sizeof(infoname), "info.%d", bounds); 615 616 /* 617 * Create or overwrite any existing dump header files. 618 */ 619 fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600); 620 if (fdinfo < 0) { 621 syslog(LOG_ERR, "%s: %m", infoname); 622 nerr++; 623 goto closefd; 624 } 625 oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ 626 if (compress) { 627 snprintf(corename, sizeof(corename), "%s.%d.gz", 628 istextdump ? "textdump.tar" : "vmcore", bounds); 629 fp = zopen(corename, "w"); 630 } else { 631 snprintf(corename, sizeof(corename), "%s.%d", 632 istextdump ? "textdump.tar" : "vmcore", bounds); 633 fp = fopen(corename, "w"); 634 } 635 if (fp == NULL) { 636 syslog(LOG_ERR, "%s: %m", corename); 637 close(fdinfo); 638 nerr++; 639 goto closefd; 640 } 641 (void)umask(oumask); 642 643 info = fdopen(fdinfo, "w"); 644 645 if (info == NULL) { 646 syslog(LOG_ERR, "fdopen failed: %m"); 647 nerr++; 648 goto closefd; 649 } 650 651 if (verbose) 652 printheader(stdout, &kdhl, device, bounds, status); 653 654 printheader(info, &kdhl, device, bounds, status); 655 fclose(info); 656 657 syslog(LOG_NOTICE, "writing %score to %s/%s", 658 compress ? "compressed " : "", savedir, corename); 659 660 if (istextdump) { 661 if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device, 662 corename, fp) < 0) 663 goto closeall; 664 } else { 665 if (DoRegularFile(fd, dumpsize, buf, device, corename, fp) 666 < 0) 667 goto closeall; 668 } 669 if (verbose) 670 printf("\n"); 671 672 if (fclose(fp) < 0) { 673 syslog(LOG_ERR, "error on %s: %m", corename); 674 nerr++; 675 goto closefd; 676 } 677 678 symlinks_remove(); 679 if (symlink(infoname, "info.last") == -1) { 680 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 681 savedir, "info.last"); 682 } 683 if (compress) { 684 snprintf(linkname, sizeof(linkname), "%s.last.gz", 685 istextdump ? "textdump.tar" : "vmcore"); 686 } else { 687 snprintf(linkname, sizeof(linkname), "%s.last", 688 istextdump ? "textdump.tar" : "vmcore"); 689 } 690 if (symlink(corename, linkname) == -1) { 691 syslog(LOG_WARNING, "unable to create symlink %s/%s: %m", 692 savedir, linkname); 693 } 694 695 nsaved++; 696 697 if (verbose) 698 printf("dump saved\n"); 699 700nuke: 701 if (!keep) { 702 if (verbose) 703 printf("clearing dump header\n"); 704 memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic); 705 lseek(fd, lasthd, SEEK_SET); 706 error = write(fd, &kdhl, sizeof kdhl); 707 if (error != sizeof kdhl) 708 syslog(LOG_ERR, 709 "error while clearing the dump header: %m"); 710 } 711 close(fd); 712 return; 713 714closeall: 715 fclose(fp); 716 717closefd: 718 close(fd); 719} 720 721static void 722usage(void) 723{ 724 fprintf(stderr, "%s\n%s\n%s\n", 725 "usage: savecore -c [-v] [device ...]", 726 " savecore -C [-v] [device ...]", 727 " savecore [-fkvz] [-m maxdumps] [directory [device ...]]"); 728 exit(1); 729} 730 731int 732main(int argc, char **argv) 733{ 734 const char *savedir = "."; 735 struct fstab *fsp; 736 int i, ch, error; 737 738 checkfor = compress = clear = force = keep = verbose = 0; 739 nfound = nsaved = nerr = 0; 740 741 openlog("savecore", LOG_PERROR, LOG_DAEMON); 742 signal(SIGINFO, infohandler); 743 744 while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1) 745 switch(ch) { 746 case 'C': 747 checkfor = 1; 748 break; 749 case 'c': 750 clear = 1; 751 break; 752 case 'f': 753 force = 1; 754 break; 755 case 'k': 756 keep = 1; 757 break; 758 case 'm': 759 maxdumps = atoi(optarg); 760 if (maxdumps <= 0) { 761 syslog(LOG_ERR, "Invalid maxdump value"); 762 exit(1); 763 } 764 break; 765 case 'v': 766 verbose++; 767 break; 768 case 'z': 769 compress = 1; 770 break; 771 case '?': 772 default: 773 usage(); 774 } 775 if (checkfor && (clear || force || keep)) 776 usage(); 777 if (clear && (compress || keep)) 778 usage(); 779 if (maxdumps > 0 && (checkfor || clear)) 780 usage(); 781 argc -= optind; 782 argv += optind; 783 if (argc >= 1 && !checkfor && !clear) { 784 error = chdir(argv[0]); 785 if (error) { 786 syslog(LOG_ERR, "chdir(%s): %m", argv[0]); 787 exit(1); 788 } 789 savedir = argv[0]; 790 argc--; 791 argv++; 792 } 793 if (argc == 0) { 794 for (;;) { 795 fsp = getfsent(); 796 if (fsp == NULL) 797 break; 798 if (strcmp(fsp->fs_vfstype, "swap") && 799 strcmp(fsp->fs_vfstype, "dump")) 800 continue; 801 DoFile(savedir, fsp->fs_spec); 802 } 803 } else { 804 for (i = 0; i < argc; i++) 805 DoFile(savedir, argv[i]); 806 } 807 808 /* Emit minimal output. */ 809 if (nfound == 0) { 810 if (checkfor) { 811 printf("No dump exists\n"); 812 exit(1); 813 } 814 syslog(LOG_WARNING, "no dumps found"); 815 } 816 else if (nsaved == 0) { 817 if (nerr != 0) 818 syslog(LOG_WARNING, "unsaved dumps found but not saved"); 819 else 820 syslog(LOG_WARNING, "no unsaved dumps found"); 821 } 822 823 return (0); 824} 825 826static void 827infohandler(int sig __unused) 828{ 829 got_siginfo = 1; 830} 831