control.c revision 229945
1184610Salfred/*- 2184610Salfred * Copyright (c) 2009-2010 The FreeBSD Foundation 3184610Salfred * All rights reserved. 4184610Salfred * 5184610Salfred * This software was developed by Pawel Jakub Dawidek under sponsorship from 6184610Salfred * the FreeBSD Foundation. 7184610Salfred * 8184610Salfred * Redistribution and use in source and binary forms, with or without 9184610Salfred * modification, are permitted provided that the following conditions 10184610Salfred * are met: 11184610Salfred * 1. Redistributions of source code must retain the above copyright 12184610Salfred * notice, this list of conditions and the following disclaimer. 13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 14184610Salfred * notice, this list of conditions and the following disclaimer in the 15184610Salfred * documentation and/or other materials provided with the distribution. 16184610Salfred * 17184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27184610Salfred * SUCH DAMAGE. 28184610Salfred */ 29184610Salfred 30184610Salfred#include <sys/cdefs.h> 31184610Salfred__FBSDID("$FreeBSD: head/sbin/hastd/control.c 229945 2012-01-10 22:39:07Z pjd $"); 32184610Salfred 33184610Salfred#include <sys/types.h> 34184610Salfred#include <sys/wait.h> 35184610Salfred 36184610Salfred#include <errno.h> 37184610Salfred#include <pthread.h> 38184610Salfred#include <signal.h> 39184610Salfred#include <stdio.h> 40184610Salfred#include <string.h> 41184610Salfred#include <unistd.h> 42184610Salfred 43184610Salfred#include "hast.h" 44184610Salfred#include "hastd.h" 45184610Salfred#include "hast_checksum.h" 46184610Salfred#include "hast_compression.h" 47184610Salfred#include "hast_proto.h" 48194677Sthompsa#include "hooks.h" 49194677Sthompsa#include "nv.h" 50194677Sthompsa#include "pjdlog.h" 51194677Sthompsa#include "proto.h" 52194677Sthompsa#include "subr.h" 53194677Sthompsa 54194677Sthompsa#include "control.h" 55194677Sthompsa 56194677Sthompsavoid 57194677Sthompsachild_cleanup(struct hast_resource *res) 58194677Sthompsa{ 59194677Sthompsa 60194677Sthompsa proto_close(res->hr_ctrl); 61194677Sthompsa res->hr_ctrl = NULL; 62194677Sthompsa if (res->hr_event != NULL) { 63194677Sthompsa proto_close(res->hr_event); 64194677Sthompsa res->hr_event = NULL; 65194677Sthompsa } 66194677Sthompsa if (res->hr_conn != NULL) { 67194677Sthompsa proto_close(res->hr_conn); 68188746Sthompsa res->hr_conn = NULL; 69188942Sthompsa } 70194677Sthompsa res->hr_workerpid = 0; 71194677Sthompsa} 72184610Salfred 73184610Salfredstatic void 74188942Sthompsacontrol_set_role_common(struct hastd_config *cfg, struct nv *nvout, 75184610Salfred uint8_t role, struct hast_resource *res, const char *name, unsigned int no) 76188942Sthompsa{ 77184610Salfred int oldrole; 78184610Salfred 79184610Salfred /* Name is always needed. */ 80193640Sariff if (name != NULL) 81193640Sariff nv_add_string(nvout, name, "resource%u", no); 82193640Sariff 83193640Sariff if (res == NULL) { 84184610Salfred PJDLOG_ASSERT(cfg != NULL); 85188957Sthompsa PJDLOG_ASSERT(name != NULL); 86188957Sthompsa 87184610Salfred TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 88184610Salfred if (strcmp(res->hr_name, name) == 0) 89184610Salfred break; 90200825Sthompsa } 91186730Salfred if (res == NULL) { 92200825Sthompsa nv_add_int16(nvout, EHAST_NOENTRY, "error%u", no); 93186730Salfred return; 94207077Sthompsa } 95184610Salfred } 96184610Salfred PJDLOG_ASSERT(res != NULL); 97192505Sthompsa 98200825Sthompsa /* Send previous role back. */ 99192505Sthompsa nv_add_string(nvout, role2str(res->hr_role), "role%u", no); 100184610Salfred 101200825Sthompsa /* Nothing changed, return here. */ 102200825Sthompsa if (role == res->hr_role) 103192505Sthompsa return; 104186730Salfred 105200825Sthompsa pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 106200825Sthompsa pjdlog_info("Role changed to %s.", role2str(role)); 107192505Sthompsa 108186730Salfred /* Change role to the new one. */ 109200825Sthompsa oldrole = res->hr_role; 110200825Sthompsa res->hr_role = role; 111192505Sthompsa pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 112186730Salfred 113184610Salfred /* 114184610Salfred * If previous role was primary or secondary we have to kill process 115199060Sthompsa * doing that work. 116184610Salfred */ 117184610Salfred if (res->hr_workerpid != 0) { 118184610Salfred if (kill(res->hr_workerpid, SIGTERM) == -1) { 119184610Salfred pjdlog_errno(LOG_WARNING, 120184610Salfred "Unable to kill worker process %u", 121193465Sthompsa (unsigned int)res->hr_workerpid); 122184610Salfred } else if (waitpid(res->hr_workerpid, NULL, 0) != 123184610Salfred res->hr_workerpid) { 124184610Salfred pjdlog_errno(LOG_WARNING, 125184610Salfred "Error while waiting for worker process %u", 126184610Salfred (unsigned int)res->hr_workerpid); 127184610Salfred } else { 128184610Salfred pjdlog_debug(1, "Worker process %u stopped.", 129184610Salfred (unsigned int)res->hr_workerpid); 130184610Salfred } 131184610Salfred child_cleanup(res); 132184610Salfred } 133184610Salfred 134184610Salfred /* Start worker process if we are changing to primary. */ 135184610Salfred if (role == HAST_ROLE_PRIMARY) 136184610Salfred hastd_primary(res); 137184610Salfred pjdlog_prefix_set("%s", ""); 138184610Salfred hook_exec(res->hr_exec, "role", res->hr_name, role2str(oldrole), 139184610Salfred role2str(res->hr_role), NULL); 140184610Salfred} 141184610Salfred 142184610Salfredvoid 143184610Salfredcontrol_set_role(struct hast_resource *res, uint8_t role) 144184610Salfred{ 145184610Salfred 146184610Salfred control_set_role_common(NULL, NULL, role, res, NULL, 0); 147184610Salfred} 148184610Salfred 149184610Salfredstatic void 150184610Salfredcontrol_status_worker(struct hast_resource *res, struct nv *nvout, 151184610Salfred unsigned int no) 152184610Salfred{ 153184610Salfred struct nv *cnvin, *cnvout; 154184610Salfred const char *str; 155184610Salfred int error; 156184610Salfred 157184610Salfred cnvin = NULL; 158203678Sbrucec 159184610Salfred /* 160184610Salfred * Prepare and send command to worker process. 161184610Salfred */ 162192984Sthompsa cnvout = nv_alloc(); 163203678Sbrucec nv_add_uint8(cnvout, CONTROL_STATUS, "cmd"); 164203678Sbrucec error = nv_error(cnvout); 165203678Sbrucec if (error != 0) { 166203678Sbrucec pjdlog_common(LOG_ERR, 0, error, 167203678Sbrucec "Unable to prepare control header"); 168184610Salfred goto end; 169184610Salfred } 170184610Salfred if (hast_proto_send(res, res->hr_ctrl, cnvout, NULL, 0) == -1) { 171184610Salfred error = errno; 172184610Salfred pjdlog_errno(LOG_ERR, "Unable to send control header"); 173184610Salfred goto end; 174184610Salfred } 175184610Salfred 176186730Salfred /* 177199060Sthompsa * Receive response. 178184610Salfred */ 179200825Sthompsa if (hast_proto_recv_hdr(res->hr_ctrl, &cnvin) == -1) { 180200825Sthompsa error = errno; 181200825Sthompsa pjdlog_errno(LOG_ERR, "Unable to receive control header"); 182200825Sthompsa goto end; 183184610Salfred } 184184610Salfred 185184610Salfred error = nv_get_int16(cnvin, "error"); 186200825Sthompsa if (error != 0) 187184610Salfred goto end; 188200825Sthompsa 189200825Sthompsa if ((str = nv_get_string(cnvin, "status")) == NULL) { 190184610Salfred error = ENOENT; 191184610Salfred pjdlog_errno(LOG_ERR, "Field 'status' is missing."); 192184610Salfred goto end; 193184610Salfred } 194184610Salfred nv_add_string(nvout, str, "status%u", no); 195184610Salfred nv_add_uint64(nvout, nv_get_uint64(cnvin, "dirty"), "dirty%u", no); 196184610Salfred nv_add_uint32(nvout, nv_get_uint32(cnvin, "extentsize"), 197184610Salfred "extentsize%u", no); 198184610Salfred nv_add_uint32(nvout, nv_get_uint32(cnvin, "keepdirty"), 199184610Salfred "keepdirty%u", no); 200192984Sthompsa nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_read"), 201184610Salfred "stat_read%u", no); 202184610Salfred nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_write"), 203184610Salfred "stat_write%u", no); 204184610Salfred nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_delete"), 205184610Salfred "stat_delete%u", no); 206184610Salfred nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_flush"), 207184610Salfred "stat_flush%u", no); 208184610Salfred nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_activemap_update"), 209184610Salfred "stat_activemap_update%u", no); 210184610Salfredend: 211184610Salfred if (cnvin != NULL) 212184610Salfred nv_free(cnvin); 213184610Salfred if (cnvout != NULL) 214184610Salfred nv_free(cnvout); 215184610Salfred if (error != 0) 216184610Salfred nv_add_int16(nvout, error, "error"); 217184610Salfred} 218184610Salfred 219184610Salfredstatic void 220184610Salfredcontrol_status(struct hastd_config *cfg, struct nv *nvout, 221184610Salfred struct hast_resource *res, const char *name, unsigned int no) 222184610Salfred{ 223192984Sthompsa 224184610Salfred PJDLOG_ASSERT(cfg != NULL); 225184610Salfred PJDLOG_ASSERT(nvout != NULL); 226184610Salfred PJDLOG_ASSERT(name != NULL); 227184610Salfred 228184610Salfred /* Name is always needed. */ 229184610Salfred nv_add_string(nvout, name, "resource%u", no); 230184610Salfred 231184610Salfred if (res == NULL) { 232184610Salfred TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 233184610Salfred if (strcmp(res->hr_name, name) == 0) 234184610Salfred break; 235184610Salfred } 236184610Salfred if (res == NULL) { 237184610Salfred nv_add_int16(nvout, EHAST_NOENTRY, "error%u", no); 238184610Salfred return; 239184610Salfred } 240184610Salfred } 241184610Salfred PJDLOG_ASSERT(res != NULL); 242184610Salfred nv_add_string(nvout, res->hr_provname, "provname%u", no); 243184610Salfred nv_add_string(nvout, res->hr_localpath, "localpath%u", no); 244184610Salfred nv_add_string(nvout, res->hr_remoteaddr, "remoteaddr%u", no); 245184610Salfred if (res->hr_sourceaddr[0] != '\0') 246184610Salfred nv_add_string(nvout, res->hr_sourceaddr, "sourceaddr%u", no); 247192984Sthompsa switch (res->hr_replication) { 248192984Sthompsa case HAST_REPLICATION_FULLSYNC: 249184610Salfred nv_add_string(nvout, "fullsync", "replication%u", no); 250184610Salfred break; 251184610Salfred case HAST_REPLICATION_MEMSYNC: 252184610Salfred nv_add_string(nvout, "memsync", "replication%u", no); 253184610Salfred break; 254184610Salfred case HAST_REPLICATION_ASYNC: 255184610Salfred nv_add_string(nvout, "async", "replication%u", no); 256184610Salfred break; 257184610Salfred default: 258184610Salfred nv_add_string(nvout, "unknown", "replication%u", no); 259184610Salfred break; 260184610Salfred } 261184610Salfred nv_add_string(nvout, checksum_name(res->hr_checksum), 262184610Salfred "checksum%u", no); 263184610Salfred nv_add_string(nvout, compression_name(res->hr_compression), 264184610Salfred "compression%u", no); 265184610Salfred nv_add_string(nvout, role2str(res->hr_role), "role%u", no); 266184610Salfred 267184610Salfred switch (res->hr_role) { 268184610Salfred case HAST_ROLE_PRIMARY: 269184610Salfred PJDLOG_ASSERT(res->hr_workerpid != 0); 270184610Salfred /* FALLTHROUGH */ 271184610Salfred case HAST_ROLE_SECONDARY: 272184610Salfred if (res->hr_workerpid != 0) 273184610Salfred break; 274184610Salfred /* FALLTHROUGH */ 275184610Salfred default: 276184610Salfred return; 277184610Salfred } 278184610Salfred 279184610Salfred /* 280192984Sthompsa * If we are here, it means that we have a worker process, which we 281203678Sbrucec * want to ask some questions. 282203678Sbrucec */ 283203678Sbrucec control_status_worker(res, nvout, no); 284203678Sbrucec} 285203678Sbrucec 286203678Sbrucecvoid 287203678Sbruceccontrol_handle(struct hastd_config *cfg) 288184610Salfred{ 289184610Salfred struct proto_conn *conn; 290184610Salfred struct nv *nvin, *nvout; 291184610Salfred unsigned int ii; 292184610Salfred const char *str; 293184610Salfred uint8_t cmd, role; 294184610Salfred int error; 295184610Salfred 296184610Salfred if (proto_accept(cfg->hc_controlconn, &conn) == -1) { 297184610Salfred pjdlog_errno(LOG_ERR, "Unable to accept control connection"); 298184610Salfred return; 299184610Salfred } 300184610Salfred 301184610Salfred cfg->hc_controlin = conn; 302184610Salfred nvin = nvout = NULL; 303184610Salfred role = HAST_ROLE_UNDEF; 304184610Salfred 305184610Salfred if (hast_proto_recv_hdr(conn, &nvin) == -1) { 306184610Salfred pjdlog_errno(LOG_ERR, "Unable to receive control header"); 307184610Salfred nvin = NULL; 308184610Salfred goto close; 309184610Salfred } 310184610Salfred 311184610Salfred /* Obtain command code. 0 means that nv_get_uint8() failed. */ 312184610Salfred cmd = nv_get_uint8(nvin, "cmd"); 313184610Salfred if (cmd == 0) { 314184610Salfred pjdlog_error("Control header is missing 'cmd' field."); 315184610Salfred goto close; 316184610Salfred } 317184610Salfred 318184610Salfred /* Allocate outgoing nv structure. */ 319184610Salfred nvout = nv_alloc(); 320184610Salfred if (nvout == NULL) { 321184610Salfred pjdlog_error("Unable to allocate header for control response."); 322184610Salfred goto close; 323184610Salfred } 324207077Sthompsa 325184610Salfred error = 0; 326184610Salfred 327184610Salfred str = nv_get_string(nvin, "resource0"); 328184610Salfred if (str == NULL) { 329184610Salfred pjdlog_error("Control header is missing 'resource0' field."); 330184610Salfred error = EHAST_INVALID; 331184610Salfred goto fail; 332184610Salfred } 333184610Salfred if (cmd == HASTCTL_CMD_SETROLE) { 334184610Salfred role = nv_get_uint8(nvin, "role"); 335184610Salfred switch (role) { 336184610Salfred case HAST_ROLE_INIT: 337193045Sthompsa case HAST_ROLE_PRIMARY: 338193045Sthompsa case HAST_ROLE_SECONDARY: 339193045Sthompsa break; 340193045Sthompsa default: 341193045Sthompsa pjdlog_error("Invalid role received (%hhu).", role); 342193045Sthompsa error = EHAST_INVALID; 343193045Sthompsa goto fail; 344184610Salfred } 345185948Sthompsa } 346200825Sthompsa if (strcmp(str, "all") == 0) { 347185948Sthompsa struct hast_resource *res; 348192984Sthompsa 349185948Sthompsa /* All configured resources. */ 350185948Sthompsa 351185948Sthompsa ii = 0; 352185948Sthompsa TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 353185948Sthompsa switch (cmd) { 354185948Sthompsa case HASTCTL_CMD_SETROLE: 355185948Sthompsa control_set_role_common(cfg, nvout, role, res, 356185948Sthompsa res->hr_name, ii++); 357185948Sthompsa break; 358185948Sthompsa case HASTCTL_CMD_STATUS: 359185948Sthompsa control_status(cfg, nvout, res, res->hr_name, 360185948Sthompsa ii++); 361185948Sthompsa break; 362203678Sbrucec default: 363185948Sthompsa pjdlog_error("Invalid command received (%hhu).", 364185948Sthompsa cmd); 365185948Sthompsa error = EHAST_UNIMPLEMENTED; 366185948Sthompsa goto fail; 367185948Sthompsa } 368185948Sthompsa } 369185948Sthompsa } else { 370185948Sthompsa /* Only selected resources. */ 371203678Sbrucec 372185948Sthompsa for (ii = 0; ; ii++) { 373185948Sthompsa str = nv_get_string(nvin, "resource%u", ii); 374185948Sthompsa if (str == NULL) 375185948Sthompsa break; 376185948Sthompsa switch (cmd) { 377185948Sthompsa case HASTCTL_CMD_SETROLE: 378185948Sthompsa control_set_role_common(cfg, nvout, role, NULL, 379185948Sthompsa str, ii); 380185948Sthompsa break; 381185948Sthompsa case HASTCTL_CMD_STATUS: 382185948Sthompsa control_status(cfg, nvout, NULL, str, ii); 383185948Sthompsa break; 384185948Sthompsa default: 385185948Sthompsa pjdlog_error("Invalid command received (%hhu).", 386192984Sthompsa cmd); 387192984Sthompsa error = EHAST_UNIMPLEMENTED; 388185948Sthompsa goto fail; 389185948Sthompsa } 390185948Sthompsa } 391193045Sthompsa } 392185948Sthompsa if (nv_error(nvout) != 0) 393185948Sthompsa goto close; 394185948Sthompsafail: 395185948Sthompsa if (error != 0) 396185948Sthompsa nv_add_int16(nvout, error, "error"); 397192984Sthompsa 398192984Sthompsa if (hast_proto_send(NULL, conn, nvout, NULL, 0) == -1) 399192984Sthompsa pjdlog_errno(LOG_ERR, "Unable to send control response"); 400192984Sthompsaclose: 401192984Sthompsa if (nvin != NULL) 402192984Sthompsa nv_free(nvin); 403192984Sthompsa if (nvout != NULL) 404192984Sthompsa nv_free(nvout); 405185948Sthompsa proto_close(conn); 406185948Sthompsa cfg->hc_controlin = NULL; 407185948Sthompsa} 408184610Salfred 409207077Sthompsa/* 410185948Sthompsa * Thread handles control requests from the parent. 411203678Sbrucec */ 412185948Sthompsavoid * 413185948Sthompsactrl_thread(void *arg) 414185948Sthompsa{ 415184610Salfred struct hast_resource *res = arg; 416184610Salfred struct nv *nvin, *nvout; 417192984Sthompsa uint8_t cmd; 418186730Salfred 419184610Salfred for (;;) { 420184610Salfred if (hast_proto_recv_hdr(res->hr_ctrl, &nvin) == -1) { 421184610Salfred if (sigexit_received) 422184610Salfred pthread_exit(NULL); 423190734Sthompsa pjdlog_errno(LOG_ERR, 424199060Sthompsa "Unable to receive control message"); 425190734Sthompsa kill(getpid(), SIGTERM); 426190734Sthompsa pthread_exit(NULL); 427184610Salfred } 428184610Salfred cmd = nv_get_uint8(nvin, "cmd"); 429184610Salfred if (cmd == 0) { 430184610Salfred pjdlog_error("Control message is missing 'cmd' field."); 431184610Salfred nv_free(nvin); 432184610Salfred continue; 433190734Sthompsa } 434199060Sthompsa nvout = nv_alloc(); 435190734Sthompsa switch (cmd) { 436190734Sthompsa case CONTROL_STATUS: 437184610Salfred if (res->hr_remotein != NULL && 438184610Salfred res->hr_remoteout != NULL) { 439184610Salfred nv_add_string(nvout, "complete", "status"); 440192984Sthompsa } else { 441186730Salfred nv_add_string(nvout, "degraded", "status"); 442184610Salfred } 443184610Salfred nv_add_uint32(nvout, (uint32_t)res->hr_extentsize, 444184610Salfred "extentsize"); 445184610Salfred if (res->hr_role == HAST_ROLE_PRIMARY) { 446190734Sthompsa nv_add_uint32(nvout, 447199060Sthompsa (uint32_t)res->hr_keepdirty, "keepdirty"); 448190734Sthompsa nv_add_uint64(nvout, 449190734Sthompsa (uint64_t)(activemap_ndirty(res->hr_amp) * 450184610Salfred res->hr_extentsize), "dirty"); 451184610Salfred } else { 452184610Salfred nv_add_uint32(nvout, (uint32_t)0, "keepdirty"); 453184610Salfred nv_add_uint64(nvout, (uint64_t)0, "dirty"); 454184610Salfred } 455184610Salfred nv_add_uint64(nvout, res->hr_stat_read, "stat_read"); 456190734Sthompsa nv_add_uint64(nvout, res->hr_stat_write, "stat_write"); 457199060Sthompsa nv_add_uint64(nvout, res->hr_stat_delete, 458190734Sthompsa "stat_delete"); 459190734Sthompsa nv_add_uint64(nvout, res->hr_stat_flush, "stat_flush"); 460184610Salfred nv_add_uint64(nvout, res->hr_stat_activemap_update, 461184610Salfred "stat_activemap_update"); 462184610Salfred nv_add_int16(nvout, 0, "error"); 463192984Sthompsa break; 464184610Salfred case CONTROL_RELOAD: 465184610Salfred /* 466184610Salfred * When parent receives SIGHUP and discovers that 467184610Salfred * something related to us has changes, it sends reload 468184610Salfred * message to us. 469192984Sthompsa */ 470190734Sthompsa PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY); 471190734Sthompsa primary_config_reload(res, nvin); 472184610Salfred nv_add_int16(nvout, 0, "error"); 473184610Salfred break; 474184610Salfred default: 475184610Salfred nv_add_int16(nvout, EINVAL, "error"); 476184610Salfred break; 477184610Salfred } 478184610Salfred nv_free(nvin); 479184610Salfred if (nv_error(nvout) != 0) { 480184610Salfred pjdlog_error("Unable to create answer on control message."); 481184610Salfred nv_free(nvout); 482184610Salfred continue; 483184610Salfred } 484184610Salfred if (hast_proto_send(NULL, res->hr_ctrl, nvout, NULL, 0) == -1) { 485184610Salfred pjdlog_errno(LOG_ERR, 486184610Salfred "Unable to send reply to control message"); 487184610Salfred } 488184610Salfred nv_free(nvout); 489184610Salfred } 490184610Salfred /* NOTREACHED */ 491184610Salfred return (NULL); 492184610Salfred} 493184610Salfred