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