1/*-
2 * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * 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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <errno.h>
32#include <paths.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdint.h>
36#include <string.h>
37#include <strings.h>
38#include <assert.h>
39#include <libgeom.h>
40#include <geom/mirror/g_mirror.h>
41#include <core/geom.h>
42#include <misc/subr.h>
43
44uint32_t lib_version = G_LIB_VERSION;
45uint32_t version = G_MIRROR_VERSION;
46
47#define	GMIRROR_BALANCE		"load"
48#define	GMIRROR_SLICE		"4096"
49#define	GMIRROR_PRIORITY	"0"
50
51static void mirror_main(struct gctl_req *req, unsigned flags);
52static void mirror_activate(struct gctl_req *req);
53static void mirror_clear(struct gctl_req *req);
54static void mirror_dump(struct gctl_req *req);
55static void mirror_label(struct gctl_req *req);
56
57struct g_command class_commands[] = {
58	{ "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
59	    "[-v] name prov ..."
60	},
61	{ "clear", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS,
62	    "[-v] prov ..."
63	},
64	{ "configure", G_FLAG_VERBOSE, NULL,
65	    {
66		{ 'a', "autosync", NULL, G_TYPE_BOOL },
67		{ 'b', "balance", "", G_TYPE_STRING },
68		{ 'd', "dynamic", NULL, G_TYPE_BOOL },
69		{ 'f', "failsync", NULL, G_TYPE_BOOL },
70		{ 'F', "nofailsync", NULL, G_TYPE_BOOL },
71		{ 'h', "hardcode", NULL, G_TYPE_BOOL },
72		{ 'n', "noautosync", NULL, G_TYPE_BOOL },
73		{ 'p', "priority", "-1", G_TYPE_NUMBER },
74		{ 's', "slice", "-1", G_TYPE_NUMBER },
75		G_OPT_SENTINEL
76	    },
77	    "[-adfFhnv] [-b balance] [-s slice] name\n"
78	    "[-v] -p priority name prov"
79	},
80	{ "deactivate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
81	    "[-v] name prov ..."
82	},
83	{ "destroy", G_FLAG_VERBOSE, NULL,
84	    {
85		{ 'f', "force", NULL, G_TYPE_BOOL },
86		G_OPT_SENTINEL
87	    },
88	    "[-fv] name ..."
89	},
90	{ "dump", 0, mirror_main, G_NULL_OPTS,
91	    "prov ..."
92	},
93	{ "forget", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
94	    "name ..."
95	},
96	{ "label", G_FLAG_VERBOSE, mirror_main,
97	    {
98		{ 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING },
99		{ 'F', "nofailsync", NULL, G_TYPE_BOOL },
100		{ 'h', "hardcode", NULL, G_TYPE_BOOL },
101		{ 'n', "noautosync", NULL, G_TYPE_BOOL },
102		{ 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER },
103		G_OPT_SENTINEL
104	    },
105	    "[-Fhnv] [-b balance] [-s slice] name prov ..."
106	},
107	{ "insert", G_FLAG_VERBOSE, NULL,
108	    {
109		{ 'h', "hardcode", NULL, G_TYPE_BOOL },
110		{ 'i', "inactive", NULL, G_TYPE_BOOL },
111		{ 'p', "priority", GMIRROR_PRIORITY, G_TYPE_NUMBER },
112		G_OPT_SENTINEL
113	    },
114	    "[-hiv] [-p priority] name prov ..."
115	},
116	{ "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
117	    "[-v] name prov ..."
118	},
119	{ "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
120	    "[-v] name prov ..."
121	},
122	{ "stop", G_FLAG_VERBOSE, NULL,
123	    {
124		{ 'f', "force", NULL, G_TYPE_BOOL },
125		G_OPT_SENTINEL
126	    },
127	    "[-fv] name ..."
128	},
129	G_CMD_SENTINEL
130};
131
132static int verbose = 0;
133
134static void
135mirror_main(struct gctl_req *req, unsigned flags)
136{
137	const char *name;
138
139	if ((flags & G_FLAG_VERBOSE) != 0)
140		verbose = 1;
141
142	name = gctl_get_ascii(req, "verb");
143	if (name == NULL) {
144		gctl_error(req, "No '%s' argument.", "verb");
145		return;
146	}
147	if (strcmp(name, "label") == 0)
148		mirror_label(req);
149	else if (strcmp(name, "clear") == 0)
150		mirror_clear(req);
151	else if (strcmp(name, "dump") == 0)
152		mirror_dump(req);
153	else if (strcmp(name, "activate") == 0)
154		mirror_activate(req);
155	else
156		gctl_error(req, "Unknown command: %s.", name);
157}
158
159static void
160mirror_label(struct gctl_req *req)
161{
162	struct g_mirror_metadata md;
163	u_char sector[512];
164	const char *str;
165	unsigned sectorsize;
166	off_t mediasize;
167	intmax_t val;
168	int error, i, nargs, bal, hardcode;
169
170	nargs = gctl_get_int(req, "nargs");
171	if (nargs < 2) {
172		gctl_error(req, "Too few arguments.");
173		return;
174	}
175
176	strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic));
177	md.md_version = G_MIRROR_VERSION;
178	str = gctl_get_ascii(req, "arg0");
179	strlcpy(md.md_name, str, sizeof(md.md_name));
180	md.md_mid = arc4random();
181	md.md_all = nargs - 1;
182	md.md_mflags = 0;
183	md.md_dflags = 0;
184	md.md_genid = 0;
185	md.md_syncid = 1;
186	md.md_sync_offset = 0;
187	val = gctl_get_intmax(req, "slice");
188	md.md_slice = val;
189	str = gctl_get_ascii(req, "balance");
190	bal = balance_id(str);
191	if (bal == -1) {
192		gctl_error(req, "Invalid balance algorithm.");
193		return;
194	}
195	md.md_balance = bal;
196	if (gctl_get_int(req, "noautosync"))
197		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC;
198	if (gctl_get_int(req, "nofailsync"))
199		md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC;
200	hardcode = gctl_get_int(req, "hardcode");
201
202	/*
203	 * Calculate sectorsize by finding least common multiple from
204	 * sectorsizes of every disk and find the smallest mediasize.
205	 */
206	mediasize = 0;
207	sectorsize = 0;
208	for (i = 1; i < nargs; i++) {
209		unsigned ssize;
210		off_t msize;
211
212		str = gctl_get_ascii(req, "arg%d", i);
213		msize = g_get_mediasize(str);
214		ssize = g_get_sectorsize(str);
215		if (msize == 0 || ssize == 0) {
216			gctl_error(req, "Can't get informations about %s: %s.",
217			    str, strerror(errno));
218			return;
219		}
220		msize -= ssize;
221		if (mediasize == 0 || (mediasize > 0 && msize < mediasize))
222			mediasize = msize;
223		if (sectorsize == 0)
224			sectorsize = ssize;
225		else
226			sectorsize = g_lcm(sectorsize, ssize);
227	}
228	md.md_mediasize = mediasize;
229	md.md_sectorsize = sectorsize;
230	md.md_mediasize -= (md.md_mediasize % md.md_sectorsize);
231
232	/*
233	 * Clear last sector first, to spoil all components if device exists.
234	 */
235	for (i = 1; i < nargs; i++) {
236		str = gctl_get_ascii(req, "arg%d", i);
237		error = g_metadata_clear(str, NULL);
238		if (error != 0) {
239			gctl_error(req, "Can't store metadata on %s: %s.", str,
240			    strerror(error));
241			return;
242		}
243	}
244
245	/*
246	 * Ok, store metadata (use disk number as priority).
247	 */
248	for (i = 1; i < nargs; i++) {
249		str = gctl_get_ascii(req, "arg%d", i);
250		md.md_did = arc4random();
251		md.md_priority = i - 1;
252		md.md_provsize = g_get_mediasize(str);
253		assert(md.md_provsize != 0);
254		if (!hardcode)
255			bzero(md.md_provider, sizeof(md.md_provider));
256		else {
257			if (strncmp(str, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
258				str += sizeof(_PATH_DEV) - 1;
259			strlcpy(md.md_provider, str, sizeof(md.md_provider));
260		}
261		mirror_metadata_encode(&md, sector);
262		error = g_metadata_store(str, sector, sizeof(sector));
263		if (error != 0) {
264			fprintf(stderr, "Can't store metadata on %s: %s.\n",
265			    str, strerror(error));
266			gctl_error(req, "Not fully done.");
267			continue;
268		}
269		if (verbose)
270			printf("Metadata value stored on %s.\n", str);
271	}
272}
273
274static void
275mirror_clear(struct gctl_req *req)
276{
277	const char *name;
278	int error, i, nargs;
279
280	nargs = gctl_get_int(req, "nargs");
281	if (nargs < 1) {
282		gctl_error(req, "Too few arguments.");
283		return;
284	}
285
286	for (i = 0; i < nargs; i++) {
287		name = gctl_get_ascii(req, "arg%d", i);
288		error = g_metadata_clear(name, G_MIRROR_MAGIC);
289		if (error != 0) {
290			fprintf(stderr, "Can't clear metadata on %s: %s.\n",
291			    name, strerror(error));
292			gctl_error(req, "Not fully done.");
293			continue;
294		}
295		if (verbose)
296			printf("Metadata cleared on %s.\n", name);
297	}
298}
299
300static void
301mirror_dump(struct gctl_req *req)
302{
303	struct g_mirror_metadata md, tmpmd;
304	const char *name;
305	int error, i, nargs;
306
307	nargs = gctl_get_int(req, "nargs");
308	if (nargs < 1) {
309		gctl_error(req, "Too few arguments.");
310		return;
311	}
312
313	for (i = 0; i < nargs; i++) {
314		name = gctl_get_ascii(req, "arg%d", i);
315		error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd),
316		    G_MIRROR_MAGIC);
317		if (error != 0) {
318			fprintf(stderr, "Can't read metadata from %s: %s.\n",
319			    name, strerror(error));
320			gctl_error(req, "Not fully done.");
321			continue;
322		}
323		if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
324			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
325			    name);
326			gctl_error(req, "Not fully done.");
327			continue;
328		}
329		printf("Metadata on %s:\n", name);
330		mirror_metadata_dump(&md);
331		printf("\n");
332	}
333}
334
335static void
336mirror_activate(struct gctl_req *req)
337{
338	struct g_mirror_metadata md, tmpmd;
339	const char *name, *path;
340	int error, i, nargs;
341
342	nargs = gctl_get_int(req, "nargs");
343	if (nargs < 2) {
344		gctl_error(req, "Too few arguments.");
345		return;
346	}
347	name = gctl_get_ascii(req, "arg0");
348
349	for (i = 1; i < nargs; i++) {
350		path = gctl_get_ascii(req, "arg%d", i);
351		error = g_metadata_read(path, (u_char *)&tmpmd, sizeof(tmpmd),
352		    G_MIRROR_MAGIC);
353		if (error != 0) {
354			fprintf(stderr, "Cannot read metadata from %s: %s.\n",
355			    path, strerror(error));
356			gctl_error(req, "Not fully done.");
357			continue;
358		}
359		if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) {
360			fprintf(stderr,
361			    "MD5 hash mismatch for provider %s, skipping.\n",
362			    path);
363			gctl_error(req, "Not fully done.");
364			continue;
365		}
366		if (strcmp(md.md_name, name) != 0) {
367			fprintf(stderr,
368			    "Provider %s is not the mirror %s component.\n",
369			    path, name);
370			gctl_error(req, "Not fully done.");
371			continue;
372		}
373		md.md_dflags &= ~G_MIRROR_DISK_FLAG_INACTIVE;
374		mirror_metadata_encode(&md, (u_char *)&tmpmd);
375		error = g_metadata_store(path, (u_char *)&tmpmd, sizeof(tmpmd));
376		if (error != 0) {
377			fprintf(stderr, "Cannot write metadata from %s: %s.\n",
378			    path, strerror(error));
379			gctl_error(req, "Not fully done.");
380			continue;
381		}
382		if (verbose)
383			printf("Provider %s activated.\n", path);
384	}
385}
386