hastctl.c revision 251025
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: stable/9/sbin/hastctl/hastctl.c 251025 2013-05-27 13:49:55Z marck $"); 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 <err.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <libutil.h> 43#include <limits.h> 44#include <signal.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <sysexits.h> 49#include <unistd.h> 50 51#include <activemap.h> 52 53#include "hast.h" 54#include "hast_proto.h" 55#include "metadata.h" 56#include "nv.h" 57#include "pjdlog.h" 58#include "proto.h" 59#include "subr.h" 60 61/* Path to configuration file. */ 62static const char *cfgpath = HAST_CONFIG; 63/* Hastd configuration. */ 64static struct hastd_config *cfg; 65/* Control connection. */ 66static struct proto_conn *controlconn; 67 68enum { 69 CMD_INVALID, 70 CMD_CREATE, 71 CMD_ROLE, 72 CMD_STATUS, 73 CMD_DUMP, 74 CMD_LIST 75}; 76 77static __dead2 void 78usage(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 list [-d] [-c config] [all | name ...]\n", 90 getprogname()); 91 fprintf(stderr, 92 " %s status [-d] [-c config] [all | name ...]\n", 93 getprogname()); 94 fprintf(stderr, 95 " %s dump [-d] [-c config] [all | name ...]\n", 96 getprogname()); 97 exit(EX_USAGE); 98} 99 100static int 101create_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, 102 intmax_t keepdirty) 103{ 104 unsigned char *buf; 105 size_t mapsize; 106 int ec; 107 108 ec = 0; 109 pjdlog_prefix_set("[%s] ", res->hr_name); 110 111 if (provinfo(res, true) == -1) { 112 ec = EX_NOINPUT; 113 goto end; 114 } 115 if (mediasize == 0) 116 mediasize = res->hr_local_mediasize; 117 else if (mediasize > res->hr_local_mediasize) { 118 pjdlog_error("Provided mediasize is larger than provider %s size.", 119 res->hr_localpath); 120 ec = EX_DATAERR; 121 goto end; 122 } 123 if (!powerof2(res->hr_local_sectorsize)) { 124 pjdlog_error("Sector size of provider %s is not power of 2 (%u).", 125 res->hr_localpath, res->hr_local_sectorsize); 126 ec = EX_DATAERR; 127 goto end; 128 } 129 if (extentsize == 0) 130 extentsize = HAST_EXTENTSIZE; 131 if (extentsize < res->hr_local_sectorsize) { 132 pjdlog_error("Extent size (%jd) is less than sector size (%u).", 133 (intmax_t)extentsize, res->hr_local_sectorsize); 134 ec = EX_DATAERR; 135 goto end; 136 } 137 if ((extentsize % res->hr_local_sectorsize) != 0) { 138 pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", 139 (intmax_t)extentsize, res->hr_local_sectorsize); 140 ec = EX_DATAERR; 141 goto end; 142 } 143 mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, 144 extentsize, res->hr_local_sectorsize); 145 if (keepdirty == 0) 146 keepdirty = HAST_KEEPDIRTY; 147 res->hr_datasize = mediasize - METADATA_SIZE - mapsize; 148 res->hr_extentsize = extentsize; 149 res->hr_keepdirty = keepdirty; 150 151 res->hr_localoff = METADATA_SIZE + mapsize; 152 153 if (metadata_write(res) == -1) { 154 ec = EX_IOERR; 155 goto end; 156 } 157 buf = calloc(1, mapsize); 158 if (buf == NULL) { 159 pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", 160 mapsize); 161 ec = EX_TEMPFAIL; 162 goto end; 163 } 164 if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != 165 (ssize_t)mapsize) { 166 pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", 167 res->hr_localpath); 168 free(buf); 169 ec = EX_IOERR; 170 goto end; 171 } 172 free(buf); 173end: 174 if (res->hr_localfd >= 0) 175 close(res->hr_localfd); 176 pjdlog_prefix_set("%s", ""); 177 return (ec); 178} 179 180static void 181control_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, 182 intmax_t keepdirty) 183{ 184 struct hast_resource *res; 185 int ec, ii, ret; 186 187 /* Initialize the given resources. */ 188 if (argc < 1) 189 usage(); 190 ec = 0; 191 for (ii = 0; ii < argc; ii++) { 192 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 193 if (strcmp(argv[ii], res->hr_name) == 0) 194 break; 195 } 196 if (res == NULL) { 197 pjdlog_error("Unknown resource %s.", argv[ii]); 198 if (ec == 0) 199 ec = EX_DATAERR; 200 continue; 201 } 202 ret = create_one(res, mediasize, extentsize, keepdirty); 203 if (ret != 0 && ec == 0) 204 ec = ret; 205 } 206 exit(ec); 207} 208 209static int 210dump_one(struct hast_resource *res) 211{ 212 int ret; 213 214 ret = metadata_read(res, false); 215 if (ret != 0) 216 return (ret); 217 218 printf("resource: %s\n", res->hr_name); 219 printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize, 220 (intmax_t)res->hr_datasize); 221 printf(" extentsize: %d (%NB)\n", res->hr_extentsize, 222 (intmax_t)res->hr_extentsize); 223 printf(" keepdirty: %d\n", res->hr_keepdirty); 224 printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 225 printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 226 printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 227 printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 228 printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 229 230 return (0); 231} 232 233static void 234control_dump(int argc, char *argv[]) 235{ 236 struct hast_resource *res; 237 int ec, ret; 238 239 /* Dump metadata of the given resource(s). */ 240 241 ec = 0; 242 if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 243 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 244 ret = dump_one(res); 245 if (ret != 0 && ec == 0) 246 ec = ret; 247 } 248 } else { 249 int ii; 250 251 for (ii = 0; ii < argc; ii++) { 252 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 253 if (strcmp(argv[ii], res->hr_name) == 0) 254 break; 255 } 256 if (res == NULL) { 257 pjdlog_error("Unknown resource %s.", argv[ii]); 258 if (ec == 0) 259 ec = EX_DATAERR; 260 continue; 261 } 262 ret = dump_one(res); 263 if (ret != 0 && ec == 0) 264 ec = ret; 265 } 266 } 267 exit(ec); 268} 269 270static int 271control_set_role(struct nv *nv, const char *newrole) 272{ 273 const char *res, *oldrole; 274 unsigned int ii; 275 int error, ret; 276 277 ret = 0; 278 279 for (ii = 0; ; ii++) { 280 res = nv_get_string(nv, "resource%u", ii); 281 if (res == NULL) 282 break; 283 pjdlog_prefix_set("[%s] ", res); 284 error = nv_get_int16(nv, "error%u", ii); 285 if (error != 0) { 286 if (ret == 0) 287 ret = error; 288 pjdlog_warning("Received error %d from hastd.", error); 289 continue; 290 } 291 oldrole = nv_get_string(nv, "role%u", ii); 292 if (strcmp(oldrole, newrole) == 0) 293 pjdlog_debug(2, "Role unchanged (%s).", oldrole); 294 else { 295 pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 296 newrole); 297 } 298 } 299 pjdlog_prefix_set("%s", ""); 300 return (ret); 301} 302 303static int 304control_status(struct nv *nv) 305{ 306 unsigned int ii; 307 const char *str; 308 int error, ret; 309 310 ret = 0; 311 312 for (ii = 0; ; ii++) { 313 str = nv_get_string(nv, "resource%u", ii); 314 if (str == NULL) 315 break; 316 printf("%s:\n", str); 317 error = nv_get_int16(nv, "error%u", ii); 318 if (error != 0) { 319 if (ret == 0) 320 ret = error; 321 printf(" error: %d\n", error); 322 continue; 323 } 324 printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 325 printf(" provname: %s\n", 326 nv_get_string(nv, "provname%u", ii)); 327 printf(" localpath: %s\n", 328 nv_get_string(nv, "localpath%u", ii)); 329 printf(" extentsize: %u (%NB)\n", 330 (unsigned int)nv_get_uint32(nv, "extentsize%u", ii), 331 (intmax_t)nv_get_uint32(nv, "extentsize%u", ii)); 332 printf(" keepdirty: %u\n", 333 (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 334 printf(" remoteaddr: %s\n", 335 nv_get_string(nv, "remoteaddr%u", ii)); 336 str = nv_get_string(nv, "sourceaddr%u", ii); 337 if (str != NULL) 338 printf(" sourceaddr: %s\n", str); 339 printf(" replication: %s\n", 340 nv_get_string(nv, "replication%u", ii)); 341 str = nv_get_string(nv, "status%u", ii); 342 if (str != NULL) 343 printf(" status: %s\n", str); 344 printf(" dirty: %ju (%NB)\n", 345 (uintmax_t)nv_get_uint64(nv, "dirty%u", ii), 346 (intmax_t)nv_get_uint64(nv, "dirty%u", ii)); 347 printf(" statistics:\n"); 348 printf(" reads: %ju\n", 349 (uintmax_t)nv_get_uint64(nv, "stat_read%u", ii)); 350 printf(" writes: %ju\n", 351 (uintmax_t)nv_get_uint64(nv, "stat_write%u", ii)); 352 printf(" deletes: %ju\n", 353 (uintmax_t)nv_get_uint64(nv, "stat_delete%u", ii)); 354 printf(" flushes: %ju\n", 355 (uintmax_t)nv_get_uint64(nv, "stat_flush%u", ii)); 356 printf(" activemap updates: %ju\n", 357 (uintmax_t)nv_get_uint64(nv, "stat_activemap_update%u", ii)); 358 printf(" local errors: " 359 "read: %ju, write: %ju, delete: %ju, flush: %ju\n", 360 (uintmax_t)nv_get_uint64(nv, "stat_read_error%u", ii), 361 (uintmax_t)nv_get_uint64(nv, "stat_write_error%u", ii), 362 (uintmax_t)nv_get_uint64(nv, "stat_delete_error%u", ii), 363 (uintmax_t)nv_get_uint64(nv, "stat_flush_error%u", ii)); 364 } 365 return (ret); 366} 367 368int 369main(int argc, char *argv[]) 370{ 371 struct nv *nv; 372 int64_t mediasize, extentsize, keepdirty; 373 int cmd, debug, error, ii; 374 const char *optstr; 375 376 debug = 0; 377 mediasize = extentsize = keepdirty = 0; 378 379 if (argc == 1) 380 usage(); 381 382 if (strcmp(argv[1], "create") == 0) { 383 cmd = CMD_CREATE; 384 optstr = "c:de:k:m:h"; 385 } else if (strcmp(argv[1], "role") == 0) { 386 cmd = CMD_ROLE; 387 optstr = "c:dh"; 388 } else if (strcmp(argv[1], "list") == 0) { 389 cmd = CMD_LIST; 390 optstr = "c:dh"; 391 } else if (strcmp(argv[1], "status") == 0) { 392 cmd = CMD_STATUS; 393 optstr = "c:dh"; 394 } else if (strcmp(argv[1], "dump") == 0) { 395 cmd = CMD_DUMP; 396 optstr = "c:dh"; 397 } else 398 usage(); 399 400 argc--; 401 argv++; 402 403 for (;;) { 404 int ch; 405 406 ch = getopt(argc, argv, optstr); 407 if (ch == -1) 408 break; 409 switch (ch) { 410 case 'c': 411 cfgpath = optarg; 412 break; 413 case 'd': 414 debug++; 415 break; 416 case 'e': 417 if (expand_number(optarg, &extentsize) == -1) 418 errx(EX_USAGE, "Invalid extentsize"); 419 break; 420 case 'k': 421 if (expand_number(optarg, &keepdirty) == -1) 422 errx(EX_USAGE, "Invalid keepdirty"); 423 break; 424 case 'm': 425 if (expand_number(optarg, &mediasize) == -1) 426 errx(EX_USAGE, "Invalid mediasize"); 427 break; 428 case 'h': 429 default: 430 usage(); 431 } 432 } 433 argc -= optind; 434 argv += optind; 435 436 switch (cmd) { 437 case CMD_CREATE: 438 case CMD_ROLE: 439 if (argc == 0) 440 usage(); 441 break; 442 } 443 444 pjdlog_init(PJDLOG_MODE_STD); 445 pjdlog_debug_set(debug); 446 447 cfg = yy_config_parse(cfgpath, true); 448 PJDLOG_ASSERT(cfg != NULL); 449 450 switch (cmd) { 451 case CMD_CREATE: 452 control_create(argc, argv, mediasize, extentsize, keepdirty); 453 /* NOTREACHED */ 454 PJDLOG_ABORT("What are we doing here?!"); 455 break; 456 case CMD_DUMP: 457 /* Dump metadata from local component of the given resource. */ 458 control_dump(argc, argv); 459 /* NOTREACHED */ 460 PJDLOG_ABORT("What are we doing here?!"); 461 break; 462 case CMD_ROLE: 463 /* Change role for the given resources. */ 464 if (argc < 2) 465 usage(); 466 nv = nv_alloc(); 467 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 468 if (strcmp(argv[0], "init") == 0) 469 nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 470 else if (strcmp(argv[0], "primary") == 0) 471 nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 472 else if (strcmp(argv[0], "secondary") == 0) 473 nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 474 else 475 usage(); 476 for (ii = 0; ii < argc - 1; ii++) 477 nv_add_string(nv, argv[ii + 1], "resource%d", ii); 478 break; 479 case CMD_LIST: 480 case CMD_STATUS: 481 /* Obtain status of the given resources. */ 482 nv = nv_alloc(); 483 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 484 if (argc == 0) 485 nv_add_string(nv, "all", "resource%d", 0); 486 else { 487 for (ii = 0; ii < argc; ii++) 488 nv_add_string(nv, argv[ii], "resource%d", ii); 489 } 490 break; 491 default: 492 PJDLOG_ABORT("Impossible command!"); 493 } 494 495 /* Setup control connection... */ 496 if (proto_client(NULL, cfg->hc_controladdr, &controlconn) == -1) { 497 pjdlog_exit(EX_OSERR, 498 "Unable to setup control connection to %s", 499 cfg->hc_controladdr); 500 } 501 /* ...and connect to hastd. */ 502 if (proto_connect(controlconn, HAST_TIMEOUT) == -1) { 503 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 504 cfg->hc_controladdr); 505 } 506 507 if (drop_privs(NULL) != 0) 508 exit(EX_CONFIG); 509 510 /* Send the command to the server... */ 511 if (hast_proto_send(NULL, controlconn, nv, NULL, 0) == -1) { 512 pjdlog_exit(EX_UNAVAILABLE, 513 "Unable to send command to hastd via %s", 514 cfg->hc_controladdr); 515 } 516 nv_free(nv); 517 /* ...and receive reply. */ 518 if (hast_proto_recv_hdr(controlconn, &nv) == -1) { 519 pjdlog_exit(EX_UNAVAILABLE, 520 "cannot receive reply from hastd via %s", 521 cfg->hc_controladdr); 522 } 523 524 error = nv_get_int16(nv, "error"); 525 if (error != 0) { 526 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 527 error); 528 } 529 nv_set_error(nv, 0); 530 531 switch (cmd) { 532 case CMD_ROLE: 533 error = control_set_role(nv, argv[0]); 534 break; 535 case CMD_LIST: 536 case CMD_STATUS: 537 error = control_status(nv); 538 break; 539 default: 540 PJDLOG_ABORT("Impossible command!"); 541 } 542 543 exit(error); 544} 545