xref: /freebsd/lib/geom/multipath/geom_multipath.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2006 Mathew Jacob <mjacob@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
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 <unistd.h>
41 #include <uuid.h>
42 #include <geom/multipath/g_multipath.h>
43 
44 #include "core/geom.h"
45 #include "misc/subr.h"
46 
47 uint32_t lib_version = G_LIB_VERSION;
48 uint32_t version = G_MULTIPATH_VERSION;
49 
50 static void mp_main(struct gctl_req *, unsigned int);
51 static void mp_label(struct gctl_req *);
52 static void mp_clear(struct gctl_req *);
53 static void mp_prefer(struct gctl_req *);
54 
55 struct g_command class_commands[] = {
56 	{
57 		"create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
58 		{
59 			{ 'A', "active_active", NULL, G_TYPE_BOOL },
60 			{ 'R', "active_read", NULL, G_TYPE_BOOL },
61 			G_OPT_SENTINEL
62 		},
63 		"[-vAR] name prov ..."
64 	},
65 	{
66 		"label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main,
67 		{
68 			{ 'A', "active_active", NULL, G_TYPE_BOOL },
69 			{ 'R', "active_read", NULL, G_TYPE_BOOL },
70 			G_OPT_SENTINEL
71 		},
72 		"[-vAR] name prov ..."
73 	},
74 	{ "configure", G_FLAG_VERBOSE, NULL,
75 		{
76 			{ 'A', "active_active", NULL, G_TYPE_BOOL },
77 			{ 'P', "active_passive", NULL, G_TYPE_BOOL },
78 			{ 'R', "active_read", NULL, G_TYPE_BOOL },
79 			G_OPT_SENTINEL
80 		},
81 		"[-vAPR] name"
82 	},
83 	{
84 		"add", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
85 		"[-v] name prov"
86 	},
87 	{
88 		"remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
89 		"[-v] name prov"
90 	},
91 	{
92 		"prefer", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
93 		"[-v] prov ..."
94 	},
95 	{
96 		"fail", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
97 		"[-v] name prov"
98 	},
99 	{
100 		"restore", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
101 		"[-v] name prov"
102 	},
103 	{
104 		"rotate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
105 		"[-v] name"
106 	},
107 	{
108 		"getactive", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
109 		"[-v] name"
110 	},
111 	{
112 		"destroy", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
113 		"[-v] name"
114 	},
115 	{
116 		"stop", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
117 		"[-v] name"
118 	},
119 	{
120 		"clear", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
121 		"[-v] prov ..."
122 	},
123 	G_CMD_SENTINEL
124 };
125 
126 static void
127 mp_main(struct gctl_req *req, unsigned int flags __unused)
128 {
129 	const char *name;
130 
131 	name = gctl_get_ascii(req, "verb");
132 	if (name == NULL) {
133 		gctl_error(req, "No '%s' argument.", "verb");
134 		return;
135 	}
136 	if (strcmp(name, "label") == 0) {
137 		mp_label(req);
138 	} else if (strcmp(name, "clear") == 0) {
139 		mp_clear(req);
140 	} else if (strcmp(name, "prefer") == 0) {
141 		mp_prefer(req);
142 	} else {
143 		gctl_error(req, "Unknown command: %s.", name);
144 	}
145 }
146 
147 static void
148 mp_label(struct gctl_req *req)
149 {
150 	struct g_multipath_metadata md;
151 	off_t disksize = 0, msize;
152 	uint8_t *sector, *rsector;
153 	char *ptr;
154 	uuid_t uuid;
155 	ssize_t secsize = 0, ssize;
156 	uint32_t status;
157 	const char *name, *name2, *mpname;
158 	int error, i, nargs, fd;
159 
160 	nargs = gctl_get_int(req, "nargs");
161 	if (nargs < 2) {
162 		gctl_error(req, "wrong number of arguments.");
163 		return;
164 	}
165 
166 	/*
167 	 * First, check each provider to make sure it's the same size.
168 	 * This also gets us our size and sectorsize for the metadata.
169 	 */
170 	for (i = 1; i < nargs; i++) {
171 		name = gctl_get_ascii(req, "arg%d", i);
172 		msize = g_get_mediasize(name);
173 		ssize = g_get_sectorsize(name);
174 		if (msize == 0 || ssize == 0) {
175 			gctl_error(req, "cannot get information about %s: %s.",
176 			    name, strerror(errno));
177 			return;
178 		}
179 		if (i == 1) {
180 			secsize = ssize;
181 			disksize = msize;
182 		} else {
183 			if (secsize != ssize) {
184 				gctl_error(req, "%s sector size %ju different.",
185 				    name, (intmax_t)ssize);
186 				return;
187 			}
188 			if (disksize != msize) {
189 				gctl_error(req, "%s media size %ju different.",
190 				    name, (intmax_t)msize);
191 				return;
192 			}
193 		}
194 
195 	}
196 
197 	/*
198 	 * Generate metadata.
199 	 */
200 	strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic));
201 	md.md_version = G_MULTIPATH_VERSION;
202 	mpname = gctl_get_ascii(req, "arg0");
203 	strlcpy(md.md_name, mpname, sizeof(md.md_name));
204 	md.md_size = disksize;
205 	md.md_sectorsize = secsize;
206 	uuid_create(&uuid, &status);
207 	if (status != uuid_s_ok) {
208 		gctl_error(req, "cannot create a UUID.");
209 		return;
210 	}
211 	uuid_to_string(&uuid, &ptr, &status);
212 	if (status != uuid_s_ok) {
213 		gctl_error(req, "cannot stringify a UUID.");
214 		return;
215 	}
216 	strlcpy(md.md_uuid, ptr, sizeof (md.md_uuid));
217 	md.md_active_active = gctl_get_int(req, "active_active");
218 	if (gctl_get_int(req, "active_read"))
219 		md.md_active_active = 2;
220 	free(ptr);
221 
222 	/*
223 	 * Allocate a sector to write as metadata.
224 	 */
225 	sector = calloc(1, secsize);
226 	if (sector == NULL) {
227 		gctl_error(req, "unable to allocate metadata buffer");
228 		return;
229 	}
230 	rsector = malloc(secsize);
231 	if (rsector == NULL) {
232 		gctl_error(req, "unable to allocate metadata buffer");
233 		goto done;
234 	}
235 
236 	/*
237 	 * encode the metadata
238 	 */
239 	multipath_metadata_encode(&md, sector);
240 
241 	/*
242 	 * Store metadata on the initial provider.
243 	 */
244 	name = gctl_get_ascii(req, "arg1");
245 	error = g_metadata_store(name, sector, secsize);
246 	if (error != 0) {
247 		gctl_error(req, "cannot store metadata on %s: %s.", name, strerror(error));
248 		goto done;
249 	}
250 
251 	/*
252 	 * Now touch the rest of the providers to hint retaste.
253 	 */
254 	for (i = 2; i < nargs; i++) {
255 		name2 = gctl_get_ascii(req, "arg%d", i);
256 		fd = g_open(name2, 1);
257 		if (fd < 0) {
258 			fprintf(stderr, "Unable to open %s: %s.\n",
259 			    name2, strerror(errno));
260 			continue;
261 		}
262 		if (pread(fd, rsector, secsize, disksize - secsize) !=
263 		    (ssize_t)secsize) {
264 			fprintf(stderr, "Unable to read metadata from %s: %s.\n",
265 			    name2, strerror(errno));
266 			g_close(fd);
267 			continue;
268 		}
269 		g_close(fd);
270 		if (memcmp(sector, rsector, secsize)) {
271 			fprintf(stderr, "No metadata found on %s."
272 			    " It is not a path of %s.\n",
273 			    name2, name);
274 		}
275 	}
276 done:
277 	free(rsector);
278 	free(sector);
279 }
280 
281 
282 static void
283 mp_clear(struct gctl_req *req)
284 {
285 	const char *name;
286 	int error, i, nargs;
287 
288 	nargs = gctl_get_int(req, "nargs");
289 	if (nargs < 1) {
290 		gctl_error(req, "Too few arguments.");
291 		return;
292 	}
293 
294 	for (i = 0; i < nargs; i++) {
295 		name = gctl_get_ascii(req, "arg%d", i);
296 		error = g_metadata_clear(name, G_MULTIPATH_MAGIC);
297 		if (error != 0) {
298 			fprintf(stderr, "Can't clear metadata on %s: %s.\n",
299 			    name, strerror(error));
300 			gctl_error(req, "Not fully done.");
301 			continue;
302 		}
303 	}
304 }
305 
306 static void
307 mp_prefer(struct gctl_req *req)
308 {
309 	const char *name, *comp, *errstr;
310 	int nargs;
311 
312 	nargs = gctl_get_int(req, "nargs");
313 	if (nargs != 2) {
314 		gctl_error(req, "Usage: prefer GEOM PROVIDER");
315 		return;
316 	}
317 	name = gctl_get_ascii(req, "arg0");
318 	comp = gctl_get_ascii(req, "arg1");
319 	errstr = gctl_issue (req);
320 	if (errstr != NULL) {
321 		fprintf(stderr, "Can't set %s preferred provider to %s: %s.\n",
322 		    name, comp, errstr);
323 	}
324 }
325