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