hastctl.c revision 248286
10SN/A/*- 26073SN/A * Copyright (c) 2009-2010 The FreeBSD Foundation 30SN/A * All rights reserved. 40SN/A * 50SN/A * This software was developed by Pawel Jakub Dawidek under sponsorship from 60SN/A * the FreeBSD Foundation. 72362SN/A * 80SN/A * Redistribution and use in source and binary forms, with or without 92362SN/A * modification, are permitted provided that the following conditions 100SN/A * are met: 110SN/A * 1. Redistributions of source code must retain the above copyright 120SN/A * notice, this list of conditions and the following disclaimer. 130SN/A * 2. Redistributions in binary form must reproduce the above copyright 140SN/A * notice, this list of conditions and the following disclaimer in the 150SN/A * documentation and/or other materials provided with the distribution. 160SN/A * 170SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 180SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 190SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 200SN/A * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 212362SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 222362SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 232362SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 240SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 250SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 260SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 270SN/A * SUCH DAMAGE. 280SN/A */ 290SN/A 300SN/A#include <sys/cdefs.h> 310SN/A__FBSDID("$FreeBSD: head/sbin/hastctl/hastctl.c 248286 2013-03-14 21:21:14Z pjd $"); 320SN/A 330SN/A#include <sys/param.h> 340SN/A 350SN/A#include <err.h> 360SN/A#include <libutil.h> 370SN/A#include <stdio.h> 380SN/A#include <string.h> 390SN/A#include <unistd.h> 400SN/A 410SN/A#include <activemap.h> 420SN/A 430SN/A#include "hast.h" 440SN/A#include "hast_proto.h" 450SN/A#include "metadata.h" 460SN/A#include "nv.h" 470SN/A#include "pjdlog.h" 480SN/A#include "proto.h" 494138SN/A#include "subr.h" 500SN/A 510SN/A/* Path to configuration file. */ 520SN/Astatic const char *cfgpath = HAST_CONFIG; 530SN/A/* Hastd configuration. */ 544138SN/Astatic struct hastd_config *cfg; 554138SN/A/* Control connection. */ 564138SN/Astatic struct proto_conn *controlconn; 574138SN/A 584138SN/Aenum { 594138SN/A CMD_INVALID, 604138SN/A CMD_CREATE, 614138SN/A CMD_ROLE, 624138SN/A CMD_STATUS, 634138SN/A CMD_DUMP 644138SN/A}; 654138SN/A 664138SN/Astatic __dead2 void 674138SN/Ausage(void) 684138SN/A{ 694138SN/A 700SN/A fprintf(stderr, 710SN/A "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n" 720SN/A "\t\t[-m mediasize] name ...\n", 730SN/A getprogname()); 740SN/A fprintf(stderr, 750SN/A " %s role [-d] [-c config] <init | primary | secondary> all | name ...\n", 760SN/A getprogname()); 770SN/A fprintf(stderr, 780SN/A " %s status [-d] [-c config] [all | name ...]\n", 790SN/A getprogname()); 800SN/A fprintf(stderr, 810SN/A " %s dump [-d] [-c config] [all | name ...]\n", 820SN/A getprogname()); 830SN/A exit(EX_USAGE); 840SN/A} 850SN/A 860SN/Astatic int 870SN/Acreate_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, 880SN/A intmax_t keepdirty) 890SN/A{ 900SN/A unsigned char *buf; 910SN/A size_t mapsize; 920SN/A int ec; 930SN/A 940SN/A ec = 0; 950SN/A pjdlog_prefix_set("[%s] ", res->hr_name); 960SN/A 970SN/A if (provinfo(res, true) == -1) { 98 ec = EX_NOINPUT; 99 goto end; 100 } 101 if (mediasize == 0) 102 mediasize = res->hr_local_mediasize; 103 else if (mediasize > res->hr_local_mediasize) { 104 pjdlog_error("Provided mediasize is larger than provider %s size.", 105 res->hr_localpath); 106 ec = EX_DATAERR; 107 goto end; 108 } 109 if (!powerof2(res->hr_local_sectorsize)) { 110 pjdlog_error("Sector size of provider %s is not power of 2 (%u).", 111 res->hr_localpath, res->hr_local_sectorsize); 112 ec = EX_DATAERR; 113 goto end; 114 } 115 if (extentsize == 0) 116 extentsize = HAST_EXTENTSIZE; 117 if (extentsize < res->hr_local_sectorsize) { 118 pjdlog_error("Extent size (%jd) is less than sector size (%u).", 119 (intmax_t)extentsize, res->hr_local_sectorsize); 120 ec = EX_DATAERR; 121 goto end; 122 } 123 if ((extentsize % res->hr_local_sectorsize) != 0) { 124 pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", 125 (intmax_t)extentsize, res->hr_local_sectorsize); 126 ec = EX_DATAERR; 127 goto end; 128 } 129 mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, 130 extentsize, res->hr_local_sectorsize); 131 if (keepdirty == 0) 132 keepdirty = HAST_KEEPDIRTY; 133 res->hr_datasize = mediasize - METADATA_SIZE - mapsize; 134 res->hr_extentsize = extentsize; 135 res->hr_keepdirty = keepdirty; 136 137 res->hr_localoff = METADATA_SIZE + mapsize; 138 139 if (metadata_write(res) == -1) { 140 ec = EX_IOERR; 141 goto end; 142 } 143 buf = calloc(1, mapsize); 144 if (buf == NULL) { 145 pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", 146 mapsize); 147 ec = EX_TEMPFAIL; 148 goto end; 149 } 150 if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != 151 (ssize_t)mapsize) { 152 pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", 153 res->hr_localpath); 154 free(buf); 155 ec = EX_IOERR; 156 goto end; 157 } 158 free(buf); 159end: 160 if (res->hr_localfd >= 0) 161 close(res->hr_localfd); 162 pjdlog_prefix_set("%s", ""); 163 return (ec); 164} 165 166static void 167control_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, 168 intmax_t keepdirty) 169{ 170 struct hast_resource *res; 171 int ec, ii, ret; 172 173 /* Initialize the given resources. */ 174 if (argc < 1) 175 usage(); 176 ec = 0; 177 for (ii = 0; ii < argc; ii++) { 178 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 179 if (strcmp(argv[ii], res->hr_name) == 0) 180 break; 181 } 182 if (res == NULL) { 183 pjdlog_error("Unknown resource %s.", argv[ii]); 184 if (ec == 0) 185 ec = EX_DATAERR; 186 continue; 187 } 188 ret = create_one(res, mediasize, extentsize, keepdirty); 189 if (ret != 0 && ec == 0) 190 ec = ret; 191 } 192 exit(ec); 193} 194 195static int 196dump_one(struct hast_resource *res) 197{ 198 int ret; 199 200 ret = metadata_read(res, false); 201 if (ret != 0) 202 return (ret); 203 204 printf("resource: %s\n", res->hr_name); 205 printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize, 206 (intmax_t)res->hr_datasize); 207 printf(" extentsize: %d (%NB)\n", res->hr_extentsize, 208 (intmax_t)res->hr_extentsize); 209 printf(" keepdirty: %d\n", res->hr_keepdirty); 210 printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 211 printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 212 printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 213 printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 214 printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 215 216 return (0); 217} 218 219static void 220control_dump(int argc, char *argv[]) 221{ 222 struct hast_resource *res; 223 int ec, ret; 224 225 /* Dump metadata of the given resource(s). */ 226 227 ec = 0; 228 if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 229 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 230 ret = dump_one(res); 231 if (ret != 0 && ec == 0) 232 ec = ret; 233 } 234 } else { 235 int ii; 236 237 for (ii = 0; ii < argc; ii++) { 238 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 239 if (strcmp(argv[ii], res->hr_name) == 0) 240 break; 241 } 242 if (res == NULL) { 243 pjdlog_error("Unknown resource %s.", argv[ii]); 244 if (ec == 0) 245 ec = EX_DATAERR; 246 continue; 247 } 248 ret = dump_one(res); 249 if (ret != 0 && ec == 0) 250 ec = ret; 251 } 252 } 253 exit(ec); 254} 255 256static int 257control_set_role(struct nv *nv, const char *newrole) 258{ 259 const char *res, *oldrole; 260 unsigned int ii; 261 int error, ret; 262 263 ret = 0; 264 265 for (ii = 0; ; ii++) { 266 res = nv_get_string(nv, "resource%u", ii); 267 if (res == NULL) 268 break; 269 pjdlog_prefix_set("[%s] ", res); 270 error = nv_get_int16(nv, "error%u", ii); 271 if (error != 0) { 272 if (ret == 0) 273 ret = error; 274 pjdlog_warning("Received error %d from hastd.", error); 275 continue; 276 } 277 oldrole = nv_get_string(nv, "role%u", ii); 278 if (strcmp(oldrole, newrole) == 0) 279 pjdlog_debug(2, "Role unchanged (%s).", oldrole); 280 else { 281 pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 282 newrole); 283 } 284 } 285 pjdlog_prefix_set("%s", ""); 286 return (ret); 287} 288 289static int 290control_status(struct nv *nv) 291{ 292 unsigned int ii; 293 const char *str; 294 int error, ret; 295 296 ret = 0; 297 298 for (ii = 0; ; ii++) { 299 str = nv_get_string(nv, "resource%u", ii); 300 if (str == NULL) 301 break; 302 printf("%s:\n", str); 303 error = nv_get_int16(nv, "error%u", ii); 304 if (error != 0) { 305 if (ret == 0) 306 ret = error; 307 printf(" error: %d\n", error); 308 continue; 309 } 310 printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 311 printf(" provname: %s\n", 312 nv_get_string(nv, "provname%u", ii)); 313 printf(" localpath: %s\n", 314 nv_get_string(nv, "localpath%u", ii)); 315 printf(" extentsize: %u (%NB)\n", 316 (unsigned int)nv_get_uint32(nv, "extentsize%u", ii), 317 (intmax_t)nv_get_uint32(nv, "extentsize%u", ii)); 318 printf(" keepdirty: %u\n", 319 (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 320 printf(" remoteaddr: %s\n", 321 nv_get_string(nv, "remoteaddr%u", ii)); 322 str = nv_get_string(nv, "sourceaddr%u", ii); 323 if (str != NULL) 324 printf(" sourceaddr: %s\n", str); 325 printf(" replication: %s\n", 326 nv_get_string(nv, "replication%u", ii)); 327 str = nv_get_string(nv, "status%u", ii); 328 if (str != NULL) 329 printf(" status: %s\n", str); 330 printf(" dirty: %ju (%NB)\n", 331 (uintmax_t)nv_get_uint64(nv, "dirty%u", ii), 332 (intmax_t)nv_get_uint64(nv, "dirty%u", ii)); 333 printf(" statistics:\n"); 334 printf(" reads: %ju\n", 335 (uintmax_t)nv_get_uint64(nv, "stat_read%u", ii)); 336 printf(" writes: %ju\n", 337 (uintmax_t)nv_get_uint64(nv, "stat_write%u", ii)); 338 printf(" deletes: %ju\n", 339 (uintmax_t)nv_get_uint64(nv, "stat_delete%u", ii)); 340 printf(" flushes: %ju\n", 341 (uintmax_t)nv_get_uint64(nv, "stat_flush%u", ii)); 342 printf(" activemap updates: %ju\n", 343 (uintmax_t)nv_get_uint64(nv, "stat_activemap_update%u", ii)); 344 printf(" local errors: " 345 "read: %ju, write: %ju, delete: %ju, flush: %ju\n", 346 (uintmax_t)nv_get_uint64(nv, "stat_read_error%u", ii), 347 (uintmax_t)nv_get_uint64(nv, "stat_write_error%u", ii), 348 (uintmax_t)nv_get_uint64(nv, "stat_delete_error%u", ii), 349 (uintmax_t)nv_get_uint64(nv, "stat_flush_error%u", ii)); 350 } 351 return (ret); 352} 353 354int 355main(int argc, char *argv[]) 356{ 357 struct nv *nv; 358 int64_t mediasize, extentsize, keepdirty; 359 int cmd, debug, error, ii; 360 const char *optstr; 361 362 debug = 0; 363 mediasize = extentsize = keepdirty = 0; 364 365 if (argc == 1) 366 usage(); 367 368 if (strcmp(argv[1], "create") == 0) { 369 cmd = CMD_CREATE; 370 optstr = "c:de:k:m:h"; 371 } else if (strcmp(argv[1], "role") == 0) { 372 cmd = CMD_ROLE; 373 optstr = "c:dh"; 374 } else if (strcmp(argv[1], "status") == 0) { 375 cmd = CMD_STATUS; 376 optstr = "c:dh"; 377 } else if (strcmp(argv[1], "dump") == 0) { 378 cmd = CMD_DUMP; 379 optstr = "c:dh"; 380 } else 381 usage(); 382 383 argc--; 384 argv++; 385 386 for (;;) { 387 int ch; 388 389 ch = getopt(argc, argv, optstr); 390 if (ch == -1) 391 break; 392 switch (ch) { 393 case 'c': 394 cfgpath = optarg; 395 break; 396 case 'd': 397 debug++; 398 break; 399 case 'e': 400 if (expand_number(optarg, &extentsize) == -1) 401 errx(EX_USAGE, "Invalid extentsize"); 402 break; 403 case 'k': 404 if (expand_number(optarg, &keepdirty) == -1) 405 errx(EX_USAGE, "Invalid keepdirty"); 406 break; 407 case 'm': 408 if (expand_number(optarg, &mediasize) == -1) 409 errx(EX_USAGE, "Invalid mediasize"); 410 break; 411 case 'h': 412 default: 413 usage(); 414 } 415 } 416 argc -= optind; 417 argv += optind; 418 419 switch (cmd) { 420 case CMD_CREATE: 421 case CMD_ROLE: 422 if (argc == 0) 423 usage(); 424 break; 425 } 426 427 pjdlog_init(PJDLOG_MODE_STD); 428 pjdlog_debug_set(debug); 429 430 cfg = yy_config_parse(cfgpath, true); 431 PJDLOG_ASSERT(cfg != NULL); 432 433 switch (cmd) { 434 case CMD_CREATE: 435 control_create(argc, argv, mediasize, extentsize, keepdirty); 436 /* NOTREACHED */ 437 PJDLOG_ABORT("What are we doing here?!"); 438 break; 439 case CMD_DUMP: 440 /* Dump metadata from local component of the given resource. */ 441 control_dump(argc, argv); 442 /* NOTREACHED */ 443 PJDLOG_ABORT("What are we doing here?!"); 444 break; 445 case CMD_ROLE: 446 /* Change role for the given resources. */ 447 if (argc < 2) 448 usage(); 449 nv = nv_alloc(); 450 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 451 if (strcmp(argv[0], "init") == 0) 452 nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 453 else if (strcmp(argv[0], "primary") == 0) 454 nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 455 else if (strcmp(argv[0], "secondary") == 0) 456 nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 457 else 458 usage(); 459 for (ii = 0; ii < argc - 1; ii++) 460 nv_add_string(nv, argv[ii + 1], "resource%d", ii); 461 break; 462 case CMD_STATUS: 463 /* Obtain status of the given resources. */ 464 nv = nv_alloc(); 465 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 466 if (argc == 0) 467 nv_add_string(nv, "all", "resource%d", 0); 468 else { 469 for (ii = 0; ii < argc; ii++) 470 nv_add_string(nv, argv[ii], "resource%d", ii); 471 } 472 break; 473 default: 474 PJDLOG_ABORT("Impossible command!"); 475 } 476 477 /* Setup control connection... */ 478 if (proto_client(NULL, cfg->hc_controladdr, &controlconn) == -1) { 479 pjdlog_exit(EX_OSERR, 480 "Unable to setup control connection to %s", 481 cfg->hc_controladdr); 482 } 483 /* ...and connect to hastd. */ 484 if (proto_connect(controlconn, HAST_TIMEOUT) == -1) { 485 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 486 cfg->hc_controladdr); 487 } 488 489 if (drop_privs(NULL) != 0) 490 exit(EX_CONFIG); 491 492 /* Send the command to the server... */ 493 if (hast_proto_send(NULL, controlconn, nv, NULL, 0) == -1) { 494 pjdlog_exit(EX_UNAVAILABLE, 495 "Unable to send command to hastd via %s", 496 cfg->hc_controladdr); 497 } 498 nv_free(nv); 499 /* ...and receive reply. */ 500 if (hast_proto_recv_hdr(controlconn, &nv) == -1) { 501 pjdlog_exit(EX_UNAVAILABLE, 502 "cannot receive reply from hastd via %s", 503 cfg->hc_controladdr); 504 } 505 506 error = nv_get_int16(nv, "error"); 507 if (error != 0) { 508 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 509 error); 510 } 511 nv_set_error(nv, 0); 512 513 switch (cmd) { 514 case CMD_ROLE: 515 error = control_set_role(nv, argv[0]); 516 break; 517 case CMD_STATUS: 518 error = control_status(nv); 519 break; 520 default: 521 PJDLOG_ABORT("Impossible command!"); 522 } 523 524 exit(error); 525} 526