hastctl.c revision 217965
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 217965 2011-01-27 19:24:07Z 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); 216204076Spjd printf(" datasize: %ju\n", (uintmax_t)res->hr_datasize); 217204076Spjd printf(" extentsize: %d\n", res->hr_extentsize); 218204076Spjd printf(" keepdirty: %d\n", res->hr_keepdirty); 219204076Spjd printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); 220204076Spjd printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); 221204076Spjd printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); 222204076Spjd printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); 223204076Spjd printf(" prevrole: %s\n", role2str(res->hr_previous_role)); 224204076Spjd 225204076Spjd return (0); 226204076Spjd} 227204076Spjd 228204076Spjdstatic void 229204076Spjdcontrol_dump(int argc, char *argv[]) 230204076Spjd{ 231204076Spjd struct hast_resource *res; 232204076Spjd int ec, ret; 233204076Spjd 234204076Spjd /* Dump metadata of the given resource(s). */ 235204076Spjd 236204076Spjd ec = 0; 237204076Spjd if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { 238204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 239204076Spjd ret = dump_one(res); 240204076Spjd if (ret != 0 && ec == 0) 241204076Spjd ec = ret; 242204076Spjd } 243204076Spjd } else { 244204076Spjd int ii; 245204076Spjd 246204076Spjd for (ii = 0; ii < argc; ii++) { 247204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 248204076Spjd if (strcmp(argv[ii], res->hr_name) == 0) 249204076Spjd break; 250204076Spjd } 251204076Spjd if (res == NULL) { 252204076Spjd pjdlog_error("Unknown resource %s.", argv[ii]); 253204076Spjd if (ec == 0) 254204076Spjd ec = EX_DATAERR; 255204076Spjd continue; 256204076Spjd } 257204076Spjd ret = dump_one(res); 258204076Spjd if (ret != 0 && ec == 0) 259204076Spjd ec = ret; 260204076Spjd } 261204076Spjd } 262204076Spjd exit(ec); 263204076Spjd} 264204076Spjd 265204076Spjdstatic int 266204076Spjdcontrol_set_role(struct nv *nv, const char *newrole) 267204076Spjd{ 268204076Spjd const char *res, *oldrole; 269204076Spjd unsigned int ii; 270204076Spjd int error, ret; 271204076Spjd 272204076Spjd ret = 0; 273204076Spjd 274204076Spjd for (ii = 0; ; ii++) { 275204076Spjd res = nv_get_string(nv, "resource%u", ii); 276204076Spjd if (res == NULL) 277204076Spjd break; 278204076Spjd pjdlog_prefix_set("[%s] ", res); 279204076Spjd error = nv_get_int16(nv, "error%u", ii); 280204076Spjd if (error != 0) { 281204076Spjd if (ret == 0) 282204076Spjd ret = error; 283204076Spjd pjdlog_warning("Received error %d from hastd.", error); 284204076Spjd continue; 285204076Spjd } 286204076Spjd oldrole = nv_get_string(nv, "role%u", ii); 287204076Spjd if (strcmp(oldrole, newrole) == 0) 288204076Spjd pjdlog_debug(2, "Role unchanged (%s).", oldrole); 289204076Spjd else { 290204076Spjd pjdlog_debug(1, "Role changed from %s to %s.", oldrole, 291204076Spjd newrole); 292204076Spjd } 293204076Spjd } 294204076Spjd pjdlog_prefix_set("%s", ""); 295204076Spjd return (ret); 296204076Spjd} 297204076Spjd 298204076Spjdstatic int 299204076Spjdcontrol_status(struct nv *nv) 300204076Spjd{ 301204076Spjd unsigned int ii; 302204076Spjd const char *str; 303204076Spjd int error, ret; 304204076Spjd 305204076Spjd ret = 0; 306204076Spjd 307204076Spjd for (ii = 0; ; ii++) { 308204076Spjd str = nv_get_string(nv, "resource%u", ii); 309204076Spjd if (str == NULL) 310204076Spjd break; 311204076Spjd printf("%s:\n", str); 312204076Spjd error = nv_get_int16(nv, "error%u", ii); 313204076Spjd if (error != 0) { 314204076Spjd if (ret == 0) 315204076Spjd ret = error; 316204076Spjd printf(" error: %d\n", error); 317204076Spjd continue; 318204076Spjd } 319204076Spjd printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); 320204076Spjd printf(" provname: %s\n", 321204076Spjd nv_get_string(nv, "provname%u", ii)); 322204076Spjd printf(" localpath: %s\n", 323204076Spjd nv_get_string(nv, "localpath%u", ii)); 324204076Spjd printf(" extentsize: %u\n", 325204076Spjd (unsigned int)nv_get_uint32(nv, "extentsize%u", ii)); 326204076Spjd printf(" keepdirty: %u\n", 327204076Spjd (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); 328204076Spjd printf(" remoteaddr: %s\n", 329204076Spjd nv_get_string(nv, "remoteaddr%u", ii)); 330204076Spjd printf(" replication: %s\n", 331204076Spjd nv_get_string(nv, "replication%u", ii)); 332204076Spjd str = nv_get_string(nv, "status%u", ii); 333204076Spjd if (str != NULL) 334204076Spjd printf(" status: %s\n", str); 335204076Spjd printf(" dirty: %ju bytes\n", 336204076Spjd (uintmax_t)nv_get_uint64(nv, "dirty%u", ii)); 337204076Spjd } 338204076Spjd return (ret); 339204076Spjd} 340204076Spjd 341204076Spjdstatic int 342204076Spjdnumfromstr(const char *str, intmax_t *nump) 343204076Spjd{ 344204076Spjd intmax_t num; 345204076Spjd char *suffix; 346204076Spjd int rerrno; 347204076Spjd 348204076Spjd rerrno = errno; 349204076Spjd errno = 0; 350204076Spjd num = strtoimax(str, &suffix, 0); 351204076Spjd if (errno == 0 && *suffix != '\0') 352204076Spjd errno = EINVAL; 353204076Spjd if (errno != 0) 354204076Spjd return (-1); 355204076Spjd *nump = num; 356204076Spjd errno = rerrno; 357204076Spjd return (0); 358204076Spjd} 359204076Spjd 360204076Spjdint 361204076Spjdmain(int argc, char *argv[]) 362204076Spjd{ 363204076Spjd struct nv *nv; 364204076Spjd intmax_t mediasize, extentsize, keepdirty; 365204076Spjd int cmd, debug, error, ii; 366204076Spjd const char *optstr; 367204076Spjd 368204076Spjd debug = 0; 369204076Spjd mediasize = extentsize = keepdirty = 0; 370204076Spjd 371204076Spjd if (argc == 1) 372204076Spjd usage(); 373204076Spjd 374204076Spjd if (strcmp(argv[1], "create") == 0) { 375204076Spjd cmd = CMD_CREATE; 376204076Spjd optstr = "c:de:k:m:h"; 377204076Spjd } else if (strcmp(argv[1], "role") == 0) { 378204076Spjd cmd = CMD_ROLE; 379204076Spjd optstr = "c:dh"; 380204076Spjd } else if (strcmp(argv[1], "status") == 0) { 381204076Spjd cmd = CMD_STATUS; 382204076Spjd optstr = "c:dh"; 383204076Spjd } else if (strcmp(argv[1], "dump") == 0) { 384204076Spjd cmd = CMD_DUMP; 385204076Spjd optstr = "c:dh"; 386204076Spjd } else 387204076Spjd usage(); 388204076Spjd 389204076Spjd argc--; 390204076Spjd argv++; 391204076Spjd 392204076Spjd for (;;) { 393204076Spjd int ch; 394204076Spjd 395204076Spjd ch = getopt(argc, argv, optstr); 396204076Spjd if (ch == -1) 397204076Spjd break; 398204076Spjd switch (ch) { 399204076Spjd case 'c': 400204076Spjd cfgpath = optarg; 401204076Spjd break; 402204076Spjd case 'd': 403204076Spjd debug++; 404204076Spjd break; 405204076Spjd case 'e': 406204076Spjd if (numfromstr(optarg, &extentsize) < 0) 407204076Spjd err(1, "Invalid extentsize"); 408204076Spjd break; 409204076Spjd case 'k': 410204076Spjd if (numfromstr(optarg, &keepdirty) < 0) 411204076Spjd err(1, "Invalid keepdirty"); 412204076Spjd break; 413204076Spjd case 'm': 414204076Spjd if (numfromstr(optarg, &mediasize) < 0) 415204076Spjd err(1, "Invalid mediasize"); 416204076Spjd break; 417204076Spjd case 'h': 418204076Spjd default: 419204076Spjd usage(); 420204076Spjd } 421204076Spjd } 422204076Spjd argc -= optind; 423204076Spjd argv += optind; 424204076Spjd 425204076Spjd switch (cmd) { 426204076Spjd case CMD_CREATE: 427204076Spjd case CMD_ROLE: 428204076Spjd if (argc == 0) 429204076Spjd usage(); 430204076Spjd break; 431204076Spjd } 432204076Spjd 433217965Spjd pjdlog_init(PJDLOG_MODE_STD); 434204076Spjd pjdlog_debug_set(debug); 435204076Spjd 436210909Sdougb cfg = yy_config_parse(cfgpath, true); 437204076Spjd assert(cfg != NULL); 438204076Spjd 439204076Spjd switch (cmd) { 440204076Spjd case CMD_CREATE: 441204076Spjd control_create(argc, argv, mediasize, extentsize, keepdirty); 442204076Spjd /* NOTREACHED */ 443204076Spjd assert(!"What are we doing here?!"); 444204076Spjd break; 445204076Spjd case CMD_DUMP: 446204076Spjd /* Dump metadata from local component of the given resource. */ 447204076Spjd control_dump(argc, argv); 448204076Spjd /* NOTREACHED */ 449204076Spjd assert(!"What are we doing here?!"); 450204076Spjd break; 451204076Spjd case CMD_ROLE: 452204076Spjd /* Change role for the given resources. */ 453204076Spjd if (argc < 2) 454204076Spjd usage(); 455204076Spjd nv = nv_alloc(); 456204076Spjd nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); 457204076Spjd if (strcmp(argv[0], "init") == 0) 458204076Spjd nv_add_uint8(nv, HAST_ROLE_INIT, "role"); 459204076Spjd else if (strcmp(argv[0], "primary") == 0) 460204076Spjd nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); 461204076Spjd else if (strcmp(argv[0], "secondary") == 0) 462204076Spjd nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); 463204076Spjd else 464204076Spjd usage(); 465204076Spjd for (ii = 0; ii < argc - 1; ii++) 466204076Spjd nv_add_string(nv, argv[ii + 1], "resource%d", ii); 467204076Spjd break; 468204076Spjd case CMD_STATUS: 469204076Spjd /* Obtain status of the given resources. */ 470204076Spjd nv = nv_alloc(); 471204076Spjd nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); 472204076Spjd if (argc == 0) 473204076Spjd nv_add_string(nv, "all", "resource%d", 0); 474204076Spjd else { 475204076Spjd for (ii = 0; ii < argc; ii++) 476204076Spjd nv_add_string(nv, argv[ii], "resource%d", ii); 477204076Spjd } 478204076Spjd break; 479204076Spjd default: 480204076Spjd assert(!"Impossible role!"); 481204076Spjd } 482204076Spjd 483204076Spjd /* Setup control connection... */ 484204076Spjd if (proto_client(cfg->hc_controladdr, &controlconn) < 0) { 485204076Spjd pjdlog_exit(EX_OSERR, 486204076Spjd "Unable to setup control connection to %s", 487204076Spjd cfg->hc_controladdr); 488204076Spjd } 489204076Spjd /* ...and connect to hastd. */ 490204076Spjd if (proto_connect(controlconn) < 0) { 491204076Spjd pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", 492204076Spjd cfg->hc_controladdr); 493204076Spjd } 494204076Spjd /* Send the command to the server... */ 495204076Spjd if (hast_proto_send(NULL, controlconn, nv, NULL, 0) < 0) { 496204076Spjd pjdlog_exit(EX_UNAVAILABLE, 497204076Spjd "Unable to send command to hastd via %s", 498204076Spjd cfg->hc_controladdr); 499204076Spjd } 500204076Spjd nv_free(nv); 501204076Spjd /* ...and receive reply. */ 502204076Spjd if (hast_proto_recv(NULL, controlconn, &nv, NULL, 0) < 0) { 503204076Spjd pjdlog_exit(EX_UNAVAILABLE, 504204076Spjd "cannot receive reply from hastd via %s", 505204076Spjd cfg->hc_controladdr); 506204076Spjd } 507204076Spjd 508204076Spjd error = nv_get_int16(nv, "error"); 509204076Spjd if (error != 0) { 510204076Spjd pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", 511204076Spjd error); 512204076Spjd } 513204076Spjd nv_set_error(nv, 0); 514204076Spjd 515204076Spjd switch (cmd) { 516204076Spjd case CMD_ROLE: 517204076Spjd error = control_set_role(nv, argv[0]); 518204076Spjd break; 519204076Spjd case CMD_STATUS: 520204076Spjd error = control_status(nv); 521204076Spjd break; 522204076Spjd default: 523204076Spjd assert(!"Impossible role!"); 524204076Spjd } 525204076Spjd 526204076Spjd exit(error); 527204076Spjd} 528