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