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