xref: /freebsd/sys/ddb/db_textdump.c (revision 990132f07db39b80c1ed103e5832adf200834e5f)
1618c7db3SRobert Watson /*-
2618c7db3SRobert Watson  * Copyright (c) 2007 Robert N. M. Watson
3618c7db3SRobert Watson  * All rights reserved.
4618c7db3SRobert Watson  *
5618c7db3SRobert Watson  * Redistribution and use in source and binary forms, with or without
6618c7db3SRobert Watson  * modification, are permitted provided that the following conditions
7618c7db3SRobert Watson  * are met:
8618c7db3SRobert Watson  * 1. Redistributions of source code must retain the above copyright
9618c7db3SRobert Watson  *    notice, this list of conditions and the following disclaimer.
10618c7db3SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
11618c7db3SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
12618c7db3SRobert Watson  *    documentation and/or other materials provided with the distribution.
13618c7db3SRobert Watson  *
14618c7db3SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15618c7db3SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16618c7db3SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17618c7db3SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18618c7db3SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19618c7db3SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20618c7db3SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21618c7db3SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22618c7db3SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23618c7db3SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24618c7db3SRobert Watson  * SUCH DAMAGE.
25618c7db3SRobert Watson  */
26618c7db3SRobert Watson 
27618c7db3SRobert Watson /*-
289b0fce60SRobert Watson  * Kernel text-dump support: write a series of text files to the dump
299b0fce60SRobert Watson  * partition for later recovery, including captured DDB output, kernel
309b0fce60SRobert Watson  * configuration, message buffer, and panic message.  This allows for a more
319b0fce60SRobert Watson  * compact representation of critical debugging information than traditional
329b0fce60SRobert Watson  * binary dumps, as well as allowing dump information to be used without
339b0fce60SRobert Watson  * access to kernel symbols, source code, etc.
34618c7db3SRobert Watson  *
35618c7db3SRobert Watson  * Storage Layout
36618c7db3SRobert Watson  * --------------
37618c7db3SRobert Watson  *
38618c7db3SRobert Watson  * Crash dumps are aligned to the end of the dump or swap partition in order
39618c7db3SRobert Watson  * to minimize the chances of swap duing fsck eating into the dump.  However,
40618c7db3SRobert Watson  * unlike a memory dump, we don't know the size of the textdump a priori, so
41618c7db3SRobert Watson  * can't just write it out sequentially in order from a known starting point
42618c7db3SRobert Watson  * calculated with respect to the end of the partition.  In order to address
43618c7db3SRobert Watson  * this, we actually write out the textdump in reverse block order, allowing
44618c7db3SRobert Watson  * us to directly align it to the end of the partition and then write out the
45618c7db3SRobert Watson  * dump header and trailer before and after it once done.  savecore(8) must
46618c7db3SRobert Watson  * know to reverse the order of the blocks in order to produce a readable
47618c7db3SRobert Watson  * file.
48618c7db3SRobert Watson  *
499b0fce60SRobert Watson  * Data is written out in the ustar file format so that we can write data
509b0fce60SRobert Watson  * incrementally as a stream without reference to previous files.
51618c7db3SRobert Watson  *
52618c7db3SRobert Watson  * TODO
53618c7db3SRobert Watson  * ----
54618c7db3SRobert Watson  *
55618c7db3SRobert Watson  * - Allow subsytems to register to submit files for inclusion in the text
56618c7db3SRobert Watson  *   dump in a generic way.
57618c7db3SRobert Watson  */
58618c7db3SRobert Watson 
59618c7db3SRobert Watson #include <sys/cdefs.h>
60618c7db3SRobert Watson __FBSDID("$FreeBSD$");
61618c7db3SRobert Watson 
62618c7db3SRobert Watson #include "opt_config.h"
63618c7db3SRobert Watson 
64618c7db3SRobert Watson #include <sys/param.h>
65618c7db3SRobert Watson #include <sys/conf.h>
66618c7db3SRobert Watson #include <sys/kernel.h>
67618c7db3SRobert Watson #include <sys/kerneldump.h>
68618c7db3SRobert Watson #include <sys/msgbuf.h>
69618c7db3SRobert Watson #include <sys/sysctl.h>
70618c7db3SRobert Watson #include <sys/systm.h>
71618c7db3SRobert Watson 
72618c7db3SRobert Watson #include <ddb/ddb.h>
73618c7db3SRobert Watson #include <ddb/db_lex.h>
74618c7db3SRobert Watson 
75618c7db3SRobert Watson static SYSCTL_NODE(_debug_ddb, OID_AUTO, textdump, CTLFLAG_RW, 0,
76618c7db3SRobert Watson     "DDB textdump options");
77618c7db3SRobert Watson 
78618c7db3SRobert Watson /*
79618c7db3SRobert Watson  * Don't touch the first SIZEOF_METADATA bytes on the dump device.  This is
80618c7db3SRobert Watson  * to protect us from metadata and metadata from us.
81618c7db3SRobert Watson  */
82618c7db3SRobert Watson #define	SIZEOF_METADATA		(64*1024)
83618c7db3SRobert Watson 
84618c7db3SRobert Watson /*
85618c7db3SRobert Watson  * Data is written out as a series of files in the ustar tar format.  ustar
86618c7db3SRobert Watson  * is a simple streamed format consiting of a series of files prefixed with
87618c7db3SRobert Watson  * headers, and all padded to 512-byte block boundaries, which maps
88618c7db3SRobert Watson  * conveniently to our requirements.
89618c7db3SRobert Watson  */
90618c7db3SRobert Watson struct ustar_header {
91618c7db3SRobert Watson 	char	uh_filename[100];
92618c7db3SRobert Watson 	char	uh_mode[8];
93618c7db3SRobert Watson 	char	uh_tar_owner[8];
94618c7db3SRobert Watson 	char	uh_tar_group[8];
95618c7db3SRobert Watson 	char	uh_size[12];
96618c7db3SRobert Watson 	char	uh_mtime[12];
97618c7db3SRobert Watson 	char	uh_sum[8];
98618c7db3SRobert Watson 	char	uh_type;
99618c7db3SRobert Watson 	char	uh_linkfile[100];
100618c7db3SRobert Watson 	char	uh_ustar[6];
101618c7db3SRobert Watson 	char	uh_version[2];
102618c7db3SRobert Watson 	char	uh_owner[32];
103618c7db3SRobert Watson 	char	uh_group[32];
104618c7db3SRobert Watson 	char	uh_major[8];
105618c7db3SRobert Watson 	char	uh_minor[8];
106618c7db3SRobert Watson 	char	uh_filenameprefix[155];
107618c7db3SRobert Watson 	char	uh_zeropad[12];
108618c7db3SRobert Watson } __packed;
109618c7db3SRobert Watson 
110618c7db3SRobert Watson /*
111618c7db3SRobert Watson  * Various size assertions -- pretty much everything must be one block in
112618c7db3SRobert Watson  * size.
113618c7db3SRobert Watson  */
114618c7db3SRobert Watson CTASSERT(sizeof(struct kerneldumpheader) == TEXTDUMP_BLOCKSIZE);
115618c7db3SRobert Watson CTASSERT(sizeof(struct ustar_header) == TEXTDUMP_BLOCKSIZE);
116618c7db3SRobert Watson 
117618c7db3SRobert Watson /*
118618c7db3SRobert Watson  * Is a textdump scheduled?  If so, the shutdown code will invoke our dumpsys
119618c7db3SRobert Watson  * routine instead of the machine-dependent kernel dump routine.
120618c7db3SRobert Watson  */
121618c7db3SRobert Watson int	textdump_pending;
122618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, pending, CTLFLAG_RW,
123618c7db3SRobert Watson     &textdump_pending, 0,
124618c7db3SRobert Watson     "Perform textdump instead of regular kernel dump.");
125618c7db3SRobert Watson 
126618c7db3SRobert Watson /*
127618c7db3SRobert Watson  * Various constants for tar headers and contents.
128618c7db3SRobert Watson  */
129618c7db3SRobert Watson #define	TAR_USER	"root"
130618c7db3SRobert Watson #define	TAR_GROUP	"wheel"
131618c7db3SRobert Watson #define	TAR_UID		"0"
132618c7db3SRobert Watson #define	TAR_GID		"0"
133618c7db3SRobert Watson #define	TAR_MODE	"0600"
134618c7db3SRobert Watson #define	TAR_USTAR	"ustar"
135618c7db3SRobert Watson 
136618c7db3SRobert Watson #define	TAR_CONFIG_FILENAME	"config.txt"	/* Kernel configuration. */
137618c7db3SRobert Watson #define	TAR_MSGBUF_FILENAME	"msgbuf.txt"	/* Kernel messsage buffer. */
138618c7db3SRobert Watson #define	TAR_PANIC_FILENAME	"panic.txt"	/* Panic message. */
139618c7db3SRobert Watson #define	TAR_VERSION_FILENAME	"version.txt"	/* Kernel version. */
140618c7db3SRobert Watson 
141618c7db3SRobert Watson /*
142618c7db3SRobert Watson  * Configure which files will be dumped.
143618c7db3SRobert Watson  */
144618c7db3SRobert Watson #ifdef INCLUDE_CONFIG_FILE
145618c7db3SRobert Watson static int textdump_do_config = 1;
146618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_config, CTLFLAG_RW,
147618c7db3SRobert Watson     &textdump_do_config, 0, "Dump kernel configuration in textdump");
148618c7db3SRobert Watson #endif
149618c7db3SRobert Watson 
150618c7db3SRobert Watson static int textdump_do_ddb = 1;
151618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_ddb, CTLFLAG_RW,
152618c7db3SRobert Watson     &textdump_do_ddb, 0, "Dump DDB captured output in textdump");
153618c7db3SRobert Watson 
154618c7db3SRobert Watson static int textdump_do_msgbuf = 1;
155618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_msgbuf, CTLFLAG_RW,
156618c7db3SRobert Watson     &textdump_do_msgbuf, 0, "Dump kernel message buffer in textdump");
157618c7db3SRobert Watson 
158618c7db3SRobert Watson static int textdump_do_panic = 1;
159618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_panic, CTLFLAG_RW,
160618c7db3SRobert Watson     &textdump_do_panic, 0, "Dump kernel panic message in textdump");
161618c7db3SRobert Watson 
162618c7db3SRobert Watson static int textdump_do_version = 1;
163618c7db3SRobert Watson SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_version, CTLFLAG_RW,
164618c7db3SRobert Watson     &textdump_do_version, 0, "Dump kernel version string in textdump");
165618c7db3SRobert Watson 
166618c7db3SRobert Watson /*
167618c7db3SRobert Watson  * State related to incremental writing of blocks to disk.
168618c7db3SRobert Watson  */
169618c7db3SRobert Watson static off_t textdump_offset;		/* Offset of next sequential write. */
170618c7db3SRobert Watson static int textdump_error;		/* Carried write error, if any. */
171618c7db3SRobert Watson 
172618c7db3SRobert Watson /*
173618c7db3SRobert Watson  * Statically allocate space to prepare block-sized headers and data.
174618c7db3SRobert Watson  */
175618c7db3SRobert Watson char textdump_block_buffer[TEXTDUMP_BLOCKSIZE];
176618c7db3SRobert Watson static struct kerneldumpheader kdh;
177618c7db3SRobert Watson 
178618c7db3SRobert Watson /*
179618c7db3SRobert Watson  * Text dumps are prefixed with a normal kernel dump header but with a
180618c7db3SRobert Watson  * different magic number to allow them to be uniquely identified.
181618c7db3SRobert Watson  */
182618c7db3SRobert Watson static void
183618c7db3SRobert Watson mkdumpheader(struct kerneldumpheader *kdh, uint32_t archver,
184618c7db3SRobert Watson     uint64_t dumplen, uint32_t blksz)
185618c7db3SRobert Watson {
186618c7db3SRobert Watson 
187618c7db3SRobert Watson 	bzero(kdh, sizeof(*kdh));
188618c7db3SRobert Watson 	strncpy(kdh->magic, TEXTDUMPMAGIC, sizeof(kdh->magic));
189618c7db3SRobert Watson 	strncpy(kdh->architecture, MACHINE_ARCH, sizeof(kdh->architecture));
190618c7db3SRobert Watson 	kdh->version = htod32(KERNELDUMPVERSION);
191618c7db3SRobert Watson 	kdh->architectureversion = htod32(archver);
192618c7db3SRobert Watson 	kdh->dumplength = htod64(dumplen);
193618c7db3SRobert Watson 	kdh->dumptime = htod64(time_second);
194618c7db3SRobert Watson 	kdh->blocksize = htod32(blksz);
195618c7db3SRobert Watson 	strncpy(kdh->hostname, hostname, sizeof(kdh->hostname));
196618c7db3SRobert Watson 	strncpy(kdh->versionstring, version, sizeof(kdh->versionstring));
197618c7db3SRobert Watson 	if (panicstr != NULL)
198618c7db3SRobert Watson 		strncpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring));
199618c7db3SRobert Watson 	kdh->parity = kerneldump_parity(kdh);
200618c7db3SRobert Watson }
201618c7db3SRobert Watson 
202618c7db3SRobert Watson /*
2039b0fce60SRobert Watson  * Calculate and fill in the checksum for a ustar header.
204618c7db3SRobert Watson  */
205618c7db3SRobert Watson static void
206618c7db3SRobert Watson ustar_checksum(struct ustar_header *uhp)
207618c7db3SRobert Watson {
208618c7db3SRobert Watson 	u_int sum;
209618c7db3SRobert Watson 	int i;
210618c7db3SRobert Watson 
211618c7db3SRobert Watson 	for (i = 0; i < sizeof(uhp->uh_sum); i++)
212618c7db3SRobert Watson 		uhp->uh_sum[i] = ' ';
213618c7db3SRobert Watson 	sum = 0;
214618c7db3SRobert Watson 	for (i = 0; i < sizeof(*uhp); i++)
215618c7db3SRobert Watson 		sum += ((u_char *)uhp)[i];
216618c7db3SRobert Watson 	snprintf(uhp->uh_sum, sizeof(uhp->uh_sum), "%6o", sum);
217618c7db3SRobert Watson }
218618c7db3SRobert Watson 
219618c7db3SRobert Watson /*
220618c7db3SRobert Watson  * Each file in the tarball has a block-sized header with its name and other,
221618c7db3SRobert Watson  * largely hard-coded, properties.
222618c7db3SRobert Watson  */
223618c7db3SRobert Watson void
224618c7db3SRobert Watson textdump_mkustar(char *block_buffer, const char *filename, u_int size)
225618c7db3SRobert Watson {
226618c7db3SRobert Watson 	struct ustar_header *uhp;
227618c7db3SRobert Watson 
228618c7db3SRobert Watson 	uhp = (struct ustar_header *)block_buffer;
229618c7db3SRobert Watson 	bzero(uhp, sizeof(*uhp));
230618c7db3SRobert Watson 	strlcpy(uhp->uh_filename, filename, sizeof(uhp->uh_filename));
231618c7db3SRobert Watson 	strlcpy(uhp->uh_mode, TAR_MODE, sizeof(uhp->uh_mode));
232618c7db3SRobert Watson 	snprintf(uhp->uh_size, sizeof(uhp->uh_size), "%o", size);
233618c7db3SRobert Watson 	strlcpy(uhp->uh_tar_owner, TAR_UID, sizeof(uhp->uh_tar_owner));
234618c7db3SRobert Watson 	strlcpy(uhp->uh_tar_group, TAR_GID, sizeof(uhp->uh_tar_group));
235618c7db3SRobert Watson 	strlcpy(uhp->uh_owner, TAR_USER, sizeof(uhp->uh_owner));
236618c7db3SRobert Watson 	strlcpy(uhp->uh_group, TAR_GROUP, sizeof(uhp->uh_group));
237618c7db3SRobert Watson 	snprintf(uhp->uh_mtime, sizeof(uhp->uh_mtime), "%lo",
238618c7db3SRobert Watson 	    (unsigned long)time_second);
239618c7db3SRobert Watson 	uhp->uh_type = 0;
240618c7db3SRobert Watson 	strlcpy(uhp->uh_ustar, TAR_USTAR, sizeof(uhp->uh_ustar));
241618c7db3SRobert Watson 	ustar_checksum(uhp);
242618c7db3SRobert Watson }
243618c7db3SRobert Watson 
244618c7db3SRobert Watson /*
245618c7db3SRobert Watson  * textdump_writeblock() writes TEXTDUMP_BLOCKSIZE-sized blocks of data to
246618c7db3SRobert Watson  * the space between di->mediaoffset and di->mediaoffset + di->mediasize.  It
247618c7db3SRobert Watson  * accepts an offset relative to di->mediaoffset.  If we're carrying any
248618c7db3SRobert Watson  * error from previous I/O, return that error and don't continue to try to
249618c7db3SRobert Watson  * write.  Most writers ignore the error and forge ahead on the basis that
250618c7db3SRobert Watson  * there's not much you can do.
251618c7db3SRobert Watson  */
252618c7db3SRobert Watson static int
253618c7db3SRobert Watson textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer)
254618c7db3SRobert Watson {
255618c7db3SRobert Watson 
256618c7db3SRobert Watson 	if (textdump_error)
257618c7db3SRobert Watson 		return (textdump_error);
258618c7db3SRobert Watson 	if (offset + TEXTDUMP_BLOCKSIZE > di->mediasize)
259618c7db3SRobert Watson 		return (EIO);
260618c7db3SRobert Watson 	if (offset < SIZEOF_METADATA)
261618c7db3SRobert Watson 		return (ENOSPC);
262990132f0SRobert Watson 	textdump_error = dump_write(di, buffer, 0, offset + di->mediaoffset,
263990132f0SRobert Watson 	    TEXTDUMP_BLOCKSIZE);
264618c7db3SRobert Watson 	return (textdump_error);
265618c7db3SRobert Watson }
266618c7db3SRobert Watson 
267618c7db3SRobert Watson /*
268618c7db3SRobert Watson  * Interfaces to save and restore the dump offset, so that printers can go
269618c7db3SRobert Watson  * back to rewrite a header if required, while avoiding their knowing about
270618c7db3SRobert Watson  * the global layout of the blocks.
2719b0fce60SRobert Watson  *
2729b0fce60SRobert Watson  * If we ever want to support writing textdumps to tape or other
2739b0fce60SRobert Watson  * stream-oriented target, we'll need to remove this.
274618c7db3SRobert Watson  */
275618c7db3SRobert Watson void
276618c7db3SRobert Watson textdump_saveoff(off_t *offsetp)
277618c7db3SRobert Watson {
278618c7db3SRobert Watson 
279618c7db3SRobert Watson 	*offsetp = textdump_offset;
280618c7db3SRobert Watson }
281618c7db3SRobert Watson 
282618c7db3SRobert Watson void
283618c7db3SRobert Watson textdump_restoreoff(off_t offset)
284618c7db3SRobert Watson {
285618c7db3SRobert Watson 
286618c7db3SRobert Watson 	textdump_offset = offset;
287618c7db3SRobert Watson }
288618c7db3SRobert Watson 
289618c7db3SRobert Watson /*
290618c7db3SRobert Watson  * Interface to write the "next block" relative to the current offset; since
291618c7db3SRobert Watson  * we write backwards from the end of the partition, we subtract, but there's
292618c7db3SRobert Watson  * no reason for the caller to know this.
293618c7db3SRobert Watson  */
294618c7db3SRobert Watson int
295618c7db3SRobert Watson textdump_writenextblock(struct dumperinfo *di, char *buffer)
296618c7db3SRobert Watson {
297618c7db3SRobert Watson 	int error;
298618c7db3SRobert Watson 
299618c7db3SRobert Watson 	error = textdump_writeblock(di, textdump_offset, buffer);
300618c7db3SRobert Watson 	textdump_offset -= TEXTDUMP_BLOCKSIZE;
301618c7db3SRobert Watson 	return (error);
302618c7db3SRobert Watson }
303618c7db3SRobert Watson 
304618c7db3SRobert Watson #ifdef INCLUDE_CONFIG_FILE
305618c7db3SRobert Watson extern char kernconfstring[];
306618c7db3SRobert Watson 
307618c7db3SRobert Watson /*
308618c7db3SRobert Watson  * Dump kernel configuration.
309618c7db3SRobert Watson  */
310618c7db3SRobert Watson static void
311618c7db3SRobert Watson textdump_dump_config(struct dumperinfo *di)
312618c7db3SRobert Watson {
313618c7db3SRobert Watson 	u_int count, fullblocks, len;
314618c7db3SRobert Watson 
315618c7db3SRobert Watson 	len = strlen(kernconfstring);
316618c7db3SRobert Watson 	textdump_mkustar(textdump_block_buffer, TAR_CONFIG_FILENAME, len);
317618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
318618c7db3SRobert Watson 
319618c7db3SRobert Watson 	/*
320618c7db3SRobert Watson 	 * Write out all full blocks directly from the string, and handle any
321618c7db3SRobert Watson 	 * left-over bits by copying it to out to the local buffer and
322618c7db3SRobert Watson 	 * zero-padding it.
323618c7db3SRobert Watson 	 */
324618c7db3SRobert Watson 	fullblocks = len / TEXTDUMP_BLOCKSIZE;
325618c7db3SRobert Watson 	for (count = 0; count < fullblocks; count++)
326618c7db3SRobert Watson 		(void)textdump_writenextblock(di, kernconfstring + count *
327618c7db3SRobert Watson 		    TEXTDUMP_BLOCKSIZE);
328618c7db3SRobert Watson 	if (len % TEXTDUMP_BLOCKSIZE != 0) {
329618c7db3SRobert Watson 		bzero(textdump_block_buffer, TEXTDUMP_BLOCKSIZE);
330618c7db3SRobert Watson 		bcopy(kernconfstring + count * TEXTDUMP_BLOCKSIZE,
331618c7db3SRobert Watson 		    textdump_block_buffer, len % TEXTDUMP_BLOCKSIZE);
332618c7db3SRobert Watson 		(void)textdump_writenextblock(di, textdump_block_buffer);
333618c7db3SRobert Watson 	}
334618c7db3SRobert Watson }
335618c7db3SRobert Watson #endif /* INCLUDE_CONFIG_FILE */
336618c7db3SRobert Watson 
337618c7db3SRobert Watson /*
338618c7db3SRobert Watson  * Dump kernel message buffer.
339618c7db3SRobert Watson  */
340618c7db3SRobert Watson static void
341618c7db3SRobert Watson textdump_dump_msgbuf(struct dumperinfo *di)
342618c7db3SRobert Watson {
343618c7db3SRobert Watson 	off_t end_offset, tarhdr_offset;
344618c7db3SRobert Watson 	u_int i, len, offset, seq, total_len;
345618c7db3SRobert Watson 	char buf[16];
346618c7db3SRobert Watson 
347618c7db3SRobert Watson 	/*
348618c7db3SRobert Watson 	 * Write out a dummy tar header to advance the offset; we'll rewrite
349618c7db3SRobert Watson 	 * it later once we know the true size.
350618c7db3SRobert Watson 	 */
351618c7db3SRobert Watson 	textdump_saveoff(&tarhdr_offset);
352618c7db3SRobert Watson 	textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME, 0);
353618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
354618c7db3SRobert Watson 
355618c7db3SRobert Watson 	/*
356618c7db3SRobert Watson 	 * Copy out the data in small chunks, but don't copy nuls that may be
357618c7db3SRobert Watson 	 * present if the message buffer has not yet completely filled at
358618c7db3SRobert Watson 	 * least once.
359618c7db3SRobert Watson 	 */
360618c7db3SRobert Watson 	total_len = 0;
361618c7db3SRobert Watson 	offset = 0;
362618c7db3SRobert Watson         msgbuf_peekbytes(msgbufp, NULL, 0, &seq);
363618c7db3SRobert Watson         while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) {
364618c7db3SRobert Watson 		for (i = 0; i < len; i++) {
365618c7db3SRobert Watson 			if (buf[i] == '\0')
366618c7db3SRobert Watson 				continue;
367618c7db3SRobert Watson 			textdump_block_buffer[offset] = buf[i];
368618c7db3SRobert Watson 			offset++;
369618c7db3SRobert Watson 			if (offset != sizeof(textdump_block_buffer))
370618c7db3SRobert Watson 				continue;
371618c7db3SRobert Watson 			(void)textdump_writenextblock(di,
372618c7db3SRobert Watson 			    textdump_block_buffer);
373618c7db3SRobert Watson 			total_len += offset;
374618c7db3SRobert Watson 			offset = 0;
375618c7db3SRobert Watson 		}
376618c7db3SRobert Watson         }
377618c7db3SRobert Watson 	total_len += offset;	/* Without the zero-padding. */
378618c7db3SRobert Watson 	if (offset != 0) {
379618c7db3SRobert Watson 		bzero(textdump_block_buffer + offset,
380618c7db3SRobert Watson 		    sizeof(textdump_block_buffer) - offset);
381618c7db3SRobert Watson 		(void)textdump_writenextblock(di, textdump_block_buffer);
382618c7db3SRobert Watson 	}
383618c7db3SRobert Watson 
384618c7db3SRobert Watson 	/*
385618c7db3SRobert Watson 	 * Rewrite tar header to reflect how much was actually written.
386618c7db3SRobert Watson 	 */
387618c7db3SRobert Watson 	textdump_saveoff(&end_offset);
388618c7db3SRobert Watson 	textdump_restoreoff(tarhdr_offset);
389618c7db3SRobert Watson 	textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME,
390618c7db3SRobert Watson 	    total_len);
391618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
392618c7db3SRobert Watson 	textdump_restoreoff(end_offset);
393618c7db3SRobert Watson }
394618c7db3SRobert Watson 
395618c7db3SRobert Watson static void
396618c7db3SRobert Watson textdump_dump_panic(struct dumperinfo *di)
397618c7db3SRobert Watson {
398618c7db3SRobert Watson 	u_int len;
399618c7db3SRobert Watson 
400618c7db3SRobert Watson 	/*
401618c7db3SRobert Watson 	 * Write out tar header -- we store up to one block of panic message.
402618c7db3SRobert Watson 	 */
403618c7db3SRobert Watson 	len = min(strlen(panicstr), TEXTDUMP_BLOCKSIZE);
404618c7db3SRobert Watson 	textdump_mkustar(textdump_block_buffer, TAR_PANIC_FILENAME, len);
405618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
406618c7db3SRobert Watson 
407618c7db3SRobert Watson 	/*
408618c7db3SRobert Watson 	 * Zero-pad the panic string and write out block.
409618c7db3SRobert Watson 	 */
410618c7db3SRobert Watson 	bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
411618c7db3SRobert Watson 	bcopy(panicstr, textdump_block_buffer, len);
412618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
413618c7db3SRobert Watson }
414618c7db3SRobert Watson 
415618c7db3SRobert Watson static void
416618c7db3SRobert Watson textdump_dump_version(struct dumperinfo *di)
417618c7db3SRobert Watson {
418618c7db3SRobert Watson 	u_int len;
419618c7db3SRobert Watson 
420618c7db3SRobert Watson 	/*
421618c7db3SRobert Watson 	 * Write out tar header -- at most one block of version information.
422618c7db3SRobert Watson 	 */
423618c7db3SRobert Watson 	len = min(strlen(version), TEXTDUMP_BLOCKSIZE);
424618c7db3SRobert Watson 	textdump_mkustar(textdump_block_buffer, TAR_VERSION_FILENAME, len);
425618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
426618c7db3SRobert Watson 
427618c7db3SRobert Watson 	/*
428618c7db3SRobert Watson 	 * Zero pad the version string and write out block.
429618c7db3SRobert Watson 	 */
430618c7db3SRobert Watson 	bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
431618c7db3SRobert Watson 	bcopy(version, textdump_block_buffer, len);
432618c7db3SRobert Watson 	(void)textdump_writenextblock(di, textdump_block_buffer);
433618c7db3SRobert Watson }
434618c7db3SRobert Watson 
435618c7db3SRobert Watson /*
436618c7db3SRobert Watson  * Commit text dump to disk.
437618c7db3SRobert Watson  */
438618c7db3SRobert Watson void
439618c7db3SRobert Watson textdump_dumpsys(struct dumperinfo *di)
440618c7db3SRobert Watson {
441618c7db3SRobert Watson 	off_t dumplen, trailer_offset;
442618c7db3SRobert Watson 
443618c7db3SRobert Watson 	if (di->blocksize != TEXTDUMP_BLOCKSIZE) {
444618c7db3SRobert Watson 		printf("Dump partition block size (%ju) not textdump "
445618c7db3SRobert Watson 		    "block size (%ju)", (uintmax_t)di->blocksize,
446618c7db3SRobert Watson 		    (uintmax_t)TEXTDUMP_BLOCKSIZE);
447618c7db3SRobert Watson 		return;
448618c7db3SRobert Watson 	}
449618c7db3SRobert Watson 
450618c7db3SRobert Watson 	/*
451618c7db3SRobert Watson 	 * We don't know a priori how large the dump will be, but we do know
452618c7db3SRobert Watson 	 * that we need to reserve space for metadata and that we need two
453618c7db3SRobert Watson 	 * dump headers.  Also leave room for one ustar header and one block
454618c7db3SRobert Watson 	 * of data.
455618c7db3SRobert Watson 	 */
456618c7db3SRobert Watson 	if (di->mediasize < SIZEOF_METADATA + 2 * sizeof(kdh)) {
457618c7db3SRobert Watson 		printf("Insufficient space on dump partition.\n");
458618c7db3SRobert Watson 		return;
459618c7db3SRobert Watson 	}
460618c7db3SRobert Watson 	textdump_error = 0;
461618c7db3SRobert Watson 
462618c7db3SRobert Watson 	/*
463618c7db3SRobert Watson 	 * Position the start of the dump so that we'll write the kernel dump
464618c7db3SRobert Watson 	 * trailer immediately before the end of the partition, and then work
465618c7db3SRobert Watson 	 * our way back.  We will rewrite this header later to reflect the
466618c7db3SRobert Watson 	 * true size if things go well.
467618c7db3SRobert Watson 	 */
468618c7db3SRobert Watson 	textdump_offset = di->mediasize - sizeof(kdh);
469618c7db3SRobert Watson 	textdump_saveoff(&trailer_offset);
470618c7db3SRobert Watson 	mkdumpheader(&kdh, KERNELDUMP_TEXT_VERSION, 0, TEXTDUMP_BLOCKSIZE);
471618c7db3SRobert Watson 	(void)textdump_writenextblock(di, (char *)&kdh);
472618c7db3SRobert Watson 
473618c7db3SRobert Watson 	/*
474618c7db3SRobert Watson 	 * Write a series of files in ustar format.
475618c7db3SRobert Watson 	 */
476618c7db3SRobert Watson 	if (textdump_do_ddb)
477618c7db3SRobert Watson 		db_capture_dump(di);
478618c7db3SRobert Watson #ifdef INCLUDE_CONFIG_FILE
479618c7db3SRobert Watson 	if (textdump_do_config)
480618c7db3SRobert Watson 		textdump_dump_config(di);
481618c7db3SRobert Watson #endif
482618c7db3SRobert Watson 	if (textdump_do_msgbuf)
483618c7db3SRobert Watson 		textdump_dump_msgbuf(di);
484618c7db3SRobert Watson 	if (textdump_do_panic && panicstr != NULL)
485618c7db3SRobert Watson 		textdump_dump_panic(di);
486618c7db3SRobert Watson 	if (textdump_do_version)
487618c7db3SRobert Watson 		textdump_dump_version(di);
488618c7db3SRobert Watson 
489618c7db3SRobert Watson 	/*
490618c7db3SRobert Watson 	 * Now that we know the true size, we can write out the header, then
491618c7db3SRobert Watson 	 * seek back to the end and rewrite the trailer with the correct
492618c7db3SRobert Watson 	 * size.
493618c7db3SRobert Watson 	 */
494618c7db3SRobert Watson 	dumplen = trailer_offset - (textdump_offset + TEXTDUMP_BLOCKSIZE);
495618c7db3SRobert Watson 	mkdumpheader(&kdh, KERNELDUMP_TEXT_VERSION, dumplen,
496618c7db3SRobert Watson 	    TEXTDUMP_BLOCKSIZE);
497618c7db3SRobert Watson 	(void)textdump_writenextblock(di, (char *)&kdh);
498618c7db3SRobert Watson 	textdump_restoreoff(trailer_offset);
499618c7db3SRobert Watson 	(void)textdump_writenextblock(di, (char *)&kdh);
500618c7db3SRobert Watson 
501618c7db3SRobert Watson 	/*
502618c7db3SRobert Watson 	 * Terminate the dump, report any errors, and clear the pending flag.
503618c7db3SRobert Watson 	 */
504618c7db3SRobert Watson 	if (textdump_error == 0)
505990132f0SRobert Watson 		(void)dump_write(di, NULL, 0, 0, 0);
506618c7db3SRobert Watson 	if (textdump_error == ENOSPC)
507618c7db3SRobert Watson 		printf("Insufficient space on dump partition\n");
508618c7db3SRobert Watson 	else if (textdump_error != 0)
509618c7db3SRobert Watson 		printf("Error %d writing dump\n", textdump_error);
510618c7db3SRobert Watson 	else
511618c7db3SRobert Watson 		printf("Textdump complete.\n");
512618c7db3SRobert Watson 	textdump_pending = 0;
513618c7db3SRobert Watson }
514618c7db3SRobert Watson 
515618c7db3SRobert Watson /*-
516618c7db3SRobert Watson  * DDB(4) command to manage textdumps:
517618c7db3SRobert Watson  *
518618c7db3SRobert Watson  * textdump set        - request a textdump
519618c7db3SRobert Watson  * textdump status     - print DDB output textdump status
520618c7db3SRobert Watson  * textdump unset      - clear textdump request
521618c7db3SRobert Watson  */
522618c7db3SRobert Watson static void
523618c7db3SRobert Watson db_textdump_usage(void)
524618c7db3SRobert Watson {
525618c7db3SRobert Watson 
526618c7db3SRobert Watson 	db_printf("textdump [unset|set|status]\n");
527618c7db3SRobert Watson }
528618c7db3SRobert Watson 
529618c7db3SRobert Watson void
530618c7db3SRobert Watson db_textdump_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
531618c7db3SRobert Watson     char *modif)
532618c7db3SRobert Watson {
533618c7db3SRobert Watson 	int t;
534618c7db3SRobert Watson 
535618c7db3SRobert Watson 	t = db_read_token();
536618c7db3SRobert Watson 	if (t != tIDENT) {
537618c7db3SRobert Watson 		db_textdump_usage();
538618c7db3SRobert Watson 		return;
539618c7db3SRobert Watson 	}
540618c7db3SRobert Watson 	if (db_read_token() != tEOL) {
541618c7db3SRobert Watson 		db_textdump_usage();
542618c7db3SRobert Watson 		return;
543618c7db3SRobert Watson 	}
544618c7db3SRobert Watson 	if (strcmp(db_tok_string, "set") == 0) {
545618c7db3SRobert Watson 		textdump_pending = 1;
546618c7db3SRobert Watson 		db_printf("textdump set\n");
547618c7db3SRobert Watson 	} else if (strcmp(db_tok_string, "status") == 0) {
548618c7db3SRobert Watson 		if (textdump_pending)
549618c7db3SRobert Watson 			db_printf("textdump is set\n");
550618c7db3SRobert Watson 		else
551618c7db3SRobert Watson 			db_printf("textdump is not set\n");
552618c7db3SRobert Watson 	} else if (strcmp(db_tok_string, "unset") == 0) {
553618c7db3SRobert Watson 		textdump_pending = 0;
554618c7db3SRobert Watson 		db_printf("textdump unset\n");
555618c7db3SRobert Watson 	} else
556618c7db3SRobert Watson 		db_textdump_usage();
557618c7db3SRobert Watson }
558