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