1167050Smjacob/*- 2167050Smjacob * Copyright (c) 2006 Mathew Jacob <mjacob@FreeBSD.org> 3167050Smjacob * All rights reserved. 4167050Smjacob * 5167050Smjacob * Redistribution and use in source and binary forms, with or without 6167050Smjacob * modification, are permitted provided that the following conditions 7167050Smjacob * are met: 8167050Smjacob * 1. Redistributions of source code must retain the above copyright 9167050Smjacob * notice, this list of conditions and the following disclaimer. 10167050Smjacob * 2. Redistributions in binary form must reproduce the above copyright 11167050Smjacob * notice, this list of conditions and the following disclaimer in the 12167050Smjacob * documentation and/or other materials provided with the distribution. 13167050Smjacob * 14167050Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15167050Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16167050Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17167050Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18167050Smjacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19167050Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20167050Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21167050Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22167050Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23167050Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24167050Smjacob * SUCH DAMAGE. 25167050Smjacob */ 26167050Smjacob 27167050Smjacob#include <sys/cdefs.h> 28167050Smjacob__FBSDID("$FreeBSD$"); 29167050Smjacob#include <sys/param.h> 30167050Smjacob#include <errno.h> 31167050Smjacob#include <paths.h> 32167050Smjacob#include <stdio.h> 33167050Smjacob#include <stdlib.h> 34167050Smjacob#include <stdint.h> 35167050Smjacob#include <string.h> 36167050Smjacob#include <strings.h> 37167050Smjacob#include <assert.h> 38167050Smjacob#include <libgeom.h> 39227464Smav#include <unistd.h> 40167050Smjacob#include <uuid.h> 41167050Smjacob#include <geom/multipath/g_multipath.h> 42167050Smjacob 43167050Smjacob#include "core/geom.h" 44167050Smjacob#include "misc/subr.h" 45167050Smjacob 46167050Smjacobuint32_t lib_version = G_LIB_VERSION; 47167050Smjacobuint32_t version = G_MULTIPATH_VERSION; 48167050Smjacob 49167050Smjacobstatic void mp_main(struct gctl_req *, unsigned int); 50167050Smjacobstatic void mp_label(struct gctl_req *); 51167050Smjacobstatic void mp_clear(struct gctl_req *); 52239012Sthomasstatic void mp_prefer(struct gctl_req *); 53167050Smjacob 54167050Smjacobstruct g_command class_commands[] = { 55167050Smjacob { 56227464Smav "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 57227464Smav { 58227464Smav { 'A', "active_active", NULL, G_TYPE_BOOL }, 59234415Smav { 'R', "active_read", NULL, G_TYPE_BOOL }, 60227464Smav G_OPT_SENTINEL 61227464Smav }, 62234415Smav "[-vAR] name prov ..." 63167050Smjacob }, 64167050Smjacob { 65227464Smav "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, mp_main, 66227464Smav { 67227464Smav { 'A', "active_active", NULL, G_TYPE_BOOL }, 68234415Smav { 'R', "active_read", NULL, G_TYPE_BOOL }, 69227464Smav G_OPT_SENTINEL 70227464Smav }, 71234415Smav "[-vAR] name prov ..." 72205847Smjacob }, 73234415Smav { "configure", G_FLAG_VERBOSE, NULL, 74234415Smav { 75234415Smav { 'A', "active_active", NULL, G_TYPE_BOOL }, 76234415Smav { 'P', "active_passive", NULL, G_TYPE_BOOL }, 77234415Smav { 'R', "active_read", NULL, G_TYPE_BOOL }, 78234415Smav G_OPT_SENTINEL 79234415Smav }, 80234415Smav "[-vAPR] name" 81234415Smav }, 82205847Smjacob { 83227464Smav "add", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 84227464Smav "[-v] name prov" 85203505Smjacob }, 86203505Smjacob { 87227464Smav "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 88227464Smav "[-v] name prov" 89167050Smjacob }, 90205412Smjacob { 91239012Sthomas "prefer", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS, 92239012Sthomas "[-v] prov ..." 93239012Sthomas }, 94239012Sthomas { 95227464Smav "fail", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 96227464Smav "[-v] name prov" 97227464Smav }, 98227464Smav { 99227464Smav "restore", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 100227464Smav "[-v] name prov" 101227464Smav }, 102227464Smav { 103205412Smjacob "rotate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 104227464Smav "[-v] name" 105205412Smjacob }, 106205412Smjacob { 107205412Smjacob "getactive", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 108227464Smav "[-v] name" 109227464Smav }, 110227464Smav { 111227464Smav "destroy", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 112227464Smav "[-v] name" 113227464Smav }, 114227464Smav { 115227464Smav "stop", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 116227464Smav "[-v] name" 117227464Smav }, 118227464Smav { 119227464Smav "clear", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS, 120212554Spjd "[-v] prov ..." 121205412Smjacob }, 122167050Smjacob G_CMD_SENTINEL 123167050Smjacob}; 124167050Smjacob 125167050Smjacobstatic void 126167050Smjacobmp_main(struct gctl_req *req, unsigned int flags __unused) 127167050Smjacob{ 128167050Smjacob const char *name; 129167050Smjacob 130167050Smjacob name = gctl_get_ascii(req, "verb"); 131167050Smjacob if (name == NULL) { 132167050Smjacob gctl_error(req, "No '%s' argument.", "verb"); 133167050Smjacob return; 134167050Smjacob } 135167050Smjacob if (strcmp(name, "label") == 0) { 136167050Smjacob mp_label(req); 137167050Smjacob } else if (strcmp(name, "clear") == 0) { 138167050Smjacob mp_clear(req); 139239012Sthomas } else if (strcmp(name, "prefer") == 0) { 140239012Sthomas mp_prefer(req); 141167050Smjacob } else { 142167050Smjacob gctl_error(req, "Unknown command: %s.", name); 143167050Smjacob } 144167050Smjacob} 145167050Smjacob 146167050Smjacobstatic void 147167050Smjacobmp_label(struct gctl_req *req) 148167050Smjacob{ 149167050Smjacob struct g_multipath_metadata md; 150227464Smav off_t disksize = 0, msize; 151227464Smav uint8_t *sector, *rsector; 152167050Smjacob char *ptr; 153167050Smjacob uuid_t uuid; 154227473Smav ssize_t secsize = 0, ssize; 155227473Smav uint32_t status; 156227464Smav const char *name, *name2, *mpname; 157227464Smav int error, i, nargs, fd; 158167050Smjacob 159167050Smjacob nargs = gctl_get_int(req, "nargs"); 160167050Smjacob if (nargs < 2) { 161167050Smjacob gctl_error(req, "wrong number of arguments."); 162167050Smjacob return; 163167050Smjacob } 164167050Smjacob 165167050Smjacob /* 166167050Smjacob * First, check each provider to make sure it's the same size. 167167050Smjacob * This also gets us our size and sectorsize for the metadata. 168167050Smjacob */ 169167050Smjacob for (i = 1; i < nargs; i++) { 170167050Smjacob name = gctl_get_ascii(req, "arg%d", i); 171167050Smjacob msize = g_get_mediasize(name); 172167050Smjacob ssize = g_get_sectorsize(name); 173167050Smjacob if (msize == 0 || ssize == 0) { 174167050Smjacob gctl_error(req, "cannot get information about %s: %s.", 175167050Smjacob name, strerror(errno)); 176167050Smjacob return; 177167050Smjacob } 178167050Smjacob if (i == 1) { 179167050Smjacob secsize = ssize; 180227464Smav disksize = msize; 181167050Smjacob } else { 182167050Smjacob if (secsize != ssize) { 183227473Smav gctl_error(req, "%s sector size %ju different.", 184227473Smav name, (intmax_t)ssize); 185167050Smjacob return; 186167050Smjacob } 187227464Smav if (disksize != msize) { 188167050Smjacob gctl_error(req, "%s media size %ju different.", 189167050Smjacob name, (intmax_t)msize); 190167050Smjacob return; 191167050Smjacob } 192167050Smjacob } 193167050Smjacob 194167050Smjacob } 195167050Smjacob 196167050Smjacob /* 197167050Smjacob * Generate metadata. 198167050Smjacob */ 199167050Smjacob strlcpy(md.md_magic, G_MULTIPATH_MAGIC, sizeof(md.md_magic)); 200167050Smjacob md.md_version = G_MULTIPATH_VERSION; 201205847Smjacob mpname = gctl_get_ascii(req, "arg0"); 202205847Smjacob strlcpy(md.md_name, mpname, sizeof(md.md_name)); 203227464Smav md.md_size = disksize; 204167050Smjacob md.md_sectorsize = secsize; 205167050Smjacob uuid_create(&uuid, &status); 206167050Smjacob if (status != uuid_s_ok) { 207167050Smjacob gctl_error(req, "cannot create a UUID."); 208167050Smjacob return; 209167050Smjacob } 210167050Smjacob uuid_to_string(&uuid, &ptr, &status); 211167050Smjacob if (status != uuid_s_ok) { 212167050Smjacob gctl_error(req, "cannot stringify a UUID."); 213167050Smjacob return; 214167050Smjacob } 215167050Smjacob strlcpy(md.md_uuid, ptr, sizeof (md.md_uuid)); 216227464Smav md.md_active_active = gctl_get_int(req, "active_active"); 217234415Smav if (gctl_get_int(req, "active_read")) 218234415Smav md.md_active_active = 2; 219167050Smjacob free(ptr); 220167050Smjacob 221167050Smjacob /* 222211282Smjacob * Allocate a sector to write as metadata. 223211282Smjacob */ 224292395Sngie sector = calloc(1, secsize); 225211282Smjacob if (sector == NULL) { 226211282Smjacob gctl_error(req, "unable to allocate metadata buffer"); 227211282Smjacob return; 228211282Smjacob } 229227464Smav rsector = malloc(secsize); 230227464Smav if (rsector == NULL) { 231227464Smav gctl_error(req, "unable to allocate metadata buffer"); 232292395Sngie goto done; 233227464Smav } 234211282Smjacob 235211282Smjacob /* 236205847Smjacob * encode the metadata 237205847Smjacob */ 238167050Smjacob multipath_metadata_encode(&md, sector); 239167050Smjacob 240167050Smjacob /* 241205847Smjacob * Store metadata on the initial provider. 242167050Smjacob */ 243227464Smav name = gctl_get_ascii(req, "arg1"); 244205847Smjacob error = g_metadata_store(name, sector, secsize); 245205847Smjacob if (error != 0) { 246205847Smjacob gctl_error(req, "cannot store metadata on %s: %s.", name, strerror(error)); 247292395Sngie goto done; 248167050Smjacob } 249167050Smjacob 250167050Smjacob /* 251227464Smav * Now touch the rest of the providers to hint retaste. 252167050Smjacob */ 253205847Smjacob for (i = 2; i < nargs; i++) { 254227464Smav name2 = gctl_get_ascii(req, "arg%d", i); 255227464Smav fd = g_open(name2, 1); 256227464Smav if (fd < 0) { 257227464Smav fprintf(stderr, "Unable to open %s: %s.\n", 258227464Smav name2, strerror(errno)); 259167050Smjacob continue; 260167050Smjacob } 261227464Smav if (pread(fd, rsector, secsize, disksize - secsize) != 262227471Sdim (ssize_t)secsize) { 263227464Smav fprintf(stderr, "Unable to read metadata from %s: %s.\n", 264227464Smav name2, strerror(errno)); 265227464Smav g_close(fd); 266227464Smav continue; 267227464Smav } 268227464Smav g_close(fd); 269227464Smav if (memcmp(sector, rsector, secsize)) { 270227464Smav fprintf(stderr, "No metadata found on %s." 271227464Smav " It is not a path of %s.\n", 272227464Smav name2, name); 273227464Smav } 274167050Smjacob } 275292395Sngiedone: 276292395Sngie free(rsector); 277292395Sngie free(sector); 278167050Smjacob} 279167050Smjacob 280209704Smjacob 281167050Smjacobstatic void 282167050Smjacobmp_clear(struct gctl_req *req) 283167050Smjacob{ 284167050Smjacob const char *name; 285209704Smjacob int error, i, nargs; 286167050Smjacob 287209704Smjacob nargs = gctl_get_int(req, "nargs"); 288209704Smjacob if (nargs < 1) { 289209704Smjacob gctl_error(req, "Too few arguments."); 290209704Smjacob return; 291167050Smjacob } 292209704Smjacob 293209704Smjacob for (i = 0; i < nargs; i++) { 294209704Smjacob name = gctl_get_ascii(req, "arg%d", i); 295209704Smjacob error = g_metadata_clear(name, G_MULTIPATH_MAGIC); 296209704Smjacob if (error != 0) { 297209704Smjacob fprintf(stderr, "Can't clear metadata on %s: %s.\n", 298209704Smjacob name, strerror(error)); 299209704Smjacob gctl_error(req, "Not fully done."); 300209704Smjacob continue; 301209704Smjacob } 302209704Smjacob } 303205847Smjacob} 304167050Smjacob 305239012Sthomasstatic void 306239012Sthomasmp_prefer(struct gctl_req *req) 307239012Sthomas{ 308239012Sthomas const char *name, *comp, *errstr; 309239012Sthomas int nargs; 310239012Sthomas 311239012Sthomas nargs = gctl_get_int(req, "nargs"); 312239012Sthomas if (nargs != 2) { 313239012Sthomas gctl_error(req, "Usage: prefer GEOM PROVIDER"); 314239012Sthomas return; 315239012Sthomas } 316239012Sthomas name = gctl_get_ascii(req, "arg0"); 317239012Sthomas comp = gctl_get_ascii(req, "arg1"); 318239012Sthomas errstr = gctl_issue (req); 319239012Sthomas if (errstr != NULL) { 320239012Sthomas fprintf(stderr, "Can't set %s preferred provider to %s: %s.\n", 321239012Sthomas name, comp, errstr); 322239012Sthomas } 323239012Sthomas} 324