hastctl.c revision 219373
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastctl/hastctl.c 219373 2011-03-07 10:42:28Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> 34204076Spjd#include <sys/disk.h> 35204076Spjd#include <sys/ioctl.h> 36204076Spjd#include <sys/stat.h> 37204076Spjd#include <sys/sysctl.h> 38204076Spjd 39204076Spjd#include <assert.h> 40204076Spjd#include <err.h> 41204076Spjd#include <errno.h> 42204076Spjd#include <fcntl.h> 43204076Spjd#include <inttypes.h> 44204076Spjd#include <limits.h> 45204076Spjd#include <signal.h> 46204076Spjd#include <stdio.h> 47204076Spjd#include <stdlib.h> 48204076Spjd#include <string.h> 49204076Spjd#include <sysexits.h> 50204076Spjd#include <unistd.h> 51204076Spjd 52204076Spjd#include <activemap.h> 53204076Spjd 54204076Spjd#include "hast.h" 55204076Spjd#include "hast_proto.h" 56204076Spjd#include "metadata.h" 57204076Spjd#include "nv.h" 58204076Spjd#include "pjdlog.h" 59204076Spjd#include "proto.h" 60204076Spjd#include "subr.h" 61204076Spjd 62204076Spjd/* Path to configuration file. */ 63204076Spjdstatic const char *cfgpath = HAST_CONFIG; 64204076Spjd/* Hastd configuration. */ 65204076Spjdstatic struct hastd_config *cfg; 66204076Spjd/* Control connection. */ 67204076Spjdstatic struct proto_conn *controlconn; 68204076Spjd 69204076Spjdenum { 70204076Spjd CMD_INVALID, 71204076Spjd CMD_CREATE, 72204076Spjd CMD_ROLE, 73204076Spjd CMD_STATUS, 74204076Spjd CMD_DUMP 75204076Spjd}; 76204076Spjd 77204076Spjdstatic __dead2 void 78204076Spjdusage(void) 79204076Spjd{ 80204076Spjd 81204076Spjd fprintf(stderr, 82204076Spjd "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n" 83204076Spjd "\t\t[-m mediasize] name ...\n", 84204076Spjd getprogname()); 85204076Spjd fprintf(stderr, 86204076Spjd " %s role [-d] [-c config] <init | primary | secondary> all | name ...\n", 87204076Spjd getprogname()); 88204076Spjd fprintf(stderr, 89204076Spjd " %s status [-d] [-c config] [all | name ...]\n", 90204076Spjd getprogname()); 91204076Spjd fprintf(stderr, 92204076Spjd " %s dump [-d] [-c config] [all | name ...]\n", 93204076Spjd getprogname()); 94204076Spjd exit(EX_USAGE); 95204076Spjd} 96204076Spjd 97204076Spjdstatic int 98204076Spjdcreate_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, 99204076Spjd intmax_t keepdirty) 100204076Spjd{ 101204076Spjd unsigned char *buf; 102204076Spjd size_t mapsize; 103204076Spjd int ec; 104204076Spjd 105204076Spjd ec = 0; 106204076Spjd pjdlog_prefix_set("[%s] ", res->hr_name); 107204076Spjd 108204076Spjd if (provinfo(res, true) < 0) { 109204076Spjd ec = EX_NOINPUT; 110204076Spjd goto end; 111204076Spjd } 112204076Spjd if (mediasize == 0) 113204076Spjd mediasize = res->hr_local_mediasize; 114204076Spjd else if (mediasize > res->hr_local_mediasize) { 115204076Spjd pjdlog_error("Provided mediasize is larger than provider %s size.", 116204076Spjd res->hr_localpath); 117204076Spjd ec = EX_DATAERR; 118204076Spjd goto end; 119204076Spjd } 120204076Spjd if (!powerof2(res->hr_local_sectorsize)) { 121204076Spjd pjdlog_error("Sector size of provider %s is not power of 2 (%u).", 122204076Spjd res->hr_localpath, res->hr_local_sectorsize); 123204076Spjd ec = EX_DATAERR; 124204076Spjd goto end; 125204076Spjd } 126204076Spjd if (extentsize == 0) 127204076Spjd extentsize = HAST_EXTENTSIZE; 128204076Spjd if (extentsize < res->hr_local_sectorsize) { 129204076Spjd pjdlog_error("Extent size (%jd) is less than sector size (%u).", 130204076Spjd (intmax_t)extentsize, res->hr_local_sectorsize); 131204076Spjd ec = EX_DATAERR; 132204076Spjd goto end; 133204076Spjd } 134204076Spjd if ((extentsize % res->hr_local_sectorsize) != 0) { 135204076Spjd pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", 136204076Spjd (intmax_t)extentsize, res->hr_local_sectorsize); 137204076Spjd ec = EX_DATAERR; 138204076Spjd goto end; 139204076Spjd } 140204076Spjd mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, 141204076Spjd extentsize, res->hr_local_sectorsize); 142204076Spjd if (keepdirty == 0) 143204076Spjd keepdirty = HAST_KEEPDIRTY; 144204076Spjd res->hr_datasize = mediasize - METADATA_SIZE - mapsize; 145204076Spjd res->hr_extentsize = extentsize; 146204076Spjd res->hr_keepdirty = keepdirty; 147204076Spjd 148204076Spjd res->hr_localoff = METADATA_SIZE + mapsize; 149204076Spjd 150204076Spjd if (metadata_write(res) < 0) { 151204076Spjd ec = EX_IOERR; 152204076Spjd goto end; 153204076Spjd } 154204076Spjd buf = calloc(1, mapsize); 155204076Spjd if (buf == NULL) { 156204076Spjd pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", 157204076Spjd mapsize); 158204076Spjd ec = EX_TEMPFAIL; 159204076Spjd goto end; 160204076Spjd } 161204076Spjd if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != 162204076Spjd (ssize_t)mapsize) { 163204076Spjd pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", 164204076Spjd res->hr_localpath); 165204076Spjd free(buf); 166204076Spjd ec = EX_IOERR; 167204076Spjd goto end; 168204076Spjd } 169204076Spjd free(buf); 170204076Spjdend: 171204076Spjd if (res->hr_localfd >= 0) 172204076Spjd close(res->hr_localfd); 173204076Spjd pjdlog_prefix_set("%s", ""); 174204076Spjd return (ec); 175204076Spjd} 176204076Spjd 177204076Spjdstatic void 178204076Spjdcontrol_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, 179204076Spjd intmax_t keepdirty) 180204076Spjd{ 181204076Spjd struct hast_resource *res; 182204076Spjd int ec, ii, ret; 183204076Spjd 184204076Spjd /* Initialize the given resources. */ 185204076Spjd if (argc < 1) 186204076Spjd usage(); 187204076Spjd ec = 0; 188204076Spjd for (ii = 0; ii < argc; ii++) { 189204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 190204076Spjd if (strcmp(argv[ii], res->hr_name) == 0) 191204076Spjd break; 192204076Spjd } 193204076Spjd if (res == NULL) { 194204076Spjd pjdlog_error("Unknown resource %s.", argv[ii]); 195204076Spjd if (ec == 0) 196204076Spjd ec = EX_DATAERR; 197204076Spjd continue; 198204076Spjd } 199204076Spjd ret = create_one(res, mediasize, extentsize, keepdirty); 200204076Spjd if (ret != 0 && ec == 0) 201204076Spjd ec = ret; 202204076Spjd } 203204076Spjd exit(ec); 204204076Spjd} 205204076Spjd 206204076Spjdstatic int 207204076Spjddump_one(struct hast_resource *res) 208204076Spjd{ 209204076Spjd int ret; 210204076Spjd 211204076Spjd ret = metadata_read(res, false); 212204076Spjd if (ret != 0) 213204076Spjd return (ret); 214204076Spjd 215204076Spjd printf("resource: %s\n", res->hr_name); 216219373Spjd printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize, 217219373Spjd (intmax_t)res->hr_datasize); 218219373Spjd printf(" extentsize: %d (%NB)\n", res->hr_extentsize, 219219373Spjd (intmax_t)res->hr_extentsize); 220204076Spjd printf(" keepdirty: %d\n", res->hr_keepdirty); 221204076Spjd printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 222204076Spjd printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 223204076Spjd printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 224204076Spjd printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 225204076Spjd printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 226204076Spjd 227204076Spjd return (0); 228204076Spjd} 229204076Spjd 230204076Spjdstatic void 231204076Spjdcontrol_dump(int argc, char *argv[]) 232204076Spjd{ 233204076Spjd struct hast_resource *res; 234204076Spjd int ec, ret; 235204076Spjd 236204076Spjd /* Dump metadata of the given resource(s). */ 237204076Spjd 238204076Spjd ec = 0; 239204076Spjd if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 240204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 241204076Spjd ret = dump_one(res); 242204076Spjd if (ret != 0 && ec == 0) 243204076Spjd ec = ret; 244204076Spjd } 245204076Spjd } else { 246204076Spjd int ii; 247204076Spjd 248204076Spjd for (ii = 0; ii < argc; ii++) { 249204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 250204076Spjd if (strcmp(argv[ii], res->hr_name) == 0) 251204076Spjd break; 252204076Spjd } 253204076Spjd if (res == NULL) { 254204076Spjd pjdlog_error("Unknown resource %s.", argv[ii]); 255204076Spjd if (ec == 0) 256204076Spjd ec = EX_DATAERR; 257204076Spjd continue; 258204076Spjd } 259204076Spjd ret = dump_one(res); 260204076Spjd if (ret != 0 && ec == 0) 261204076Spjd ec = ret; 262204076Spjd } 263204076Spjd } 264204076Spjd exit(ec); 265204076Spjd} 266204076Spjd 267204076Spjdstatic int 268204076Spjdcontrol_set_role(struct nv *nv, const char *newrole) 269204076Spjd{ 270204076Spjd const char *res, *oldrole; 271204076Spjd unsigned int ii; 272204076Spjd int error, ret; 273204076Spjd 274204076Spjd ret = 0; 275204076Spjd 276204076Spjd for (ii = 0; ; ii++) { 277204076Spjd res = nv_get_string(nv, "resource%u", ii); 278204076Spjd if (res == NULL) 279204076Spjd break; 280204076Spjd pjdlog_prefix_set("[%s] ", res); 281204076Spjd error = nv_get_int16(nv, "error%u", ii); 282204076Spjd if (error != 0) { 283204076Spjd if (ret == 0) 284204076Spjd ret = error; 285204076Spjd pjdlog_warning("Received error %d from hastd.", error); 286204076Spjd continue; 287204076Spjd } 288204076Spjd oldrole = nv_get_string(nv, "role%u", ii); 289204076Spjd if (strcmp(oldrole, newrole) == 0) 290204076Spjd pjdlog_debug(2, "Role unchanged (%s).", oldrole); 291204076Spjd else { 292204076Spjd pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 293204076Spjd newrole); 294204076Spjd } 295204076Spjd } 296204076Spjd pjdlog_prefix_set("%s", ""); 297204076Spjd return (ret); 298204076Spjd} 299204076Spjd 300204076Spjdstatic int 301204076Spjdcontrol_status(struct nv *nv) 302204076Spjd{ 303204076Spjd unsigned int ii; 304204076Spjd const char *str; 305204076Spjd int error, ret; 306204076Spjd 307204076Spjd ret = 0; 308204076Spjd 309204076Spjd for (ii = 0; ; ii++) { 310204076Spjd str = nv_get_string(nv, "resource%u", ii); 311204076Spjd if (str == NULL) 312204076Spjd break; 313204076Spjd printf("%s:\n", str); 314204076Spjd error = nv_get_int16(nv, "error%u", ii); 315204076Spjd if (error != 0) { 316204076Spjd if (ret == 0) 317204076Spjd ret = error; 318204076Spjd printf(" error: %d\n", error); 319204076Spjd continue; 320204076Spjd } 321204076Spjd printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 322204076Spjd printf(" provname: %s\n", 323204076Spjd nv_get_string(nv, "provname%u", ii)); 324204076Spjd printf(" localpath: %s\n", 325204076Spjd nv_get_string(nv, "localpath%u", ii)); 326219373Spjd printf(" extentsize: %u (%NB)\n", 327219373Spjd (unsigned int)nv_get_uint32(nv, "extentsize%u", ii), 328219373Spjd (intmax_t)nv_get_uint32(nv, "extentsize%u", ii)); 329204076Spjd printf(" keepdirty: %u\n", 330204076Spjd (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 331204076Spjd printf(" remoteaddr: %s\n", 332204076Spjd nv_get_string(nv, "remoteaddr%u", ii)); 333204076Spjd printf(" replication: %s\n", 334204076Spjd nv_get_string(nv, "replication%u", ii)); 335204076Spjd str = nv_get_string(nv, "status%u", ii); 336204076Spjd if (str != NULL) 337204076Spjd printf(" status: %s\n", str); 338219373Spjd printf(" dirty: %ju (%NB)\n", 339219373Spjd (uintmax_t)nv_get_uint64(nv, "dirty%u", ii), 340219373Spjd (intmax_t)nv_get_uint64(nv, "dirty%u", ii)); 341204076Spjd } 342204076Spjd return (ret); 343204076Spjd} 344204076Spjd 345204076Spjdstatic int 346204076Spjdnumfromstr(const char *str, intmax_t *nump) 347204076Spjd{ 348204076Spjd intmax_t num; 349204076Spjd char *suffix; 350204076Spjd int rerrno; 351204076Spjd 352204076Spjd rerrno = errno; 353204076Spjd errno = 0; 354204076Spjd num = strtoimax(str, &suffix, 0); 355204076Spjd if (errno == 0 && *suffix != '\0') 356204076Spjd errno = EINVAL; 357204076Spjd if (errno != 0) 358204076Spjd return (-1); 359204076Spjd *nump = num; 360204076Spjd errno = rerrno; 361204076Spjd return (0); 362204076Spjd} 363204076Spjd 364204076Spjdint 365204076Spjdmain(int argc, char *argv[]) 366204076Spjd{ 367204076Spjd struct nv *nv; 368204076Spjd intmax_t mediasize, extentsize, keepdirty; 369204076Spjd int cmd, debug, error, ii; 370204076Spjd const char *optstr; 371204076Spjd 372204076Spjd debug = 0; 373204076Spjd mediasize = extentsize = keepdirty = 0; 374204076Spjd 375204076Spjd if (argc == 1) 376204076Spjd usage(); 377204076Spjd 378204076Spjd if (strcmp(argv[1], "create") == 0) { 379204076Spjd cmd = CMD_CREATE; 380204076Spjd optstr = "c:de:k:m:h"; 381204076Spjd } else if (strcmp(argv[1], "role") == 0) { 382204076Spjd cmd = CMD_ROLE; 383204076Spjd optstr = "c:dh"; 384204076Spjd } else if (strcmp(argv[1], "status") == 0) { 385204076Spjd cmd = CMD_STATUS; 386204076Spjd optstr = "c:dh"; 387204076Spjd } else if (strcmp(argv[1], "dump") == 0) { 388204076Spjd cmd = CMD_DUMP; 389204076Spjd optstr = "c:dh"; 390204076Spjd } else 391204076Spjd usage(); 392204076Spjd 393204076Spjd argc--; 394204076Spjd argv++; 395204076Spjd 396204076Spjd for (;;) { 397204076Spjd int ch; 398204076Spjd 399204076Spjd ch = getopt(argc, argv, optstr); 400204076Spjd if (ch == -1) 401204076Spjd break; 402204076Spjd switch (ch) { 403204076Spjd case 'c': 404204076Spjd cfgpath = optarg; 405204076Spjd break; 406204076Spjd case 'd': 407204076Spjd debug++; 408204076Spjd break; 409204076Spjd case 'e': 410204076Spjd if (numfromstr(optarg, &extentsize) < 0) 411204076Spjd err(1, "Invalid extentsize"); 412204076Spjd break; 413204076Spjd case 'k': 414204076Spjd if (numfromstr(optarg, &keepdirty) < 0) 415204076Spjd err(1, "Invalid keepdirty"); 416204076Spjd break; 417204076Spjd case 'm': 418204076Spjd if (numfromstr(optarg, &mediasize) < 0) 419204076Spjd err(1, "Invalid mediasize"); 420204076Spjd break; 421204076Spjd case 'h': 422204076Spjd default: 423204076Spjd usage(); 424204076Spjd } 425204076Spjd } 426204076Spjd argc -= optind; 427204076Spjd argv += optind; 428204076Spjd 429204076Spjd switch (cmd) { 430204076Spjd case CMD_CREATE: 431204076Spjd case CMD_ROLE: 432204076Spjd if (argc == 0) 433204076Spjd usage(); 434204076Spjd break; 435204076Spjd } 436204076Spjd 437217965Spjd pjdlog_init(PJDLOG_MODE_STD); 438204076Spjd pjdlog_debug_set(debug); 439204076Spjd 440210909Sdougb cfg = yy_config_parse(cfgpath, true); 441204076Spjd assert(cfg != NULL); 442204076Spjd 443204076Spjd switch (cmd) { 444204076Spjd case CMD_CREATE: 445204076Spjd control_create(argc, argv, mediasize, extentsize, keepdirty); 446204076Spjd /* NOTREACHED */ 447204076Spjd assert(!"What are we doing here?!"); 448204076Spjd break; 449204076Spjd case CMD_DUMP: 450204076Spjd /* Dump metadata from local component of the given resource. */ 451204076Spjd control_dump(argc, argv); 452204076Spjd /* NOTREACHED */ 453204076Spjd assert(!"What are we doing here?!"); 454204076Spjd break; 455204076Spjd case CMD_ROLE: 456204076Spjd /* Change role for the given resources. */ 457204076Spjd if (argc < 2) 458204076Spjd usage(); 459204076Spjd nv = nv_alloc(); 460204076Spjd nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 461204076Spjd if (strcmp(argv[0], "init") == 0) 462204076Spjd nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 463204076Spjd else if (strcmp(argv[0], "primary") == 0) 464204076Spjd nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 465204076Spjd else if (strcmp(argv[0], "secondary") == 0) 466204076Spjd nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 467204076Spjd else 468204076Spjd usage(); 469204076Spjd for (ii = 0; ii < argc - 1; ii++) 470204076Spjd nv_add_string(nv, argv[ii + 1], "resource%d", ii); 471204076Spjd break; 472204076Spjd case CMD_STATUS: 473204076Spjd /* Obtain status of the given resources. */ 474204076Spjd nv = nv_alloc(); 475204076Spjd nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 476204076Spjd if (argc == 0) 477204076Spjd nv_add_string(nv, "all", "resource%d", 0); 478204076Spjd else { 479204076Spjd for (ii = 0; ii < argc; ii++) 480204076Spjd nv_add_string(nv, argv[ii], "resource%d", ii); 481204076Spjd } 482204076Spjd break; 483204076Spjd default: 484204076Spjd assert(!"Impossible role!"); 485204076Spjd } 486204076Spjd 487204076Spjd /* Setup control connection... */ 488204076Spjd if (proto_client(cfg->hc_controladdr, &controlconn) < 0) { 489204076Spjd pjdlog_exit(EX_OSERR, 490204076Spjd "Unable to setup control connection to %s", 491204076Spjd cfg->hc_controladdr); 492204076Spjd } 493204076Spjd /* ...and connect to hastd. */ 494218201Sbz if (proto_connect(controlconn, HAST_TIMEOUT) < 0) { 495204076Spjd pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 496204076Spjd cfg->hc_controladdr); 497204076Spjd } 498218215Spjd 499218215Spjd if (drop_privs() != 0) 500218215Spjd exit(EX_CONFIG); 501218215Spjd pjdlog_debug(1, "Privileges successfully dropped."); 502218215Spjd 503204076Spjd /* Send the command to the server... */ 504204076Spjd if (hast_proto_send(NULL, controlconn, nv, NULL, 0) < 0) { 505204076Spjd pjdlog_exit(EX_UNAVAILABLE, 506204076Spjd "Unable to send command to hastd via %s", 507204076Spjd cfg->hc_controladdr); 508204076Spjd } 509204076Spjd nv_free(nv); 510204076Spjd /* ...and receive reply. */ 511204076Spjd if (hast_proto_recv(NULL, controlconn, &nv, NULL, 0) < 0) { 512204076Spjd pjdlog_exit(EX_UNAVAILABLE, 513204076Spjd "cannot receive reply from hastd via %s", 514204076Spjd cfg->hc_controladdr); 515204076Spjd } 516204076Spjd 517204076Spjd error = nv_get_int16(nv, "error"); 518204076Spjd if (error != 0) { 519204076Spjd pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 520204076Spjd error); 521204076Spjd } 522204076Spjd nv_set_error(nv, 0); 523204076Spjd 524204076Spjd switch (cmd) { 525204076Spjd case CMD_ROLE: 526204076Spjd error = control_set_role(nv, argv[0]); 527204076Spjd break; 528204076Spjd case CMD_STATUS: 529204076Spjd error = control_status(nv); 530204076Spjd break; 531204076Spjd default: 532204076Spjd assert(!"Impossible role!"); 533204076Spjd } 534204076Spjd 535204076Spjd exit(error); 536204076Spjd} 537