1 /*- 2 * Copyright (c) 2009-2010 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Pawel Jakub Dawidek under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/disk.h> 35 #include <sys/ioctl.h> 36 #include <sys/stat.h> 37 #include <sys/sysctl.h> 38 39 #include <assert.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <libutil.h> 44 #include <limits.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <sysexits.h> 50 #include <unistd.h> 51 52 #include <activemap.h> 53 54 #include "hast.h" 55 #include "hast_proto.h" 56 #include "metadata.h" 57 #include "nv.h" 58 #include "pjdlog.h" 59 #include "proto.h" 60 #include "subr.h" 61 62 /* Path to configuration file. */ 63 static const char *cfgpath = HAST_CONFIG; 64 /* Hastd configuration. */ 65 static struct hastd_config *cfg; 66 /* Control connection. */ 67 static struct proto_conn *controlconn; 68 69 enum { 70 CMD_INVALID, 71 CMD_CREATE, 72 CMD_ROLE, 73 CMD_STATUS, 74 CMD_DUMP 75 }; 76 77 static __dead2 void 78 usage(void) 79 { 80 81 fprintf(stderr, 82 "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n" 83 "\t\t[-m mediasize] name ...\n", 84 getprogname()); 85 fprintf(stderr, 86 " %s role [-d] [-c config] <init | primary | secondary> all | name ...\n", 87 getprogname()); 88 fprintf(stderr, 89 " %s status [-d] [-c config] [all | name ...]\n", 90 getprogname()); 91 fprintf(stderr, 92 " %s dump [-d] [-c config] [all | name ...]\n", 93 getprogname()); 94 exit(EX_USAGE); 95 } 96 97 static int 98 create_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, 99 intmax_t keepdirty) 100 { 101 unsigned char *buf; 102 size_t mapsize; 103 int ec; 104 105 ec = 0; 106 pjdlog_prefix_set("[%s] ", res->hr_name); 107 108 if (provinfo(res, true) < 0) { 109 ec = EX_NOINPUT; 110 goto end; 111 } 112 if (mediasize == 0) 113 mediasize = res->hr_local_mediasize; 114 else if (mediasize > res->hr_local_mediasize) { 115 pjdlog_error("Provided mediasize is larger than provider %s size.", 116 res->hr_localpath); 117 ec = EX_DATAERR; 118 goto end; 119 } 120 if (!powerof2(res->hr_local_sectorsize)) { 121 pjdlog_error("Sector size of provider %s is not power of 2 (%u).", 122 res->hr_localpath, res->hr_local_sectorsize); 123 ec = EX_DATAERR; 124 goto end; 125 } 126 if (extentsize == 0) 127 extentsize = HAST_EXTENTSIZE; 128 if (extentsize < res->hr_local_sectorsize) { 129 pjdlog_error("Extent size (%jd) is less than sector size (%u).", 130 (intmax_t)extentsize, res->hr_local_sectorsize); 131 ec = EX_DATAERR; 132 goto end; 133 } 134 if ((extentsize % res->hr_local_sectorsize) != 0) { 135 pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", 136 (intmax_t)extentsize, res->hr_local_sectorsize); 137 ec = EX_DATAERR; 138 goto end; 139 } 140 mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, 141 extentsize, res->hr_local_sectorsize); 142 if (keepdirty == 0) 143 keepdirty = HAST_KEEPDIRTY; 144 res->hr_datasize = mediasize - METADATA_SIZE - mapsize; 145 res->hr_extentsize = extentsize; 146 res->hr_keepdirty = keepdirty; 147 148 res->hr_localoff = METADATA_SIZE + mapsize; 149 150 if (metadata_write(res) < 0) { 151 ec = EX_IOERR; 152 goto end; 153 } 154 buf = calloc(1, mapsize); 155 if (buf == NULL) { 156 pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", 157 mapsize); 158 ec = EX_TEMPFAIL; 159 goto end; 160 } 161 if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != 162 (ssize_t)mapsize) { 163 pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", 164 res->hr_localpath); 165 free(buf); 166 ec = EX_IOERR; 167 goto end; 168 } 169 free(buf); 170 end: 171 if (res->hr_localfd >= 0) 172 close(res->hr_localfd); 173 pjdlog_prefix_set("%s", ""); 174 return (ec); 175 } 176 177 static void 178 control_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, 179 intmax_t keepdirty) 180 { 181 struct hast_resource *res; 182 int ec, ii, ret; 183 184 /* Initialize the given resources. */ 185 if (argc < 1) 186 usage(); 187 ec = 0; 188 for (ii = 0; ii < argc; ii++) { 189 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 190 if (strcmp(argv[ii], res->hr_name) == 0) 191 break; 192 } 193 if (res == NULL) { 194 pjdlog_error("Unknown resource %s.", argv[ii]); 195 if (ec == 0) 196 ec = EX_DATAERR; 197 continue; 198 } 199 ret = create_one(res, mediasize, extentsize, keepdirty); 200 if (ret != 0 && ec == 0) 201 ec = ret; 202 } 203 exit(ec); 204 } 205 206 static int 207 dump_one(struct hast_resource *res) 208 { 209 int ret; 210 211 ret = metadata_read(res, false); 212 if (ret != 0) 213 return (ret); 214 215 printf("resource: %s\n", res->hr_name); 216 printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize, 217 (intmax_t)res->hr_datasize); 218 printf(" extentsize: %d (%NB)\n", res->hr_extentsize, 219 (intmax_t)res->hr_extentsize); 220 printf(" keepdirty: %d\n", res->hr_keepdirty); 221 printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 222 printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 223 printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 224 printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 225 printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 226 227 return (0); 228 } 229 230 static void 231 control_dump(int argc, char *argv[]) 232 { 233 struct hast_resource *res; 234 int ec, ret; 235 236 /* Dump metadata of the given resource(s). */ 237 238 ec = 0; 239 if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 240 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 241 ret = dump_one(res); 242 if (ret != 0 && ec == 0) 243 ec = ret; 244 } 245 } else { 246 int ii; 247 248 for (ii = 0; ii < argc; ii++) { 249 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 250 if (strcmp(argv[ii], res->hr_name) == 0) 251 break; 252 } 253 if (res == NULL) { 254 pjdlog_error("Unknown resource %s.", argv[ii]); 255 if (ec == 0) 256 ec = EX_DATAERR; 257 continue; 258 } 259 ret = dump_one(res); 260 if (ret != 0 && ec == 0) 261 ec = ret; 262 } 263 } 264 exit(ec); 265 } 266 267 static int 268 control_set_role(struct nv *nv, const char *newrole) 269 { 270 const char *res, *oldrole; 271 unsigned int ii; 272 int error, ret; 273 274 ret = 0; 275 276 for (ii = 0; ; ii++) { 277 res = nv_get_string(nv, "resource%u", ii); 278 if (res == NULL) 279 break; 280 pjdlog_prefix_set("[%s] ", res); 281 error = nv_get_int16(nv, "error%u", ii); 282 if (error != 0) { 283 if (ret == 0) 284 ret = error; 285 pjdlog_warning("Received error %d from hastd.", error); 286 continue; 287 } 288 oldrole = nv_get_string(nv, "role%u", ii); 289 if (strcmp(oldrole, newrole) == 0) 290 pjdlog_debug(2, "Role unchanged (%s).", oldrole); 291 else { 292 pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 293 newrole); 294 } 295 } 296 pjdlog_prefix_set("%s", ""); 297 return (ret); 298 } 299 300 static int 301 control_status(struct nv *nv) 302 { 303 unsigned int ii; 304 const char *str; 305 int error, ret; 306 307 ret = 0; 308 309 for (ii = 0; ; ii++) { 310 str = nv_get_string(nv, "resource%u", ii); 311 if (str == NULL) 312 break; 313 printf("%s:\n", str); 314 error = nv_get_int16(nv, "error%u", ii); 315 if (error != 0) { 316 if (ret == 0) 317 ret = error; 318 printf(" error: %d\n", error); 319 continue; 320 } 321 printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 322 printf(" provname: %s\n", 323 nv_get_string(nv, "provname%u", ii)); 324 printf(" localpath: %s\n", 325 nv_get_string(nv, "localpath%u", ii)); 326 printf(" extentsize: %u (%NB)\n", 327 (unsigned int)nv_get_uint32(nv, "extentsize%u", ii), 328 (intmax_t)nv_get_uint32(nv, "extentsize%u", ii)); 329 printf(" keepdirty: %u\n", 330 (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 331 printf(" remoteaddr: %s\n", 332 nv_get_string(nv, "remoteaddr%u", ii)); 333 str = nv_get_string(nv, "sourceaddr%u", ii); 334 if (str != NULL) 335 printf(" sourceaddr: %s\n", str); 336 printf(" replication: %s\n", 337 nv_get_string(nv, "replication%u", ii)); 338 str = nv_get_string(nv, "status%u", ii); 339 if (str != NULL) 340 printf(" status: %s\n", str); 341 printf(" dirty: %ju (%NB)\n", 342 (uintmax_t)nv_get_uint64(nv, "dirty%u", ii), 343 (intmax_t)nv_get_uint64(nv, "dirty%u", ii)); 344 } 345 return (ret); 346 } 347 348 int 349 main(int argc, char *argv[]) 350 { 351 struct nv *nv; 352 int64_t mediasize, extentsize, keepdirty; 353 int cmd, debug, error, ii; 354 const char *optstr; 355 356 debug = 0; 357 mediasize = extentsize = keepdirty = 0; 358 359 if (argc == 1) 360 usage(); 361 362 if (strcmp(argv[1], "create") == 0) { 363 cmd = CMD_CREATE; 364 optstr = "c:de:k:m:h"; 365 } else if (strcmp(argv[1], "role") == 0) { 366 cmd = CMD_ROLE; 367 optstr = "c:dh"; 368 } else if (strcmp(argv[1], "status") == 0) { 369 cmd = CMD_STATUS; 370 optstr = "c:dh"; 371 } else if (strcmp(argv[1], "dump") == 0) { 372 cmd = CMD_DUMP; 373 optstr = "c:dh"; 374 } else 375 usage(); 376 377 argc--; 378 argv++; 379 380 for (;;) { 381 int ch; 382 383 ch = getopt(argc, argv, optstr); 384 if (ch == -1) 385 break; 386 switch (ch) { 387 case 'c': 388 cfgpath = optarg; 389 break; 390 case 'd': 391 debug++; 392 break; 393 case 'e': 394 if (expand_number(optarg, &extentsize) < 0) 395 err(1, "Invalid extentsize"); 396 break; 397 case 'k': 398 if (expand_number(optarg, &keepdirty) < 0) 399 err(1, "Invalid keepdirty"); 400 break; 401 case 'm': 402 if (expand_number(optarg, &mediasize) < 0) 403 err(1, "Invalid mediasize"); 404 break; 405 case 'h': 406 default: 407 usage(); 408 } 409 } 410 argc -= optind; 411 argv += optind; 412 413 switch (cmd) { 414 case CMD_CREATE: 415 case CMD_ROLE: 416 if (argc == 0) 417 usage(); 418 break; 419 } 420 421 pjdlog_init(PJDLOG_MODE_STD); 422 pjdlog_debug_set(debug); 423 424 cfg = yy_config_parse(cfgpath, true); 425 assert(cfg != NULL); 426 427 switch (cmd) { 428 case CMD_CREATE: 429 control_create(argc, argv, mediasize, extentsize, keepdirty); 430 /* NOTREACHED */ 431 assert(!"What are we doing here?!"); 432 break; 433 case CMD_DUMP: 434 /* Dump metadata from local component of the given resource. */ 435 control_dump(argc, argv); 436 /* NOTREACHED */ 437 assert(!"What are we doing here?!"); 438 break; 439 case CMD_ROLE: 440 /* Change role for the given resources. */ 441 if (argc < 2) 442 usage(); 443 nv = nv_alloc(); 444 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 445 if (strcmp(argv[0], "init") == 0) 446 nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 447 else if (strcmp(argv[0], "primary") == 0) 448 nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 449 else if (strcmp(argv[0], "secondary") == 0) 450 nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 451 else 452 usage(); 453 for (ii = 0; ii < argc - 1; ii++) 454 nv_add_string(nv, argv[ii + 1], "resource%d", ii); 455 break; 456 case CMD_STATUS: 457 /* Obtain status of the given resources. */ 458 nv = nv_alloc(); 459 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 460 if (argc == 0) 461 nv_add_string(nv, "all", "resource%d", 0); 462 else { 463 for (ii = 0; ii < argc; ii++) 464 nv_add_string(nv, argv[ii], "resource%d", ii); 465 } 466 break; 467 default: 468 assert(!"Impossible command!"); 469 } 470 471 /* Setup control connection... */ 472 if (proto_client(NULL, cfg->hc_controladdr, &controlconn) < 0) { 473 pjdlog_exit(EX_OSERR, 474 "Unable to setup control connection to %s", 475 cfg->hc_controladdr); 476 } 477 /* ...and connect to hastd. */ 478 if (proto_connect(controlconn, HAST_TIMEOUT) < 0) { 479 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 480 cfg->hc_controladdr); 481 } 482 483 if (drop_privs(true) != 0) 484 exit(EX_CONFIG); 485 486 /* Send the command to the server... */ 487 if (hast_proto_send(NULL, controlconn, nv, NULL, 0) < 0) { 488 pjdlog_exit(EX_UNAVAILABLE, 489 "Unable to send command to hastd via %s", 490 cfg->hc_controladdr); 491 } 492 nv_free(nv); 493 /* ...and receive reply. */ 494 if (hast_proto_recv_hdr(controlconn, &nv) < 0) { 495 pjdlog_exit(EX_UNAVAILABLE, 496 "cannot receive reply from hastd via %s", 497 cfg->hc_controladdr); 498 } 499 500 error = nv_get_int16(nv, "error"); 501 if (error != 0) { 502 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 503 error); 504 } 505 nv_set_error(nv, 0); 506 507 switch (cmd) { 508 case CMD_ROLE: 509 error = control_set_role(nv, argv[0]); 510 break; 511 case CMD_STATUS: 512 error = control_status(nv); 513 break; 514 default: 515 assert(!"Impossible command!"); 516 } 517 518 exit(error); 519 } 520