1 /*-
2  * Copyright (c) 2014-2018 MongoDB, Inc.
3  * Copyright (c) 2008-2014 WiredTiger, Inc.
4  *	All rights reserved.
5  *
6  * See the file LICENSE for redistribution information.
7  */
8 
9 #include "util.h"
10 
11 static int list_get_allocsize(WT_SESSION *, const char *, size_t *);
12 static int list_print(WT_SESSION *, const char *, bool, bool);
13 static int list_print_checkpoint(WT_SESSION *, const char *);
14 static int usage(void);
15 
16 int
util_list(WT_SESSION * session,int argc,char * argv[])17 util_list(WT_SESSION *session, int argc, char *argv[])
18 {
19 	WT_DECL_RET;
20 	int ch;
21 	char *uri;
22 	bool cflag, vflag;
23 
24 	cflag = vflag = false;
25 	uri = NULL;
26 	while ((ch = __wt_getopt(progname, argc, argv, "cv")) != EOF)
27 		switch (ch) {
28 		case 'c':
29 			cflag = true;
30 			break;
31 		case 'v':
32 			vflag = true;
33 			break;
34 		case '?':
35 		default:
36 			return (usage());
37 		}
38 	argc -= __wt_optind;
39 	argv += __wt_optind;
40 
41 	switch (argc) {
42 	case 0:
43 		break;
44 	case 1:
45 		if ((uri = util_uri(session, *argv, "table")) == NULL)
46 			return (1);
47 		break;
48 	default:
49 		return (usage());
50 	}
51 
52 	ret = list_print(session, uri, cflag, vflag);
53 
54 	free(uri);
55 	return (ret);
56 }
57 
58 /*
59  * list_get_allocsize --
60  *	Get the allocation size for this file from the metadata.
61  */
62 static int
list_get_allocsize(WT_SESSION * session,const char * key,size_t * allocsize)63 list_get_allocsize(WT_SESSION *session, const char *key, size_t *allocsize)
64 {
65 	WT_CONFIG_ITEM szvalue;
66 	WT_CONFIG_PARSER *parser;
67 	WT_DECL_RET;
68 	WT_EXTENSION_API *wt_api;
69 	int tret;
70 	char *config;
71 
72 	wt_api = session->connection->get_extension_api(session->connection);
73 	if ((ret = wt_api->metadata_search(wt_api, session, key, &config)) != 0)
74 		return (util_err(
75 		    session, ret, "%s: WT_EXTENSION_API.metadata_search", key));
76 	if ((ret = wt_api->config_parser_open(wt_api, session, config,
77 	    strlen(config), &parser)) != 0)
78 		return (util_err(
79 		    session, ret, "WT_EXTENSION_API.config_parser_open"));
80 	if ((ret = parser->get(parser, "allocation_size", &szvalue)) != 0) {
81 		if (ret == WT_NOTFOUND) {
82 			*allocsize = 0;
83 			ret = 0;
84 		} else
85 			ret = util_err(session, ret, "WT_CONFIG_PARSER.get");
86 		if ((tret = parser->close(parser)) != 0)
87 			(void)util_err(session, tret, "WT_CONFIG_PARSER.close");
88 		return (ret);
89 	}
90 	if ((ret = parser->close(parser)) != 0)
91 		return (util_err(session, ret, "WT_CONFIG_PARSER.close"));
92 	*allocsize = (size_t)szvalue.val;
93 	return (0);
94 }
95 
96 /*
97  * list_print --
98  *	List the high-level objects in the database.
99  */
100 static int
list_print(WT_SESSION * session,const char * uri,bool cflag,bool vflag)101 list_print(WT_SESSION *session, const char *uri, bool cflag, bool vflag)
102 {
103 	WT_CURSOR *cursor;
104 	WT_DECL_RET;
105 	const char *key, *value;
106 	bool found;
107 
108 	/* Open the metadata file. */
109 	if ((ret = session->open_cursor(
110 	    session, WT_METADATA_URI, NULL, NULL, &cursor)) != 0) {
111 		/*
112 		 * If there is no metadata (yet), this will return ENOENT.
113 		 * Treat that the same as an empty metadata.
114 		 */
115 		if (ret == ENOENT)
116 			return (0);
117 
118 		return (util_err(session,
119 		    ret, "%s: WT_SESSION.open_cursor", WT_METADATA_URI));
120 	}
121 
122 	found = uri == NULL;
123 	while ((ret = cursor->next(cursor)) == 0) {
124 		/* Get the key. */
125 		if ((ret = cursor->get_key(cursor, &key)) != 0)
126 			return (util_cerr(cursor, "get_key", ret));
127 
128 		/*
129 		 * If a name is specified, only show objects that match.
130 		 */
131 		if (uri != NULL) {
132 			if (!WT_PREFIX_MATCH(key, uri))
133 				continue;
134 			found = true;
135 		}
136 
137 		/*
138 		 * !!!
139 		 * We don't normally say anything about the WiredTiger metadata
140 		 * and lookaside tables, they're not application/user "objects"
141 		 * in the database.  I'm making an exception for the checkpoint
142 		 * and verbose options. However, skip over the metadata system
143 		 * information for anything except the verbose option.
144 		 */
145 		if (!vflag && WT_PREFIX_MATCH(key, WT_SYSTEM_PREFIX))
146 			continue;
147 		if (cflag || vflag ||
148 		    (strcmp(key, WT_METADATA_URI) != 0 &&
149 		    strcmp(key, WT_LAS_URI) != 0))
150 			printf("%s\n", key);
151 
152 		if (!cflag && !vflag)
153 			continue;
154 
155 		if (cflag && (ret = list_print_checkpoint(session, key)) != 0)
156 			return (ret);
157 		if (vflag) {
158 			if ((ret = cursor->get_value(cursor, &value)) != 0)
159 				return (util_cerr(cursor, "get_value", ret));
160 			printf("%s\n", value);
161 		}
162 	}
163 	if (ret != WT_NOTFOUND)
164 		return (util_cerr(cursor, "next", ret));
165 	if (!found) {
166 		fprintf(stderr, "%s: %s: not found\n", progname, uri);
167 		return (1);
168 	}
169 
170 	return (0);
171 }
172 
173 /*
174  * list_print_checkpoint --
175  *	List the checkpoint information.
176  */
177 static int
list_print_checkpoint(WT_SESSION * session,const char * key)178 list_print_checkpoint(WT_SESSION *session, const char *key)
179 {
180 	WT_BLOCK_CKPT ci;
181 	WT_CKPT *ckpt, *ckptbase;
182 	WT_DECL_RET;
183 	size_t allocsize, len;
184 	time_t t;
185 	uint64_t v;
186 
187 	/*
188 	 * We may not find any checkpoints for this file, in which case we don't
189 	 * report an error, and continue our caller's loop.  Otherwise, read the
190 	 * list of checkpoints and print each checkpoint's name and time.
191 	 */
192 	if ((ret = __wt_metadata_get_ckptlist(session, key, &ckptbase)) != 0)
193 		return (ret == WT_NOTFOUND ? 0 : ret);
194 
195 	/* We need the allocation size for decoding the checkpoint addr */
196 	if ((ret = list_get_allocsize(session, key, &allocsize)) != 0)
197 		return (ret);
198 
199 	/* Find the longest name, so we can pretty-print. */
200 	len = 0;
201 	WT_CKPT_FOREACH(ckptbase, ckpt)
202 		if (strlen(ckpt->name) > len)
203 			len = strlen(ckpt->name);
204 	++len;
205 
206 	memset(&ci, 0, sizeof(ci));
207 	WT_CKPT_FOREACH(ckptbase, ckpt) {
208 		if (allocsize != 0 && (ret = __wt_block_ckpt_decode(
209 		    session, allocsize, ckpt->raw.data, &ci)) != 0) {
210 			(void)util_err(session, ret, "__wt_block_ckpt_decode");
211 			/* continue if damaged */
212 			ci.root_size = 0;
213 		}
214 		/*
215 		 * Call ctime, not ctime_r; ctime_r has portability problems,
216 		 * the Solaris version is different from the POSIX standard.
217 		 */
218 		t = (time_t)ckpt->sec;
219 		printf("\t%*s: %.24s", (int)len, ckpt->name, ctime(&t));
220 
221 		v = ckpt->ckpt_size;
222 		if (v >= WT_PETABYTE)
223 			printf(" (%" PRIu64 " PB)\n", v / WT_PETABYTE);
224 		else if (v >= WT_TERABYTE)
225 			printf(" (%" PRIu64 " TB)\n", v / WT_TERABYTE);
226 		else if (v >= WT_GIGABYTE)
227 			printf(" (%" PRIu64 " GB)\n", v / WT_GIGABYTE);
228 		else if (v >= WT_MEGABYTE)
229 			printf(" (%" PRIu64 " MB)\n", v / WT_MEGABYTE);
230 		else if (v >= WT_KILOBYTE)
231 			printf(" (%" PRIu64 " KB)\n", v / WT_KILOBYTE);
232 		else
233 			printf(" (%" PRIu64 " B)\n", v);
234 		if (ci.root_size != 0) {
235 			printf("\t\t" "root offset: %" PRIuMAX
236 			    " (0x%" PRIxMAX ")\n",
237 			    (uintmax_t)ci.root_offset,
238 			    (uintmax_t)ci.root_offset);
239 			printf("\t\t" "root size: %" PRIu32
240 			    " (0x%" PRIx32 ")\n",
241 			    ci.root_size, ci.root_size);
242 			printf("\t\t" "root checksum: %" PRIu32
243 			    " (0x%" PRIx32 ")\n",
244 			    ci.root_checksum, ci.root_checksum);
245 		}
246 	}
247 
248 	__wt_metadata_free_ckptlist(session, ckptbase);
249 	return (0);
250 }
251 
252 static int
usage(void)253 usage(void)
254 {
255 	(void)fprintf(stderr,
256 	    "usage: %s %s "
257 	    "list [-cv] [uri]\n",
258 	    progname, usage_prefix);
259 	return (1);
260 }
261