hastctl.c revision 210909
1121965Sphk/*- 2121965Sphk * Copyright (c) 2009-2010 The FreeBSD Foundation 3121965Sphk * All rights reserved. 4121965Sphk * 5121965Sphk * This software was developed by Pawel Jakub Dawidek under sponsorship from 6121965Sphk * the FreeBSD Foundation. 7121965Sphk * 8121965Sphk * Redistribution and use in source and binary forms, with or without 9121965Sphk * modification, are permitted provided that the following conditions 10121965Sphk * are met: 11121965Sphk * 1. Redistributions of source code must retain the above copyright 12121965Sphk * notice, this list of conditions and the following disclaimer. 13121965Sphk * 2. Redistributions in binary form must reproduce the above copyright 14121965Sphk * notice, this list of conditions and the following disclaimer in the 15121965Sphk * documentation and/or other materials provided with the distribution. 16121965Sphk * 17121965Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18121965Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19121965Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20121965Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21121965Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22121965Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23121965Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24121965Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25121965Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26121965Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27169001Sphk * SUCH DAMAGE. 28121965Sphk */ 29121965Sphk 30121965Sphk#include <sys/cdefs.h> 31121965Sphk__FBSDID("$FreeBSD: head/sbin/hastctl/hastctl.c 210909 2010-08-06 05:20:21Z dougb $"); 32131348Sru 33121965Sphk#include <sys/param.h> 34121965Sphk#include <sys/disk.h> 35131348Sru#include <sys/ioctl.h> 36131348Sru#include <sys/stat.h> 37146786Ssimon#include <sys/sysctl.h> 38169000Sphk 39169000Sphk#include <assert.h> 40131348Sru#include <err.h> 41121965Sphk#include <errno.h> 42146786Ssimon#include <fcntl.h> 43121965Sphk#include <inttypes.h> 44121965Sphk#include <limits.h> 45121965Sphk#include <signal.h> 46121965Sphk#include <stdio.h> 47130857Smpp#include <stdlib.h> 48121965Sphk#include <string.h> 49130857Smpp#include <sysexits.h> 50121965Sphk#include <unistd.h> 51131348Sru 52131348Sru#include <activemap.h> 53131348Sru 54121965Sphk#include "hast.h" 55131348Sru#include "hast_proto.h" 56121965Sphk#include "metadata.h" 57121965Sphk#include "nv.h" 58121965Sphk#include "pjdlog.h" 59131348Sru#include "proto.h" 60131348Sru#include "subr.h" 61131348Sru 62131348Sru/* Path to configuration file. */ 63131348Srustatic const char *cfgpath = HAST_CONFIG; 64121965Sphk/* Hastd configuration. */ 65131348Srustatic struct hastd_config *cfg; 66130857Smpp/* Control connection. */ 67131348Srustatic struct proto_conn *controlconn; 68131348Sru 69121965Sphkenum { 70130857Smpp CMD_INVALID, 71121965Sphk CMD_CREATE, 72130857Smpp CMD_ROLE, 73121965Sphk CMD_STATUS, 74121965Sphk CMD_DUMP 75131348Sru}; 76130857Smpp 77121965Sphkstatic __dead2 void 78121965Sphkusage(void) 79121965Sphk{ 80131348Sru 81131348Sru fprintf(stderr, 82121965Sphk "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n" 83130857Smpp "\t\t[-m mediasize] name ...\n", 84121965Sphk getprogname()); 85130857Smpp fprintf(stderr, 86121965Sphk " %s role [-d] [-c config] <init | primary | secondary> all | name ...\n", 87121965Sphk getprogname()); 88131348Sru fprintf(stderr, 89131348Sru " %s status [-d] [-c config] [all | name ...]\n", 90131348Sru getprogname()); 91131348Sru fprintf(stderr, 92121965Sphk " %s dump [-d] [-c config] [all | name ...]\n", 93121965Sphk getprogname()); 94121965Sphk exit(EX_USAGE); 95121965Sphk} 96121965Sphk 97121965Sphkstatic int 98121965Sphkcreate_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, 99121965Sphk intmax_t keepdirty) 100131348Sru{ 101121965Sphk unsigned char *buf; 102121965Sphk size_t mapsize; 103121965Sphk int ec; 104121965Sphk 105121965Sphk ec = 0; 106131348Sru pjdlog_prefix_set("[%s] ", res->hr_name); 107121965Sphk 108131348Sru if (provinfo(res, true) < 0) { 109131348Sru ec = EX_NOINPUT; 110131348Sru goto end; 111121965Sphk } 112121965Sphk if (mediasize == 0) 113131348Sru mediasize = res->hr_local_mediasize; 114130857Smpp else if (mediasize > res->hr_local_mediasize) { 115131348Sru pjdlog_error("Provided mediasize is larger than provider %s size.", 116131348Sru res->hr_localpath); 117131348Sru ec = EX_DATAERR; 118131348Sru goto end; 119131348Sru } 120121965Sphk if (!powerof2(res->hr_local_sectorsize)) { 121131348Sru pjdlog_error("Sector size of provider %s is not power of 2 (%u).", 122131348Sru res->hr_localpath, res->hr_local_sectorsize); 123131348Sru ec = EX_DATAERR; 124131348Sru goto end; 125131348Sru } 126128679Sphk if (extentsize == 0) 127140967Sphk extentsize = HAST_EXTENTSIZE; 128140967Sphk if (extentsize < res->hr_local_sectorsize) { 129140967Sphk pjdlog_error("Extent size (%jd) is less than sector size (%u).", 130140967Sphk (intmax_t)extentsize, res->hr_local_sectorsize); 131141057Sceri ec = EX_DATAERR; 132140967Sphk goto end; 133131348Sru } 134131348Sru if ((extentsize % res->hr_local_sectorsize) != 0) { 135131348Sru pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", 136121965Sphk (intmax_t)extentsize, res->hr_local_sectorsize); 137131348Sru ec = EX_DATAERR; 138131348Sru goto end; 139131348Sru } 140131348Sru mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, 141131348Sru extentsize, res->hr_local_sectorsize); 142131348Sru if (keepdirty == 0) 143131348Sru keepdirty = HAST_KEEPDIRTY; 144131348Sru res->hr_datasize = mediasize - METADATA_SIZE - mapsize; 145131348Sru res->hr_extentsize = extentsize; 146131348Sru res->hr_keepdirty = keepdirty; 147131348Sru 148131348Sru res->hr_localoff = METADATA_SIZE + mapsize; 149131348Sru 150131348Sru if (metadata_write(res) < 0) { 151131348Sru ec = EX_IOERR; 152121965Sphk goto end; 153131348Sru } 154121965Sphk buf = calloc(1, mapsize); 155121965Sphk if (buf == NULL) { 156140561Sru pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", 157140561Sru mapsize); 158140561Sru ec = EX_TEMPFAIL; 159140561Sru goto end; 160121965Sphk } 161131348Sru if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != 162131348Sru (ssize_t)mapsize) { 163131348Sru pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", 164121965Sphk res->hr_localpath); 165131348Sru free(buf); 166121965Sphk ec = EX_IOERR; 167131348Sru goto end; 168131348Sru } 169131348Sru free(buf); 170131348Sruend: 171148227Sphk if (res->hr_localfd >= 0) 172131348Sru close(res->hr_localfd); 173278616Scperciva pjdlog_prefix_set("%s", ""); 174131348Sru return (ec); 175121965Sphk} 176121965Sphk 177121965Sphkstatic void 178121965Sphkcontrol_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, 179121965Sphk intmax_t keepdirty) 180121965Sphk{ 181121965Sphk struct hast_resource *res; 182121965Sphk int ec, ii, ret; 183121965Sphk 184121965Sphk /* Initialize the given resources. */ 185267938Sbapt if (argc < 1) 186121965Sphk usage(); 187121965Sphk ec = 0; 188267938Sbapt for (ii = 0; ii < argc; ii++) { 189121965Sphk TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 190267938Sbapt if (strcmp(argv[ii], res->hr_name) == 0) 191 break; 192 } 193 if (res == NULL) { 194 pjdlog_error("Unknown resource %s.", argv[ii]); 195 if (ec == 0) 196 ec = EX_DATAERR; 197 continue; 198 } 199 ret = create_one(res, mediasize, extentsize, keepdirty); 200 if (ret != 0 && ec == 0) 201 ec = ret; 202 } 203 exit(ec); 204} 205 206static int 207dump_one(struct hast_resource *res) 208{ 209 int ret; 210 211 ret = metadata_read(res, false); 212 if (ret != 0) 213 return (ret); 214 215 printf("resource: %s\n", res->hr_name); 216 printf(" datasize: %ju\n", (uintmax_t)res->hr_datasize); 217 printf(" extentsize: %d\n", res->hr_extentsize); 218 printf(" keepdirty: %d\n", res->hr_keepdirty); 219 printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 220 printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 221 printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 222 printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 223 printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 224 225 return (0); 226} 227 228static void 229control_dump(int argc, char *argv[]) 230{ 231 struct hast_resource *res; 232 int ec, ret; 233 234 /* Dump metadata of the given resource(s). */ 235 236 ec = 0; 237 if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 238 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 239 ret = dump_one(res); 240 if (ret != 0 && ec == 0) 241 ec = ret; 242 } 243 } else { 244 int ii; 245 246 for (ii = 0; ii < argc; ii++) { 247 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 248 if (strcmp(argv[ii], res->hr_name) == 0) 249 break; 250 } 251 if (res == NULL) { 252 pjdlog_error("Unknown resource %s.", argv[ii]); 253 if (ec == 0) 254 ec = EX_DATAERR; 255 continue; 256 } 257 ret = dump_one(res); 258 if (ret != 0 && ec == 0) 259 ec = ret; 260 } 261 } 262 exit(ec); 263} 264 265static int 266control_set_role(struct nv *nv, const char *newrole) 267{ 268 const char *res, *oldrole; 269 unsigned int ii; 270 int error, ret; 271 272 ret = 0; 273 274 for (ii = 0; ; ii++) { 275 res = nv_get_string(nv, "resource%u", ii); 276 if (res == NULL) 277 break; 278 pjdlog_prefix_set("[%s] ", res); 279 error = nv_get_int16(nv, "error%u", ii); 280 if (error != 0) { 281 if (ret == 0) 282 ret = error; 283 pjdlog_warning("Received error %d from hastd.", error); 284 continue; 285 } 286 oldrole = nv_get_string(nv, "role%u", ii); 287 if (strcmp(oldrole, newrole) == 0) 288 pjdlog_debug(2, "Role unchanged (%s).", oldrole); 289 else { 290 pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 291 newrole); 292 } 293 } 294 pjdlog_prefix_set("%s", ""); 295 return (ret); 296} 297 298static int 299control_status(struct nv *nv) 300{ 301 unsigned int ii; 302 const char *str; 303 int error, ret; 304 305 ret = 0; 306 307 for (ii = 0; ; ii++) { 308 str = nv_get_string(nv, "resource%u", ii); 309 if (str == NULL) 310 break; 311 printf("%s:\n", str); 312 error = nv_get_int16(nv, "error%u", ii); 313 if (error != 0) { 314 if (ret == 0) 315 ret = error; 316 printf(" error: %d\n", error); 317 continue; 318 } 319 printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 320 printf(" provname: %s\n", 321 nv_get_string(nv, "provname%u", ii)); 322 printf(" localpath: %s\n", 323 nv_get_string(nv, "localpath%u", ii)); 324 printf(" extentsize: %u\n", 325 (unsigned int)nv_get_uint32(nv, "extentsize%u", ii)); 326 printf(" keepdirty: %u\n", 327 (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 328 printf(" remoteaddr: %s\n", 329 nv_get_string(nv, "remoteaddr%u", ii)); 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 printf(" dirty: %ju bytes\n", 336 (uintmax_t)nv_get_uint64(nv, "dirty%u", ii)); 337 } 338 return (ret); 339} 340 341static int 342numfromstr(const char *str, intmax_t *nump) 343{ 344 intmax_t num; 345 char *suffix; 346 int rerrno; 347 348 rerrno = errno; 349 errno = 0; 350 num = strtoimax(str, &suffix, 0); 351 if (errno == 0 && *suffix != '\0') 352 errno = EINVAL; 353 if (errno != 0) 354 return (-1); 355 *nump = num; 356 errno = rerrno; 357 return (0); 358} 359 360int 361main(int argc, char *argv[]) 362{ 363 struct nv *nv; 364 intmax_t mediasize, extentsize, keepdirty; 365 int cmd, debug, error, ii; 366 const char *optstr; 367 368 debug = 0; 369 mediasize = extentsize = keepdirty = 0; 370 371 if (argc == 1) 372 usage(); 373 374 if (strcmp(argv[1], "create") == 0) { 375 cmd = CMD_CREATE; 376 optstr = "c:de:k:m:h"; 377 } else if (strcmp(argv[1], "role") == 0) { 378 cmd = CMD_ROLE; 379 optstr = "c:dh"; 380 } else if (strcmp(argv[1], "status") == 0) { 381 cmd = CMD_STATUS; 382 optstr = "c:dh"; 383 } else if (strcmp(argv[1], "dump") == 0) { 384 cmd = CMD_DUMP; 385 optstr = "c:dh"; 386 } else 387 usage(); 388 389 argc--; 390 argv++; 391 392 for (;;) { 393 int ch; 394 395 ch = getopt(argc, argv, optstr); 396 if (ch == -1) 397 break; 398 switch (ch) { 399 case 'c': 400 cfgpath = optarg; 401 break; 402 case 'd': 403 debug++; 404 break; 405 case 'e': 406 if (numfromstr(optarg, &extentsize) < 0) 407 err(1, "Invalid extentsize"); 408 break; 409 case 'k': 410 if (numfromstr(optarg, &keepdirty) < 0) 411 err(1, "Invalid keepdirty"); 412 break; 413 case 'm': 414 if (numfromstr(optarg, &mediasize) < 0) 415 err(1, "Invalid mediasize"); 416 break; 417 case 'h': 418 default: 419 usage(); 420 } 421 } 422 argc -= optind; 423 argv += optind; 424 425 switch (cmd) { 426 case CMD_CREATE: 427 case CMD_ROLE: 428 if (argc == 0) 429 usage(); 430 break; 431 } 432 433 pjdlog_debug_set(debug); 434 435 cfg = yy_config_parse(cfgpath, true); 436 assert(cfg != NULL); 437 438 switch (cmd) { 439 case CMD_CREATE: 440 control_create(argc, argv, mediasize, extentsize, keepdirty); 441 /* NOTREACHED */ 442 assert(!"What are we doing here?!"); 443 break; 444 case CMD_DUMP: 445 /* Dump metadata from local component of the given resource. */ 446 control_dump(argc, argv); 447 /* NOTREACHED */ 448 assert(!"What are we doing here?!"); 449 break; 450 case CMD_ROLE: 451 /* Change role for the given resources. */ 452 if (argc < 2) 453 usage(); 454 nv = nv_alloc(); 455 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 456 if (strcmp(argv[0], "init") == 0) 457 nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 458 else if (strcmp(argv[0], "primary") == 0) 459 nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 460 else if (strcmp(argv[0], "secondary") == 0) 461 nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 462 else 463 usage(); 464 for (ii = 0; ii < argc - 1; ii++) 465 nv_add_string(nv, argv[ii + 1], "resource%d", ii); 466 break; 467 case CMD_STATUS: 468 /* Obtain status of the given resources. */ 469 nv = nv_alloc(); 470 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 471 if (argc == 0) 472 nv_add_string(nv, "all", "resource%d", 0); 473 else { 474 for (ii = 0; ii < argc; ii++) 475 nv_add_string(nv, argv[ii], "resource%d", ii); 476 } 477 break; 478 default: 479 assert(!"Impossible role!"); 480 } 481 482 /* Setup control connection... */ 483 if (proto_client(cfg->hc_controladdr, &controlconn) < 0) { 484 pjdlog_exit(EX_OSERR, 485 "Unable to setup control connection to %s", 486 cfg->hc_controladdr); 487 } 488 /* ...and connect to hastd. */ 489 if (proto_connect(controlconn) < 0) { 490 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 491 cfg->hc_controladdr); 492 } 493 /* Send the command to the server... */ 494 if (hast_proto_send(NULL, controlconn, nv, NULL, 0) < 0) { 495 pjdlog_exit(EX_UNAVAILABLE, 496 "Unable to send command to hastd via %s", 497 cfg->hc_controladdr); 498 } 499 nv_free(nv); 500 /* ...and receive reply. */ 501 if (hast_proto_recv(NULL, controlconn, &nv, NULL, 0) < 0) { 502 pjdlog_exit(EX_UNAVAILABLE, 503 "cannot receive reply from hastd via %s", 504 cfg->hc_controladdr); 505 } 506 507 error = nv_get_int16(nv, "error"); 508 if (error != 0) { 509 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 510 error); 511 } 512 nv_set_error(nv, 0); 513 514 switch (cmd) { 515 case CMD_ROLE: 516 error = control_set_role(nv, argv[0]); 517 break; 518 case CMD_STATUS: 519 error = control_status(nv); 520 break; 521 default: 522 assert(!"Impossible role!"); 523 } 524 525 exit(error); 526} 527