hastctl.c revision 248291
1145132Sanholt/*- 2145132Sanholt * Copyright (c) 2009-2010 The FreeBSD Foundation 3145132Sanholt * All rights reserved. 4145132Sanholt * 5145132Sanholt * This software was developed by Pawel Jakub Dawidek under sponsorship from 6145132Sanholt * the FreeBSD Foundation. 7145132Sanholt * 8145132Sanholt * Redistribution and use in source and binary forms, with or without 9145132Sanholt * modification, are permitted provided that the following conditions 10145132Sanholt * are met: 11145132Sanholt * 1. Redistributions of source code must retain the above copyright 12145132Sanholt * notice, this list of conditions and the following disclaimer. 13145132Sanholt * 2. Redistributions in binary form must reproduce the above copyright 14145132Sanholt * notice, this list of conditions and the following disclaimer in the 15145132Sanholt * documentation and/or other materials provided with the distribution. 16145132Sanholt * 17145132Sanholt * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18145132Sanholt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19145132Sanholt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20145132Sanholt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21145132Sanholt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22145132Sanholt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23145132Sanholt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24145132Sanholt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25145132Sanholt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26145132Sanholt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27145132Sanholt * SUCH DAMAGE. 28145132Sanholt */ 29152909Sanholt 30152909Sanholt#include <sys/cdefs.h> 31152909Sanholt__FBSDID("$FreeBSD: head/sbin/hastctl/hastctl.c 248291 2013-03-14 22:29:37Z marck $"); 32182080Srnoland 33182080Srnoland#include <sys/param.h> 34182080Srnoland 35182080Srnoland#include <err.h> 36182080Srnoland#include <libutil.h> 37145132Sanholt#include <stdio.h> 38145132Sanholt#include <string.h> 39152909Sanholt#include <unistd.h> 40182080Srnoland 41145132Sanholt#include <activemap.h> 42207069Srnoland 43207069Srnoland#include "hast.h" 44207069Srnoland#include "hast_proto.h" 45182080Srnoland#include "metadata.h" 46182884Srnoland#include "nv.h" 47182884Srnoland#include "pjdlog.h" 48182884Srnoland#include "proto.h" 49145132Sanholt#include "subr.h" 50182884Srnoland 51182883Srnoland/* Path to configuration file. */ 52182884Srnolandstatic const char *cfgpath = HAST_CONFIG; 53182884Srnoland/* Hastd configuration. */ 54182884Srnolandstatic struct hastd_config *cfg; 55182884Srnoland/* Control connection. */ 56182884Srnolandstatic struct proto_conn *controlconn; 57182884Srnoland 58182884Srnolandenum { 59182884Srnoland CMD_INVALID, 60182884Srnoland CMD_CREATE, 61182884Srnoland CMD_ROLE, 62182884Srnoland CMD_STATUS, 63182884Srnoland CMD_DUMP, 64182884Srnoland CMD_LIST 65182884Srnoland}; 66182884Srnoland 67182884Srnolandstatic __dead2 void 68183833Srnolandusage(void) 69183833Srnoland{ 70182884Srnoland 71182884Srnoland fprintf(stderr, 72182884Srnoland "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n" 73182883Srnoland "\t\t[-m mediasize] name ...\n", 74182884Srnoland getprogname()); 75182884Srnoland fprintf(stderr, 76182884Srnoland " %s role [-d] [-c config] <init | primary | secondary> all | name ...\n", 77182884Srnoland getprogname()); 78182884Srnoland fprintf(stderr, 79190123Srnoland " %s list [-d] [-c config] [all | name ...]\n", 80182884Srnoland getprogname()); 81182884Srnoland fprintf(stderr, 82183833Srnoland " %s status [-d] [-c config] [all | name ...]\n", 83182884Srnoland getprogname()); 84182884Srnoland fprintf(stderr, 85182884Srnoland " %s dump [-d] [-c config] [all | name ...]\n", 86190282Srnoland getprogname()); 87190282Srnoland exit(EX_USAGE); 88190282Srnoland} 89190282Srnoland 90190282Srnolandstatic int 91182884Srnolandcreate_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, 92182884Srnoland intmax_t keepdirty) 93183833Srnoland{ 94182884Srnoland unsigned char *buf; 95182884Srnoland size_t mapsize; 96182883Srnoland int ec; 97182884Srnoland 98182884Srnoland ec = 0; 99190282Srnoland pjdlog_prefix_set("[%s] ", res->hr_name); 100190282Srnoland 101182884Srnoland if (provinfo(res, true) == -1) { 102182884Srnoland ec = EX_NOINPUT; 103182884Srnoland goto end; 104183833Srnoland } 105182080Srnoland if (mediasize == 0) 106182884Srnoland mediasize = res->hr_local_mediasize; 107145132Sanholt else if (mediasize > res->hr_local_mediasize) { 108190399Srnoland pjdlog_error("Provided mediasize is larger than provider %s size.", 109182883Srnoland res->hr_localpath); 110182080Srnoland ec = EX_DATAERR; 111182080Srnoland goto end; 112182080Srnoland } 113182884Srnoland if (!powerof2(res->hr_local_sectorsize)) { 114182884Srnoland pjdlog_error("Sector size of provider %s is not power of 2 (%u).", 115182884Srnoland res->hr_localpath, res->hr_local_sectorsize); 116182080Srnoland ec = EX_DATAERR; 117190399Srnoland goto end; 118182884Srnoland } 119182884Srnoland if (extentsize == 0) 120182884Srnoland extentsize = HAST_EXTENTSIZE; 121183833Srnoland if (extentsize < res->hr_local_sectorsize) { 122190399Srnoland pjdlog_error("Extent size (%jd) is less than sector size (%u).", 123182080Srnoland (intmax_t)extentsize, res->hr_local_sectorsize); 124182080Srnoland ec = EX_DATAERR; 125182884Srnoland goto end; 126182884Srnoland } 127182884Srnoland if ((extentsize % res->hr_local_sectorsize) != 0) { 128182080Srnoland pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", 129182080Srnoland (intmax_t)extentsize, res->hr_local_sectorsize); 130152909Sanholt ec = EX_DATAERR; 131182080Srnoland goto end; 132152909Sanholt } 133145132Sanholt mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, 134145132Sanholt extentsize, res->hr_local_sectorsize); 135182080Srnoland if (keepdirty == 0) 136182080Srnoland keepdirty = HAST_KEEPDIRTY; 137182080Srnoland res->hr_datasize = mediasize - METADATA_SIZE - mapsize; 138190399Srnoland res->hr_extentsize = extentsize; 139182080Srnoland res->hr_keepdirty = keepdirty; 140182080Srnoland 141182080Srnoland res->hr_localoff = METADATA_SIZE + mapsize; 142182080Srnoland 143182080Srnoland if (metadata_write(res) == -1) { 144182080Srnoland ec = EX_IOERR; 145182080Srnoland goto end; 146182884Srnoland } 147182884Srnoland buf = calloc(1, mapsize); 148182884Srnoland if (buf == NULL) { 149182080Srnoland pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", 150182080Srnoland mapsize); 151182080Srnoland ec = EX_TEMPFAIL; 152182080Srnoland goto end; 153182080Srnoland } 154182884Srnoland if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != 155182080Srnoland (ssize_t)mapsize) { 156182080Srnoland pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", 157182080Srnoland res->hr_localpath); 158182080Srnoland free(buf); 159182080Srnoland ec = EX_IOERR; 160182080Srnoland goto end; 161182080Srnoland } 162182080Srnoland free(buf); 163182080Srnolandend: 164152909Sanholt if (res->hr_localfd >= 0) 165182080Srnoland close(res->hr_localfd); 166182080Srnoland pjdlog_prefix_set("%s", ""); 167182080Srnoland return (ec); 168182080Srnoland} 169182080Srnoland 170182080Srnolandstatic void 171152909Sanholtcontrol_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, 172182080Srnoland intmax_t keepdirty) 173190399Srnoland{ 174190399Srnoland struct hast_resource *res; 175152909Sanholt int ec, ii, ret; 176182080Srnoland 177182080Srnoland /* Initialize the given resources. */ 178182080Srnoland if (argc < 1) 179182080Srnoland usage(); 180145132Sanholt ec = 0; 181145132Sanholt for (ii = 0; ii < argc; ii++) { 182182080Srnoland TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 183145132Sanholt if (strcmp(argv[ii], res->hr_name) == 0) 184182080Srnoland break; 185182080Srnoland } 186182080Srnoland if (res == NULL) { 187182080Srnoland pjdlog_error("Unknown resource %s.", argv[ii]); 188182080Srnoland if (ec == 0) 189182080Srnoland ec = EX_DATAERR; 190152909Sanholt continue; 191145132Sanholt } 192182080Srnoland ret = create_one(res, mediasize, extentsize, keepdirty); 193182080Srnoland if (ret != 0 && ec == 0) 194145132Sanholt ec = ret; 195182080Srnoland } 196182080Srnoland exit(ec); 197182080Srnoland} 198182884Srnoland 199182884Srnolandstatic int 200207069Srnolanddump_one(struct hast_resource *res) 201207069Srnoland{ 202182080Srnoland int ret; 203182080Srnoland 204182080Srnoland ret = metadata_read(res, false); 205182884Srnoland if (ret != 0) 206182884Srnoland return (ret); 207207069Srnoland 208207069Srnoland printf("resource: %s\n", res->hr_name); 209182080Srnoland printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize, 210182080Srnoland (intmax_t)res->hr_datasize); 211182080Srnoland printf(" extentsize: %d (%NB)\n", res->hr_extentsize, 212182080Srnoland (intmax_t)res->hr_extentsize); 213182080Srnoland printf(" keepdirty: %d\n", res->hr_keepdirty); 214182080Srnoland printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 215148211Sanholt printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 216182080Srnoland printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 217145132Sanholt printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 218145132Sanholt printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 219145132Sanholt 220182080Srnoland return (0); 221145132Sanholt} 222182080Srnoland 223182080Srnolandstatic void 224182080Srnolandcontrol_dump(int argc, char *argv[]) 225182080Srnoland{ 226145132Sanholt struct hast_resource *res; 227 int ec, ret; 228 229 /* Dump metadata of the given resource(s). */ 230 231 ec = 0; 232 if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 233 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 234 ret = dump_one(res); 235 if (ret != 0 && ec == 0) 236 ec = ret; 237 } 238 } else { 239 int ii; 240 241 for (ii = 0; ii < argc; ii++) { 242 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 243 if (strcmp(argv[ii], res->hr_name) == 0) 244 break; 245 } 246 if (res == NULL) { 247 pjdlog_error("Unknown resource %s.", argv[ii]); 248 if (ec == 0) 249 ec = EX_DATAERR; 250 continue; 251 } 252 ret = dump_one(res); 253 if (ret != 0 && ec == 0) 254 ec = ret; 255 } 256 } 257 exit(ec); 258} 259 260static int 261control_set_role(struct nv *nv, const char *newrole) 262{ 263 const char *res, *oldrole; 264 unsigned int ii; 265 int error, ret; 266 267 ret = 0; 268 269 for (ii = 0; ; ii++) { 270 res = nv_get_string(nv, "resource%u", ii); 271 if (res == NULL) 272 break; 273 pjdlog_prefix_set("[%s] ", res); 274 error = nv_get_int16(nv, "error%u", ii); 275 if (error != 0) { 276 if (ret == 0) 277 ret = error; 278 pjdlog_warning("Received error %d from hastd.", error); 279 continue; 280 } 281 oldrole = nv_get_string(nv, "role%u", ii); 282 if (strcmp(oldrole, newrole) == 0) 283 pjdlog_debug(2, "Role unchanged (%s).", oldrole); 284 else { 285 pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 286 newrole); 287 } 288 } 289 pjdlog_prefix_set("%s", ""); 290 return (ret); 291} 292 293static int 294control_list(struct nv *nv) 295{ 296 unsigned int ii; 297 const char *str; 298 int error, ret; 299 300 ret = 0; 301 302 for (ii = 0; ; ii++) { 303 str = nv_get_string(nv, "resource%u", ii); 304 if (str == NULL) 305 break; 306 printf("%s:\n", str); 307 error = nv_get_int16(nv, "error%u", ii); 308 if (error != 0) { 309 if (ret == 0) 310 ret = error; 311 printf(" error: %d\n", error); 312 continue; 313 } 314 printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 315 printf(" provname: %s\n", 316 nv_get_string(nv, "provname%u", ii)); 317 printf(" localpath: %s\n", 318 nv_get_string(nv, "localpath%u", ii)); 319 printf(" extentsize: %u (%NB)\n", 320 (unsigned int)nv_get_uint32(nv, "extentsize%u", ii), 321 (intmax_t)nv_get_uint32(nv, "extentsize%u", ii)); 322 printf(" keepdirty: %u\n", 323 (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 324 printf(" remoteaddr: %s\n", 325 nv_get_string(nv, "remoteaddr%u", ii)); 326 str = nv_get_string(nv, "sourceaddr%u", ii); 327 if (str != NULL) 328 printf(" sourceaddr: %s\n", str); 329 printf(" replication: %s\n", 330 nv_get_string(nv, "replication%u", ii)); 331 str = nv_get_string(nv, "status%u", ii); 332 if (str != NULL) 333 printf(" status: %s\n", str); 334 printf(" dirty: %ju (%NB)\n", 335 (uintmax_t)nv_get_uint64(nv, "dirty%u", ii), 336 (intmax_t)nv_get_uint64(nv, "dirty%u", ii)); 337 printf(" statistics:\n"); 338 printf(" reads: %ju\n", 339 (uintmax_t)nv_get_uint64(nv, "stat_read%u", ii)); 340 printf(" writes: %ju\n", 341 (uintmax_t)nv_get_uint64(nv, "stat_write%u", ii)); 342 printf(" deletes: %ju\n", 343 (uintmax_t)nv_get_uint64(nv, "stat_delete%u", ii)); 344 printf(" flushes: %ju\n", 345 (uintmax_t)nv_get_uint64(nv, "stat_flush%u", ii)); 346 printf(" activemap updates: %ju\n", 347 (uintmax_t)nv_get_uint64(nv, "stat_activemap_update%u", ii)); 348 printf(" local errors: " 349 "read: %ju, write: %ju, delete: %ju, flush: %ju\n", 350 (uintmax_t)nv_get_uint64(nv, "stat_read_error%u", ii), 351 (uintmax_t)nv_get_uint64(nv, "stat_write_error%u", ii), 352 (uintmax_t)nv_get_uint64(nv, "stat_delete_error%u", ii), 353 (uintmax_t)nv_get_uint64(nv, "stat_flush_error%u", ii)); 354 } 355 return (ret); 356} 357 358static int 359control_status(struct nv *nv) 360{ 361 unsigned int ii; 362 const char *str; 363 int error, hprinted, ret; 364 365 hprinted = 0; 366 ret = 0; 367 368 for (ii = 0; ; ii++) { 369 str = nv_get_string(nv, "resource%u", ii); 370 if (str == NULL) 371 break; 372 if (!hprinted) { 373 printf("Name\tStatus\t Role\t\tComponents\n"); 374 hprinted = 1; 375 } 376 printf("%s\t", str); 377 error = nv_get_int16(nv, "error%u", ii); 378 if (error != 0) { 379 if (ret == 0) 380 ret = error; 381 printf("ERR%d\n", error); 382 continue; 383 } 384 str = nv_get_string(nv, "status%u", ii); 385 printf("%-9s", (str != NULL) ? str : "-"); 386 printf("%-15s", nv_get_string(nv, "role%u", ii)); 387 printf("%s\t", 388 nv_get_string(nv, "localpath%u", ii)); 389 printf("%s\n", 390 nv_get_string(nv, "remoteaddr%u", ii)); 391 } 392 return (ret); 393} 394 395int 396main(int argc, char *argv[]) 397{ 398 struct nv *nv; 399 int64_t mediasize, extentsize, keepdirty; 400 int cmd, debug, error, ii; 401 const char *optstr; 402 403 debug = 0; 404 mediasize = extentsize = keepdirty = 0; 405 406 if (argc == 1) 407 usage(); 408 409 if (strcmp(argv[1], "create") == 0) { 410 cmd = CMD_CREATE; 411 optstr = "c:de:k:m:h"; 412 } else if (strcmp(argv[1], "role") == 0) { 413 cmd = CMD_ROLE; 414 optstr = "c:dh"; 415 } else if (strcmp(argv[1], "list") == 0) { 416 cmd = CMD_LIST; 417 optstr = "c:dh"; 418 } else if (strcmp(argv[1], "status") == 0) { 419 cmd = CMD_STATUS; 420 optstr = "c:dh"; 421 } else if (strcmp(argv[1], "dump") == 0) { 422 cmd = CMD_DUMP; 423 optstr = "c:dh"; 424 } else 425 usage(); 426 427 argc--; 428 argv++; 429 430 for (;;) { 431 int ch; 432 433 ch = getopt(argc, argv, optstr); 434 if (ch == -1) 435 break; 436 switch (ch) { 437 case 'c': 438 cfgpath = optarg; 439 break; 440 case 'd': 441 debug++; 442 break; 443 case 'e': 444 if (expand_number(optarg, &extentsize) == -1) 445 errx(EX_USAGE, "Invalid extentsize"); 446 break; 447 case 'k': 448 if (expand_number(optarg, &keepdirty) == -1) 449 errx(EX_USAGE, "Invalid keepdirty"); 450 break; 451 case 'm': 452 if (expand_number(optarg, &mediasize) == -1) 453 errx(EX_USAGE, "Invalid mediasize"); 454 break; 455 case 'h': 456 default: 457 usage(); 458 } 459 } 460 argc -= optind; 461 argv += optind; 462 463 switch (cmd) { 464 case CMD_CREATE: 465 case CMD_ROLE: 466 if (argc == 0) 467 usage(); 468 break; 469 } 470 471 pjdlog_init(PJDLOG_MODE_STD); 472 pjdlog_debug_set(debug); 473 474 cfg = yy_config_parse(cfgpath, true); 475 PJDLOG_ASSERT(cfg != NULL); 476 477 switch (cmd) { 478 case CMD_CREATE: 479 control_create(argc, argv, mediasize, extentsize, keepdirty); 480 /* NOTREACHED */ 481 PJDLOG_ABORT("What are we doing here?!"); 482 break; 483 case CMD_DUMP: 484 /* Dump metadata from local component of the given resource. */ 485 control_dump(argc, argv); 486 /* NOTREACHED */ 487 PJDLOG_ABORT("What are we doing here?!"); 488 break; 489 case CMD_ROLE: 490 /* Change role for the given resources. */ 491 if (argc < 2) 492 usage(); 493 nv = nv_alloc(); 494 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 495 if (strcmp(argv[0], "init") == 0) 496 nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 497 else if (strcmp(argv[0], "primary") == 0) 498 nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 499 else if (strcmp(argv[0], "secondary") == 0) 500 nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 501 else 502 usage(); 503 for (ii = 0; ii < argc - 1; ii++) 504 nv_add_string(nv, argv[ii + 1], "resource%d", ii); 505 break; 506 case CMD_LIST: 507 /* Obtain verbose status of the given resources. */ 508 nv = nv_alloc(); 509 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 510 if (argc == 0) 511 nv_add_string(nv, "all", "resource%d", 0); 512 else { 513 for (ii = 0; ii < argc; ii++) 514 nv_add_string(nv, argv[ii], "resource%d", ii); 515 } 516 break; 517 case CMD_STATUS: 518 /* Obtain brief status of the given resources. */ 519 nv = nv_alloc(); 520 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 521 if (argc == 0) 522 nv_add_string(nv, "all", "resource%d", 0); 523 else { 524 for (ii = 0; ii < argc; ii++) 525 nv_add_string(nv, argv[ii], "resource%d", ii); 526 } 527 break; 528 default: 529 PJDLOG_ABORT("Impossible command!"); 530 } 531 532 /* Setup control connection... */ 533 if (proto_client(NULL, cfg->hc_controladdr, &controlconn) == -1) { 534 pjdlog_exit(EX_OSERR, 535 "Unable to setup control connection to %s", 536 cfg->hc_controladdr); 537 } 538 /* ...and connect to hastd. */ 539 if (proto_connect(controlconn, HAST_TIMEOUT) == -1) { 540 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 541 cfg->hc_controladdr); 542 } 543 544 if (drop_privs(NULL) != 0) 545 exit(EX_CONFIG); 546 547 /* Send the command to the server... */ 548 if (hast_proto_send(NULL, controlconn, nv, NULL, 0) == -1) { 549 pjdlog_exit(EX_UNAVAILABLE, 550 "Unable to send command to hastd via %s", 551 cfg->hc_controladdr); 552 } 553 nv_free(nv); 554 /* ...and receive reply. */ 555 if (hast_proto_recv_hdr(controlconn, &nv) == -1) { 556 pjdlog_exit(EX_UNAVAILABLE, 557 "cannot receive reply from hastd via %s", 558 cfg->hc_controladdr); 559 } 560 561 error = nv_get_int16(nv, "error"); 562 if (error != 0) { 563 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 564 error); 565 } 566 nv_set_error(nv, 0); 567 568 switch (cmd) { 569 case CMD_ROLE: 570 error = control_set_role(nv, argv[0]); 571 break; 572 case CMD_LIST: 573 error = control_list(nv); 574 break; 575 case CMD_STATUS: 576 error = control_status(nv); 577 break; 578 default: 579 PJDLOG_ABORT("Impossible command!"); 580 } 581 582 exit(error); 583} 584