geom_multipath.c revision 292395
1/*-
2 * Copyright (c) 2006 Mathew Jacob <mjacob@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: stable/10/sbin/geom/class/multipath/geom_multipath.c 292395 2015-12-17 06:31:55Z ngie $");
29#include <sys/param.h>
30#include <errno.h>
31#include <paths.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <stdint.h>
35#include <string.h>
36#include <strings.h>
37#include <assert.h>
38#include <libgeom.h>
39#include <unistd.h>
40#include <uuid.h>
41#include <geom/multipath/g_multipath.h>
42
43#include "core/geom.h"
44#include "misc/subr.h"
45
46uint32_t lib_version = G_LIB_VERSION;
47uint32_t version = G_MULTIPATH_VERSION;
48
49static void mp_main(struct gctl_req *, unsigned int);
50static void mp_label(struct gctl_req *);
51static void mp_clear(struct gctl_req *);
52static void mp_prefer(struct gctl_req *);
53
54struct g_command class_commands[] = {
55	{
56		"create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
57		{
58			{ 'A', "active_active", NULL, G_TYPE_BOOL },
59			{ 'R', "active_read", NULL, G_TYPE_BOOL },
60			G_OPT_SENTINEL
61		},
62		"[-vAR] name prov ..."
63	},
64	{
65		"label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main,
66		{
67			{ 'A', "active_active", NULL, G_TYPE_BOOL },
68			{ 'R', "active_read", NULL, G_TYPE_BOOL },
69			G_OPT_SENTINEL
70		},
71		"[-vAR] name prov ..."
72	},
73	{ "configure", G_FLAG_VERBOSE, NULL,
74		{
75			{ 'A', "active_active", NULL, G_TYPE_BOOL },
76			{ 'P', "active_passive", NULL, G_TYPE_BOOL },
77			{ 'R', "active_read", NULL, G_TYPE_BOOL },
78			G_OPT_SENTINEL
79		},
80		"[-vAPR] name"
81	},
82	{
83		"add", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
84		"[-v] name prov"
85	},
86	{
87		"remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
88		"[-v] name prov"
89	},
90	{
91		"prefer", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
92		"[-v] prov ..."
93	},
94	{
95		"fail", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
96		"[-v] name prov"
97	},
98	{
99		"restore", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
100		"[-v] name prov"
101	},
102	{
103		"rotate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
104		"[-v] name"
105	},
106	{
107		"getactive", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
108		"[-v] name"
109	},
110	{
111		"destroy", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
112		"[-v] name"
113	},
114	{
115		"stop", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
116		"[-v] name"
117	},
118	{
119		"clear", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
120		"[-v] prov ..."
121	},
122	G_CMD_SENTINEL
123};
124
125static void
126mp_main(struct gctl_req *req, unsigned int flags __unused)
127{
128	const char *name;
129
130	name = gctl_get_ascii(req, "verb");
131	if (name == NULL) {
132		gctl_error(req, "No '%s' argument.", "verb");
133		return;
134	}
135	if (strcmp(name, "label") == 0) {
136		mp_label(req);
137	} else if (strcmp(name, "clear") == 0) {
138		mp_clear(req);
139	} else if (strcmp(name, "prefer") == 0) {
140		mp_prefer(req);
141	} else {
142		gctl_error(req, "Unknown command: %s.", name);
143	}
144}
145
146static void
147mp_label(struct gctl_req *req)
148{
149	struct g_multipath_metadata md;
150	off_t disksize = 0, msize;
151	uint8_t *sector, *rsector;
152	char *ptr;
153	uuid_t uuid;
154	ssize_t secsize = 0, ssize;
155	uint32_t status;
156	const char *name, *name2, *mpname;
157	int error, i, nargs, fd;
158
159	nargs = gctl_get_int(req, "nargs");
160	if (nargs < 2) {
161		gctl_error(req, "wrong number of arguments.");
162		return;
163	}
164
165	/*
166	 * First, check each provider to make sure it's the same size.
167	 * This also gets us our size and sectorsize for the metadata.
168	 */
169	for (i = 1; i < nargs; i++) {
170		name = gctl_get_ascii(req, "arg%d", i);
171		msize = g_get_mediasize(name);
172		ssize = g_get_sectorsize(name);
173		if (msize == 0 || ssize == 0) {
174			gctl_error(req, "cannot get information about %s: %s.",
175			    name, strerror(errno));
176			return;
177		}
178		if (i == 1) {
179			secsize = ssize;
180			disksize = msize;
181		} else {
182			if (secsize != ssize) {
183				gctl_error(req, "%s sector size %ju different.",
184				    name, (intmax_t)ssize);
185				return;
186			}
187			if (disksize != msize) {
188				gctl_error(req, "%s media size %ju different.",
189				    name, (intmax_t)msize);
190				return;
191			}
192		}
193
194	}
195
196	/*
197	 * Generate metadata.
198	 */
199	strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
200	md.md_version = G_MULTIPATH_VERSION;
201	mpname = gctl_get_ascii(req, "arg0");
202	strlcpy(md.md_name, mpname, sizeof(md.md_name));
203	md.md_size = disksize;
204	md.md_sectorsize = secsize;
205	uuid_create(&uuid, &status);
206	if (status != uuid_s_ok) {
207		gctl_error(req, "cannot create a UUID.");
208		return;
209	}
210	uuid_to_string(&uuid, &ptr, &status);
211	if (status != uuid_s_ok) {
212		gctl_error(req, "cannot stringify a UUID.");
213		return;
214	}
215	strlcpy(md.md_uuid, ptr, sizeof (md.md_uuid));
216	md.md_active_active = gctl_get_int(req, "active_active");
217	if (gctl_get_int(req, "active_read"))
218		md.md_active_active = 2;
219	free(ptr);
220
221	/*
222	 * Allocate a sector to write as metadata.
223	 */
224	sector = calloc(1, secsize);
225	if (sector == NULL) {
226		gctl_error(req, "unable to allocate metadata buffer");
227		return;
228	}
229	rsector = malloc(secsize);
230	if (rsector == NULL) {
231		gctl_error(req, "unable to allocate metadata buffer");
232		goto done;
233	}
234
235	/*
236	 * encode the metadata
237	 */
238	multipath_metadata_encode(&md, sector);
239
240	/*
241	 * Store metadata on the initial provider.
242	 */
243	name = gctl_get_ascii(req, "arg1");
244	error = g_metadata_store(name, sector, secsize);
245	if (error != 0) {
246		gctl_error(req, "cannot store metadata on %s: %s.", name, strerror(error));
247		goto done;
248	}
249
250	/*
251	 * Now touch the rest of the providers to hint retaste.
252	 */
253	for (i = 2; i < nargs; i++) {
254		name2 = gctl_get_ascii(req, "arg%d", i);
255		fd = g_open(name2, 1);
256		if (fd < 0) {
257			fprintf(stderr, "Unable to open %s: %s.\n",
258			    name2, strerror(errno));
259			continue;
260		}
261		if (pread(fd, rsector, secsize, disksize - secsize) !=
262		    (ssize_t)secsize) {
263			fprintf(stderr, "Unable to read metadata from %s: %s.\n",
264			    name2, strerror(errno));
265			g_close(fd);
266			continue;
267		}
268		g_close(fd);
269		if (memcmp(sector, rsector, secsize)) {
270			fprintf(stderr, "No metadata found on %s."
271			    " It is not a path of %s.\n",
272			    name2, name);
273		}
274	}
275done:
276	free(rsector);
277	free(sector);
278}
279
280
281static void
282mp_clear(struct gctl_req *req)
283{
284	const char *name;
285	int error, i, nargs;
286
287	nargs = gctl_get_int(req, "nargs");
288	if (nargs < 1) {
289		gctl_error(req, "Too few arguments.");
290		return;
291	}
292
293	for (i = 0; i < nargs; i++) {
294		name = gctl_get_ascii(req, "arg%d", i);
295		error = g_metadata_clear(name, G_MULTIPATH_MAGIC);
296		if (error != 0) {
297			fprintf(stderr, "Can't clear metadata on %s: %s.\n",
298			    name, strerror(error));
299			gctl_error(req, "Not fully done.");
300			continue;
301		}
302	}
303}
304
305static void
306mp_prefer(struct gctl_req *req)
307{
308	const char *name, *comp, *errstr;
309	int nargs;
310
311	nargs = gctl_get_int(req, "nargs");
312	if (nargs != 2) {
313		gctl_error(req, "Usage: prefer GEOM PROVIDER");
314		return;
315	}
316	name = gctl_get_ascii(req, "arg0");
317	comp = gctl_get_ascii(req, "arg1");
318	errstr = gctl_issue (req);
319	if (errstr != NULL) {
320		fprintf(stderr, "Can't set %s preferred provider to %s: %s.\n",
321		    name, comp, errstr);
322	}
323}
324