hastd.c revision 218375
1233447Sedwin/*- 2192886Sedwin * Copyright (c) 2009-2010 The FreeBSD Foundation 3192886Sedwin * Copyright (c) 2010-2011 Pawel Jakub Dawidek <pjd@FreeBSD.org> 464499Swollman * All rights reserved. 52742Swollman * 62742Swollman * This software was developed by Pawel Jakub Dawidek under sponsorship from 72742Swollman * the FreeBSD Foundation. 82742Swollman * 9158421Swollman * Redistribution and use in source and binary forms, with or without 102742Swollman * modification, are permitted provided that the following conditions 112742Swollman * are met: 12158421Swollman * 1. Redistributions of source code must retain the above copyright 13158421Swollman * notice, this list of conditions and the following disclaimer. 142742Swollman * 2. Redistributions in binary form must reproduce the above copyright 1586222Swollman * notice, this list of conditions and the following disclaimer in the 1620094Swollman * documentation and/or other materials provided with the distribution. 1720094Swollman * 1820094Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 1920094Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2020094Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21158421Swollman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22158421Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2320094Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 242742Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 252742Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 262742Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 272742Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 282742Swollman * SUCH DAMAGE. 2958787Sru */ 302742Swollman 312742Swollman#include <sys/cdefs.h> 322742Swollman__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 218375 2011-02-06 14:09:19Z pjd $"); 332742Swollman 34114173Swollman#include <sys/param.h> 35114173Swollman#include <sys/linker.h> 36114173Swollman#include <sys/module.h> 37114173Swollman#include <sys/stat.h> 38114173Swollman#include <sys/wait.h> 39114173Swollman 40114173Swollman#include <err.h> 41114173Swollman#include <errno.h> 42114173Swollman#include <libutil.h> 43114173Swollman#include <signal.h> 44114173Swollman#include <stdbool.h> 45114173Swollman#include <stdio.h> 46114173Swollman#include <stdlib.h> 47114173Swollman#include <string.h> 48149590Swollman#include <sysexits.h> 49149590Swollman#include <unistd.h> 50114173Swollman 512742Swollman#include <activemap.h> 529908Swollman#include <pjdlog.h> 532742Swollman 542742Swollman#include "control.h" 552742Swollman#include "event.h" 562742Swollman#include "hast.h" 572742Swollman#include "hast_proto.h" 582742Swollman#include "hastd.h" 592742Swollman#include "hooks.h" 602742Swollman#include "subr.h" 612742Swollman 6220094Swollman/* Path to configuration file. */ 632742Swollmanconst char *cfgpath = HAST_CONFIG; 6420094Swollman/* Hastd configuration. */ 65158421Swollmanstatic struct hastd_config *cfg; 6620094Swollman/* Was SIGINT or SIGTERM signal received? */ 6720094Swollmanbool sigexit_received = false; 6820094Swollman/* PID file handle. */ 6920094Swollmanstruct pidfh *pfh; 7020094Swollman 7120094Swollman/* How often check for hooks running for too long. */ 7220094Swollman#define REPORT_INTERVAL 5 7320094Swollman 7420094Swollmanstatic void 7520094Swollmanusage(void) 7620094Swollman{ 7720094Swollman 7820094Swollman errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]"); 792742Swollman} 802742Swollman 812742Swollmanstatic void 822742Swollmang_gate_load(void) 8319878Swollman{ 842742Swollman 852742Swollman if (modfind("g_gate") == -1) { 862742Swollman /* Not present in kernel, try loading it. */ 87158421Swollman if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) { 88158421Swollman if (errno != EEXIST) { 89158421Swollman pjdlog_exit(EX_OSERR, 90158421Swollman "Unable to load geom_gate module"); 91158421Swollman } 92153670Swollman } 9343014Swollman } 9443014Swollman} 9543014Swollman 96233447Sedwinvoid 97233447Sedwindescriptors_cleanup(struct hast_resource *res) 98233447Sedwin{ 99233447Sedwin struct hast_resource *tres; 100233447Sedwin 101233447Sedwin TAILQ_FOREACH(tres, &cfg->hc_resources, hr_next) { 102233447Sedwin if (tres == res) { 103233447Sedwin PJDLOG_VERIFY(res->hr_role == HAST_ROLE_SECONDARY || 104233447Sedwin (res->hr_remotein == NULL && 105233447Sedwin res->hr_remoteout == NULL)); 106233447Sedwin continue; 107233447Sedwin } 108233447Sedwin if (tres->hr_remotein != NULL) 109233447Sedwin proto_close(tres->hr_remotein); 110233447Sedwin if (tres->hr_remoteout != NULL) 1112742Swollman proto_close(tres->hr_remoteout); 1122742Swollman if (tres->hr_ctrl != NULL) 11319878Swollman proto_close(tres->hr_ctrl); 11419878Swollman if (tres->hr_event != NULL) 11519878Swollman proto_close(tres->hr_event); 11658787Sru if (tres->hr_conn != NULL) 11743014Swollman proto_close(tres->hr_conn); 118233447Sedwin } 119233447Sedwin if (cfg->hc_controlin != NULL) 1202742Swollman proto_close(cfg->hc_controlin); 1212742Swollman proto_close(cfg->hc_controlconn); 122153670Swollman proto_close(cfg->hc_listenconn); 123153670Swollman (void)pidfile_close(pfh); 124153670Swollman hook_fini(); 12543014Swollman pjdlog_fini(); 126153670Swollman} 127153670Swollman 1282742Swollmanstatic const char * 1292742Swollmandtype2str(mode_t mode) 13019878Swollman{ 13119878Swollman 13219878Swollman if (S_ISBLK(mode)) 133149514Swollman return ("block device"); 13420094Swollman else if (S_ISCHR(mode)) 13543014Swollman return ("character device"); 13643014Swollman else if (S_ISDIR(mode)) 1372742Swollman return ("directory"); 1382742Swollman else if (S_ISFIFO(mode)) 1392742Swollman return ("pipe or FIFO"); 14067578Swollman else if (S_ISLNK(mode)) 1412742Swollman return ("symbolic link"); 1422742Swollman else if (S_ISREG(mode)) 1432742Swollman return ("regular file"); 1442742Swollman else if (S_ISSOCK(mode)) 145193785Sedwin return ("socket"); 146193785Sedwin else if (S_ISWHT(mode)) 147193785Sedwin return ("whiteout"); 148193785Sedwin else 149193785Sedwin return ("unknown"); 150193785Sedwin} 151193785Sedwin 152193785Sedwinvoid 153193785Sedwindescriptors_assert(const struct hast_resource *res, int pjdlogmode) 154193785Sedwin{ 155193785Sedwin char msg[256]; 156193785Sedwin struct stat sb; 157193785Sedwin long maxfd; 158193785Sedwin bool isopen; 159193785Sedwin mode_t mode; 160193785Sedwin int fd; 161193785Sedwin 162193785Sedwin /* 163193785Sedwin * At this point descriptor to syslog socket is closed, so if we want 164193785Sedwin * to log assertion message, we have to first store it in 'msg' local 165193785Sedwin * buffer and then open syslog socket and log it. 166193785Sedwin */ 167193785Sedwin msg[0] = '\0'; 168193785Sedwin 169193785Sedwin maxfd = sysconf(_SC_OPEN_MAX); 170193785Sedwin if (maxfd < 0) { 171193785Sedwin pjdlog_init(pjdlogmode); 172193785Sedwin pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 173193785Sedwin role2str(res->hr_role)); 174193785Sedwin pjdlog_errno(LOG_WARNING, "sysconf(_SC_OPEN_MAX) failed"); 175193785Sedwin pjdlog_fini(); 176193785Sedwin maxfd = 16384; 177193785Sedwin } 178193785Sedwin for (fd = 0; fd <= maxfd; fd++) { 179193785Sedwin if (fstat(fd, &sb) == 0) { 180193785Sedwin isopen = true; 181193785Sedwin mode = sb.st_mode; 182193785Sedwin } else if (errno == EBADF) { 183194485Sedwin isopen = false; 184194485Sedwin mode = 0; 185194485Sedwin } else { 186194485Sedwin isopen = true; /* silence gcc */ 187194485Sedwin mode = 0; /* silence gcc */ 188194485Sedwin (void)snprintf(msg, sizeof(msg), 189193785Sedwin "Unable to fstat descriptor %d: %s", fd, 190198270Sedwin strerror(errno)); 191198270Sedwin break; 192198270Sedwin } 193198270Sedwin if (fd == STDIN_FILENO || fd == STDOUT_FILENO || 194198270Sedwin fd == STDERR_FILENO) { 195198270Sedwin if (!isopen) { 196198270Sedwin (void)snprintf(msg, sizeof(msg), 197198270Sedwin "Descriptor %d (%s) is closed, but should be open.", 198198270Sedwin fd, (fd == STDIN_FILENO ? "stdin" : 199198270Sedwin (fd == STDOUT_FILENO ? "stdout" : "stderr"))); 200198270Sedwin break; 201198270Sedwin } 202198270Sedwin } else if (fd == proto_descriptor(res->hr_event)) { 203196581Sedwin if (!isopen) { 204198270Sedwin (void)snprintf(msg, sizeof(msg), 205198270Sedwin "Descriptor %d (event) is closed, but should be open.", 206198270Sedwin fd); 207198270Sedwin break; 208198270Sedwin } 209198270Sedwin if (!S_ISSOCK(mode)) { 210198270Sedwin (void)snprintf(msg, sizeof(msg), 211198270Sedwin "Descriptor %d (event) is %s, but should be %s.", 212198270Sedwin fd, dtype2str(mode), dtype2str(S_IFSOCK)); 213198270Sedwin break; 214198270Sedwin } 215201189Sedwin } else if (fd == proto_descriptor(res->hr_ctrl)) { 216201189Sedwin if (!isopen) { 217201189Sedwin (void)snprintf(msg, sizeof(msg), 218201189Sedwin "Descriptor %d (ctrl) is closed, but should be open.", 219201189Sedwin fd); 220201189Sedwin break; 221201189Sedwin } 222201189Sedwin if (!S_ISSOCK(mode)) { 223201189Sedwin (void)snprintf(msg, sizeof(msg), 224201189Sedwin "Descriptor %d (ctrl) is %s, but should be %s.", 225201189Sedwin fd, dtype2str(mode), dtype2str(S_IFSOCK)); 226201189Sedwin break; 227201189Sedwin } 228201189Sedwin } else if (fd == proto_descriptor(res->hr_conn)) { 229201189Sedwin if (!isopen) { 230201189Sedwin (void)snprintf(msg, sizeof(msg), 231201189Sedwin "Descriptor %d (conn) is closed, but should be open.", 232201189Sedwin fd); 233206219Sedwin break; 234206219Sedwin } 235206219Sedwin if (!S_ISSOCK(mode)) { 236206219Sedwin (void)snprintf(msg, sizeof(msg), 237206219Sedwin "Descriptor %d (conn) is %s, but should be %s.", 238204887Sedwin fd, dtype2str(mode), dtype2str(S_IFSOCK)); 239206219Sedwin break; 240206219Sedwin } 241206219Sedwin } else if (res->hr_role == HAST_ROLE_SECONDARY && 242206219Sedwin fd == proto_descriptor(res->hr_remotein)) { 243204887Sedwin if (!isopen) { 244201189Sedwin (void)snprintf(msg, sizeof(msg), 245202606Sedwin "Descriptor %d (remote in) is closed, but should be open.", 246204887Sedwin fd); 247201189Sedwin break; 2482742Swollman } 24975267Swollman if (!S_ISSOCK(mode)) { 25019878Swollman (void)snprintf(msg, sizeof(msg), 25119878Swollman "Descriptor %d (remote in) is %s, but should be %s.", 2522742Swollman fd, dtype2str(mode), dtype2str(S_IFSOCK)); 25319878Swollman break; 25419878Swollman } 255201189Sedwin } else if (res->hr_role == HAST_ROLE_SECONDARY && 256201189Sedwin fd == proto_descriptor(res->hr_remoteout)) { 2572742Swollman if (!isopen) { 2582742Swollman (void)snprintf(msg, sizeof(msg), 2592742Swollman "Descriptor %d (remote out) is closed, but should be open.", 26067578Swollman fd); 2612742Swollman break; 26219878Swollman } 2632742Swollman if (!S_ISSOCK(mode)) { 2642742Swollman (void)snprintf(msg, sizeof(msg), 26586222Swollman "Descriptor %d (remote out) is %s, but should be %s.", 26686222Swollman fd, dtype2str(mode), dtype2str(S_IFSOCK)); 267149514Swollman break; 268149514Swollman } 269149514Swollman } else { 2702742Swollman if (isopen) { 271149514Swollman (void)snprintf(msg, sizeof(msg), 272149514Swollman "Descriptor %d is open (%s), but should be closed.", 27386222Swollman fd, dtype2str(mode)); 2742742Swollman break; 2752742Swollman } 2762742Swollman } 2772742Swollman } 2782742Swollman if (msg[0] != '\0') { 2792742Swollman pjdlog_init(pjdlogmode); 2802742Swollman pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 28114343Swollman role2str(res->hr_role)); 2822742Swollman PJDLOG_ABORT("%s", msg); 28317200Swollman } 28419878Swollman} 28519878Swollman 2862742Swollmanstatic void 28719878Swollmanchild_exit_log(unsigned int pid, int status) 2882742Swollman{ 2892742Swollman 2902742Swollman if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 2912742Swollman pjdlog_debug(1, "Worker process exited gracefully (pid=%u).", 29219878Swollman pid); 2932742Swollman } else if (WIFSIGNALED(status)) { 2942742Swollman pjdlog_error("Worker process killed (pid=%u, signal=%d).", 2952742Swollman pid, WTERMSIG(status)); 2962742Swollman } else { 29743014Swollman pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).", 2982742Swollman pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1); 2992742Swollman } 3002742Swollman} 3012742Swollman 30219878Swollmanstatic void 30319878Swollmanchild_exit(void) 3042742Swollman{ 3052742Swollman struct hast_resource *res; 3062742Swollman int status; 30793799Swollman pid_t pid; 3082742Swollman 3092742Swollman while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 3102742Swollman /* Find resource related to the process that just exited. */ 3112742Swollman TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 3122742Swollman if (pid == res->hr_workerpid) 3132742Swollman break; 3142742Swollman } 3152742Swollman if (res == NULL) { 31619878Swollman /* 3172742Swollman * This can happen when new connection arrives and we 3182742Swollman * cancel child responsible for the old one or if this 3192742Swollman * was hook which we executed. 320158421Swollman */ 321158421Swollman hook_check_one(pid, status); 322158421Swollman continue; 323158421Swollman } 3242742Swollman pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 325158421Swollman role2str(res->hr_role)); 326158421Swollman child_exit_log(pid, status); 3272742Swollman child_cleanup(res); 328158421Swollman if (res->hr_role == HAST_ROLE_PRIMARY) { 3292742Swollman /* 3302742Swollman * Restart child process if it was killed by signal 3312742Swollman * or exited because of temporary problem. 3322742Swollman */ 3332742Swollman if (WIFSIGNALED(status) || 33414343Swollman (WIFEXITED(status) && 33514343Swollman WEXITSTATUS(status) == EX_TEMPFAIL)) { 336163302Sru sleep(1); 33793799Swollman pjdlog_info("Restarting worker process."); 33893799Swollman hastd_primary(res); 33993799Swollman } else { 340163302Sru res->hr_role = HAST_ROLE_INIT; 341169811Swollman pjdlog_info("Changing resource role back to %s.", 342163302Sru role2str(res->hr_role)); 343163302Sru } 344163302Sru } 345163302Sru pjdlog_prefix_set("%s", ""); 346163302Sru } 347163302Sru} 348163302Sru 349163302Srustatic bool 350163302Sruresource_needs_restart(const struct hast_resource *res0, 351163302Sru const struct hast_resource *res1) 352163302Sru{ 353181421Sedwin 354181421Sedwin PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0); 355181421Sedwin 356181421Sedwin if (strcmp(res0->hr_provname, res1->hr_provname) != 0) 357181421Sedwin return (true); 358181421Sedwin if (strcmp(res0->hr_localpath, res1->hr_localpath) != 0) 359181421Sedwin return (true); 360181421Sedwin if (res0->hr_role == HAST_ROLE_INIT || 361181421Sedwin res0->hr_role == HAST_ROLE_SECONDARY) { 362181421Sedwin if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 363181421Sedwin return (true); 364181421Sedwin if (res0->hr_replication != res1->hr_replication) 365181421Sedwin return (true); 366181421Sedwin if (res0->hr_timeout != res1->hr_timeout) 367181421Sedwin return (true); 368181421Sedwin if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 369181421Sedwin return (true); 370181421Sedwin } 371181421Sedwin return (false); 372181421Sedwin} 373181421Sedwin 374181421Sedwinstatic bool 375163302Sruresource_needs_reload(const struct hast_resource *res0, 376163302Sru const struct hast_resource *res1) 37793799Swollman{ 378163302Sru 37967578Swollman PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0); 38093799Swollman PJDLOG_ASSERT(strcmp(res0->hr_provname, res1->hr_provname) == 0); 38114343Swollman PJDLOG_ASSERT(strcmp(res0->hr_localpath, res1->hr_localpath) == 0); 38293799Swollman 38393799Swollman if (res0->hr_role != HAST_ROLE_PRIMARY) 38414343Swollman return (false); 38593799Swollman 386163302Sru if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 3872742Swollman return (true); 3882742Swollman if (res0->hr_replication != res1->hr_replication) 3892742Swollman return (true); 39093799Swollman if (res0->hr_timeout != res1->hr_timeout) 391163302Sru return (true); 392163302Sru if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 393163302Sru return (true); 394163302Sru return (false); 39586222Swollman} 39693799Swollman 39714343Swollmanstatic void 39893799Swollmanresource_reload(const struct hast_resource *res) 399163302Sru{ 400163302Sru struct nv *nvin, *nvout; 401163302Sru int error; 402163302Sru 403163302Sru PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY); 404163302Sru 405163302Sru nvout = nv_alloc(); 406163302Sru nv_add_uint8(nvout, HASTCTL_RELOAD, "cmd"); 40767578Swollman nv_add_string(nvout, res->hr_remoteaddr, "remoteaddr"); 40819878Swollman nv_add_int32(nvout, (int32_t)res->hr_replication, "replication"); 40914343Swollman nv_add_int32(nvout, (int32_t)res->hr_timeout, "timeout"); 41093799Swollman nv_add_string(nvout, res->hr_exec, "exec"); 411163302Sru if (nv_error(nvout) != 0) { 412163302Sru nv_free(nvout); 413163302Sru pjdlog_error("Unable to allocate header for reload message."); 414163302Sru return; 415200835Sedwin } 416200835Sedwin if (hast_proto_send(res, res->hr_ctrl, nvout, NULL, 0) < 0) { 417200835Sedwin pjdlog_errno(LOG_ERR, "Unable to send reload message"); 418200835Sedwin nv_free(nvout); 419200835Sedwin return; 420200835Sedwin } 421200835Sedwin nv_free(nvout); 422200835Sedwin 423200835Sedwin /* Receive response. */ 424200835Sedwin if (hast_proto_recv_hdr(res->hr_ctrl, &nvin) < 0) { 425200835Sedwin pjdlog_errno(LOG_ERR, "Unable to receive reload reply"); 426200835Sedwin return; 427200835Sedwin } 428200835Sedwin error = nv_get_int16(nvin, "error"); 429200835Sedwin nv_free(nvin); 430200835Sedwin if (error != 0) { 431200835Sedwin pjdlog_common(LOG_ERR, 0, error, "Reload failed"); 432200835Sedwin return; 433200835Sedwin } 434200835Sedwin} 435200835Sedwin 436200835Sedwinstatic void 437200835Sedwinhastd_reload(void) 438200835Sedwin{ 439200835Sedwin struct hastd_config *newcfg; 440200835Sedwin struct hast_resource *nres, *cres, *tres; 441200835Sedwin uint8_t role; 442200835Sedwin 443200835Sedwin pjdlog_info("Reloading configuration..."); 444200835Sedwin 445200835Sedwin newcfg = yy_config_parse(cfgpath, false); 446200835Sedwin if (newcfg == NULL) 447200835Sedwin goto failed; 448200835Sedwin 449200835Sedwin /* 450200835Sedwin * Check if control address has changed. 451200835Sedwin */ 452200835Sedwin if (strcmp(cfg->hc_controladdr, newcfg->hc_controladdr) != 0) { 453200835Sedwin if (proto_server(newcfg->hc_controladdr, 454200835Sedwin &newcfg->hc_controlconn) < 0) { 455200835Sedwin pjdlog_errno(LOG_ERR, 456200835Sedwin "Unable to listen on control address %s", 457200835Sedwin newcfg->hc_controladdr); 458200835Sedwin goto failed; 459200835Sedwin } 460200835Sedwin } 461200835Sedwin /* 462200835Sedwin * Check if listen address has changed. 463200835Sedwin */ 464200835Sedwin if (strcmp(cfg->hc_listenaddr, newcfg->hc_listenaddr) != 0) { 465200835Sedwin if (proto_server(newcfg->hc_listenaddr, 466200835Sedwin &newcfg->hc_listenconn) < 0) { 467200835Sedwin pjdlog_errno(LOG_ERR, "Unable to listen on address %s", 468200835Sedwin newcfg->hc_listenaddr); 469200835Sedwin goto failed; 470200835Sedwin } 471200835Sedwin } 472200835Sedwin /* 473200835Sedwin * Only when both control and listen sockets are successfully 474200835Sedwin * initialized switch them to new configuration. 475200835Sedwin */ 476200835Sedwin if (newcfg->hc_controlconn != NULL) { 477200835Sedwin pjdlog_info("Control socket changed from %s to %s.", 47867578Swollman cfg->hc_controladdr, newcfg->hc_controladdr); 47919878Swollman proto_close(cfg->hc_controlconn); 48019878Swollman cfg->hc_controlconn = newcfg->hc_controlconn; 48114343Swollman newcfg->hc_controlconn = NULL; 48293799Swollman strlcpy(cfg->hc_controladdr, newcfg->hc_controladdr, 483198825Sedwin sizeof(cfg->hc_controladdr)); 484198825Sedwin } 485200835Sedwin if (newcfg->hc_listenconn != NULL) { 486200835Sedwin pjdlog_info("Listen socket changed from %s to %s.", 487198825Sedwin cfg->hc_listenaddr, newcfg->hc_listenaddr); 488198825Sedwin proto_close(cfg->hc_listenconn); 489198825Sedwin cfg->hc_listenconn = newcfg->hc_listenconn; 490198825Sedwin newcfg->hc_listenconn = NULL; 491198825Sedwin strlcpy(cfg->hc_listenaddr, newcfg->hc_listenaddr, 492198825Sedwin sizeof(cfg->hc_listenaddr)); 493198825Sedwin } 494198825Sedwin 495198825Sedwin /* 496198825Sedwin * Stop and remove resources that were removed from the configuration. 497198825Sedwin */ 498198825Sedwin TAILQ_FOREACH_SAFE(cres, &cfg->hc_resources, hr_next, tres) { 499198825Sedwin TAILQ_FOREACH(nres, &newcfg->hc_resources, hr_next) { 500198825Sedwin if (strcmp(cres->hr_name, nres->hr_name) == 0) 501198825Sedwin break; 502198825Sedwin } 503198825Sedwin if (nres == NULL) { 504198825Sedwin control_set_role(cres, HAST_ROLE_INIT); 505198825Sedwin TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 506198825Sedwin pjdlog_info("Resource %s removed.", cres->hr_name); 507198825Sedwin free(cres); 508198825Sedwin } 509198825Sedwin } 510198825Sedwin /* 511198825Sedwin * Move new resources to the current configuration. 512198825Sedwin */ 513198825Sedwin TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 514198825Sedwin TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 515198825Sedwin if (strcmp(cres->hr_name, nres->hr_name) == 0) 516198825Sedwin break; 517198825Sedwin } 518198825Sedwin if (cres == NULL) { 519198825Sedwin TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 520198825Sedwin TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 521198825Sedwin pjdlog_info("Resource %s added.", nres->hr_name); 522198825Sedwin } 523198825Sedwin } 524198825Sedwin /* 525198825Sedwin * Deal with modified resources. 526198825Sedwin * Depending on what has changed exactly we might want to perform 527198825Sedwin * different actions. 528198825Sedwin * 529198825Sedwin * We do full resource restart in the following situations: 530198825Sedwin * Resource role is INIT or SECONDARY. 531198825Sedwin * Resource role is PRIMARY and path to local component or provider 532198825Sedwin * name has changed. 533198825Sedwin * In case of PRIMARY, the worker process will be killed and restarted, 534198825Sedwin * which also means removing /dev/hast/<name> provider and 535198825Sedwin * recreating it. 536198825Sedwin * 537198825Sedwin * We do just reload (send SIGHUP to worker process) if we act as 538198825Sedwin * PRIMARY, but only if remote address, replication mode, timeout or 539198825Sedwin * execution path has changed. For those, there is no need to restart 540198825Sedwin * worker process. 541198825Sedwin * If PRIMARY receives SIGHUP, it will reconnect if remote address or 542198825Sedwin * replication mode has changed or simply set new timeout if only 543198825Sedwin * timeout has changed. 544198825Sedwin */ 545198825Sedwin TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 546198825Sedwin TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 547198825Sedwin if (strcmp(cres->hr_name, nres->hr_name) == 0) 548198825Sedwin break; 549198825Sedwin } 55067578Swollman PJDLOG_ASSERT(cres != NULL); 5512742Swollman if (resource_needs_restart(cres, nres)) { 552198825Sedwin pjdlog_info("Resource %s configuration was modified, restarting it.", 553198825Sedwin cres->hr_name); 5542742Swollman role = cres->hr_role; 5552742Swollman control_set_role(cres, HAST_ROLE_INIT); 5562742Swollman TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 5572742Swollman free(cres); 5582742Swollman TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 559198825Sedwin TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 560198825Sedwin control_set_role(nres, role); 5612742Swollman } else if (resource_needs_reload(cres, nres)) { 5622742Swollman pjdlog_info("Resource %s configuration was modified, reloading it.", 5632742Swollman cres->hr_name); 5642742Swollman strlcpy(cres->hr_remoteaddr, nres->hr_remoteaddr, 5652742Swollman sizeof(cres->hr_remoteaddr)); 566213312Sedwin cres->hr_replication = nres->hr_replication; 567213312Sedwin cres->hr_timeout = nres->hr_timeout; 568198825Sedwin strlcpy(cres->hr_exec, nres->hr_exec, 569198825Sedwin sizeof(cres->hr_exec)); 570198825Sedwin if (cres->hr_workerpid != 0) 5712742Swollman resource_reload(cres); 5722742Swollman } 573198825Sedwin } 574198825Sedwin 57558787Sru yy_config_free(newcfg); 5762742Swollman pjdlog_info("Configuration reloaded successfully."); 57730711Swollman return; 57830711Swollmanfailed: 57943014Swollman if (newcfg != NULL) { 58030711Swollman if (newcfg->hc_controlconn != NULL) 581158421Swollman proto_close(newcfg->hc_controlconn); 58243543Swollman if (newcfg->hc_listenconn != NULL) 58343543Swollman proto_close(newcfg->hc_listenconn); 58443543Swollman yy_config_free(newcfg); 585206868Sedwin } 586206868Sedwin pjdlog_warning("Configuration not reloaded."); 587206868Sedwin} 588206868Sedwin 589206868Sedwinstatic void 590206868Sedwinterminate_workers(void) 591206868Sedwin{ 592206868Sedwin struct hast_resource *res; 593206868Sedwin 594206868Sedwin pjdlog_info("Termination signal received, exiting."); 595206868Sedwin TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 596206868Sedwin if (res->hr_workerpid == 0) 597206868Sedwin continue; 598206868Sedwin pjdlog_info("Terminating worker process (resource=%s, role=%s, pid=%u).", 599206868Sedwin res->hr_name, role2str(res->hr_role), res->hr_workerpid); 600206868Sedwin if (kill(res->hr_workerpid, SIGTERM) == 0) 601206868Sedwin continue; 602206868Sedwin pjdlog_errno(LOG_WARNING, 603206868Sedwin "Unable to send signal to worker process (resource=%s, role=%s, pid=%u).", 604206868Sedwin res->hr_name, role2str(res->hr_role), res->hr_workerpid); 605206868Sedwin } 606206868Sedwin} 60730711Swollman 60830711Swollmanstatic void 60930711Swollmanlisten_accept(void) 61030711Swollman{ 61130711Swollman struct hast_resource *res; 61230711Swollman struct proto_conn *conn; 61330711Swollman struct nv *nvin, *nvout, *nverr; 61430711Swollman const char *resname; 61530711Swollman const unsigned char *token; 61630711Swollman char laddr[256], raddr[256]; 617206868Sedwin size_t size; 618206868Sedwin pid_t pid; 619206868Sedwin int status; 62030711Swollman 62186222Swollman proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr)); 62230711Swollman pjdlog_debug(1, "Accepting connection to %s.", laddr); 62330711Swollman 62493799Swollman if (proto_accept(cfg->hc_listenconn, &conn) < 0) { 6252742Swollman pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr); 62693799Swollman return; 62793799Swollman } 62893799Swollman 62993799Swollman proto_local_address(conn, laddr, sizeof(laddr)); 63093799Swollman proto_remote_address(conn, raddr, sizeof(raddr)); 63193799Swollman pjdlog_info("Connection from %s to %s.", raddr, laddr); 63293799Swollman 63393799Swollman /* Error in setting timeout is not critical, but why should it fail? */ 63493799Swollman if (proto_timeout(conn, HAST_TIMEOUT) < 0) 63593799Swollman pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); 63693799Swollman 63793799Swollman nvin = nvout = nverr = NULL; 63893799Swollman 63993799Swollman /* 6402742Swollman * Before receiving any data see if remote host have access to any 64193799Swollman * resource. 64293799Swollman */ 64319878Swollman TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 6442742Swollman if (proto_address_match(conn, res->hr_remoteaddr)) 6452742Swollman break; 6462742Swollman } 6472742Swollman if (res == NULL) { 6482742Swollman pjdlog_error("Client %s isn't known.", raddr); 6492742Swollman goto close; 65019878Swollman } 6512742Swollman /* Ok, remote host can access at least one resource. */ 65219878Swollman 6532742Swollman if (hast_proto_recv_hdr(conn, &nvin) < 0) { 65419878Swollman pjdlog_errno(LOG_ERR, "Unable to receive header from %s", 6552742Swollman raddr); 6562742Swollman goto close; 65743543Swollman } 65843543Swollman 6592742Swollman resname = nv_get_string(nvin, "resource"); 6602742Swollman if (resname == NULL) { 66143543Swollman pjdlog_error("No 'resource' field in the header received from %s.", 66258787Sru raddr); 66343543Swollman goto close; 6642742Swollman } 66567578Swollman pjdlog_debug(2, "%s: resource=%s", raddr, resname); 66667578Swollman token = nv_get_uint8_array(nvin, &size, "token"); 66767578Swollman /* 66867578Swollman * NULL token means that this is first conection. 6692742Swollman */ 670149514Swollman if (token != NULL && size != sizeof(res->hr_token)) { 6719908Swollman pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).", 6729908Swollman raddr, sizeof(res->hr_token), size); 6739908Swollman goto close; 67414343Swollman } 67514343Swollman 676149514Swollman /* 67720094Swollman * From now on we want to send errors to the remote node. 67820094Swollman */ 67920094Swollman nverr = nv_alloc(); 680136638Swollman 681136638Swollman /* Find resource related to this connection. */ 682149514Swollman TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 683136638Swollman if (strcmp(resname, res->hr_name) == 0) 684136638Swollman break; 685136638Swollman } 686136638Swollman /* Have we found the resource? */ 687136638Swollman if (res == NULL) { 688136638Swollman pjdlog_error("No resource '%s' as requested by %s.", 689136638Swollman resname, raddr); 690153670Swollman nv_add_stringf(nverr, "errmsg", "Resource not configured."); 691153670Swollman goto fail; 692153670Swollman } 693153670Swollman 694153670Swollman /* Now that we know resource name setup log prefix. */ 695153670Swollman pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 696153670Swollman 697153670Swollman /* Does the remote host have access to this resource? */ 698153670Swollman if (!proto_address_match(conn, res->hr_remoteaddr)) { 699153670Swollman pjdlog_error("Client %s has no access to the resource.", raddr); 700153670Swollman nv_add_stringf(nverr, "errmsg", "No access to the resource."); 7012742Swollman goto fail; 7022742Swollman } 70319878Swollman /* Is the resource marked as secondary? */ 70419878Swollman if (res->hr_role != HAST_ROLE_SECONDARY) { 70519878Swollman pjdlog_error("We act as %s for the resource and not as %s as requested by %s.", 70619878Swollman role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY), 70720094Swollman raddr); 70820094Swollman nv_add_stringf(nverr, "errmsg", 70920094Swollman "Remote node acts as %s for the resource and not as %s.", 71043543Swollman role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY)); 711136638Swollman goto fail; 712153670Swollman } 713153670Swollman /* Does token (if exists) match? */ 7142742Swollman if (token != NULL && memcmp(token, res->hr_token, 71558787Sru sizeof(res->hr_token)) != 0) { 71675267Swollman pjdlog_error("Token received from %s doesn't match.", raddr); 717169811Swollman nv_add_stringf(nverr, "errmsg", "Token doesn't match."); 718169811Swollman goto fail; 71975267Swollman } 72075267Swollman /* 72175267Swollman * If there is no token, but we have half-open connection 72275267Swollman * (only remotein) or full connection (worker process is running) 72375267Swollman * we have to cancel those and accept the new connection. 72475267Swollman */ 72575267Swollman if (token == NULL) { 72675267Swollman PJDLOG_ASSERT(res->hr_remoteout == NULL); 72775267Swollman pjdlog_debug(1, "Initial connection from %s.", raddr); 72875267Swollman if (res->hr_workerpid != 0) { 72975267Swollman PJDLOG_ASSERT(res->hr_remotein == NULL); 73075267Swollman pjdlog_debug(1, 73175267Swollman "Worker process exists (pid=%u), stopping it.", 73275267Swollman (unsigned int)res->hr_workerpid); 73375267Swollman /* Stop child process. */ 73475267Swollman if (kill(res->hr_workerpid, SIGINT) < 0) { 73575267Swollman pjdlog_errno(LOG_ERR, 73675267Swollman "Unable to stop worker process (pid=%u)", 73775267Swollman (unsigned int)res->hr_workerpid); 73875267Swollman /* 73975267Swollman * Other than logging the problem we 74058787Sru * ignore it - nothing smart to do. 74158787Sru */ 742149514Swollman } 743169811Swollman /* Wait for it to exit. */ 744149514Swollman else if ((pid = waitpid(res->hr_workerpid, 74586222Swollman &status, 0)) != res->hr_workerpid) { 746149514Swollman /* We can only log the problem. */ 74758787Sru pjdlog_errno(LOG_ERR, 7482742Swollman "Waiting for worker process (pid=%u) failed", 7492742Swollman (unsigned int)res->hr_workerpid); 750177591Sedwin } else { 75119878Swollman child_exit_log(res->hr_workerpid, status); 75219878Swollman } 7532742Swollman child_cleanup(res); 7542742Swollman } else if (res->hr_remotein != NULL) { 7552742Swollman char oaddr[256]; 756177591Sedwin 7572742Swollman proto_remote_address(res->hr_remotein, oaddr, 7582742Swollman sizeof(oaddr)); 7592742Swollman pjdlog_debug(1, 7602742Swollman "Canceling half-open connection from %s on connection from %s.", 7612742Swollman oaddr, raddr); 76286222Swollman proto_close(res->hr_remotein); 763158421Swollman res->hr_remotein = NULL; 76486222Swollman } 76586222Swollman } 76686222Swollman 76786222Swollman /* 76886222Swollman * Checks and cleanups are done. 769169811Swollman */ 770169811Swollman 771169811Swollman if (token == NULL) { 772169811Swollman arc4random_buf(res->hr_token, sizeof(res->hr_token)); 773169811Swollman nvout = nv_alloc(); 774169811Swollman nv_add_uint8_array(nvout, res->hr_token, 775169811Swollman sizeof(res->hr_token), "token"); 776169811Swollman if (nv_error(nvout) != 0) { 777169811Swollman pjdlog_common(LOG_ERR, 0, nv_error(nvout), 778169811Swollman "Unable to prepare return header for %s", raddr); 779169811Swollman nv_add_stringf(nverr, "errmsg", 780169811Swollman "Remote node was unable to prepare return header: %s.", 781169811Swollman strerror(nv_error(nvout))); 7822742Swollman goto fail; 7832742Swollman } 784158421Swollman if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) { 78558787Sru int error = errno; 78658787Sru 78719878Swollman pjdlog_errno(LOG_ERR, "Unable to send response to %s", 78886222Swollman raddr); 789169811Swollman nv_add_stringf(nverr, "errmsg", 79086222Swollman "Remote node was unable to send response: %s.", 79186222Swollman strerror(error)); 79286222Swollman goto fail; 79386222Swollman } 79486222Swollman res->hr_remotein = conn; 79586222Swollman pjdlog_debug(1, "Incoming connection from %s configured.", 79686222Swollman raddr); 797169811Swollman } else { 79886222Swollman res->hr_remoteout = conn; 79986222Swollman pjdlog_debug(1, "Outgoing connection to %s configured.", raddr); 80086222Swollman hastd_secondary(res, nvin); 80186222Swollman } 80286222Swollman nv_free(nvin); 80393799Swollman nv_free(nvout); 80419878Swollman nv_free(nverr); 80586222Swollman pjdlog_prefix_set("%s", ""); 806169811Swollman return; 80786222Swollmanfail: 8082742Swollman if (nv_error(nverr) != 0) { 809169811Swollman pjdlog_common(LOG_ERR, 0, nv_error(nverr), 8102742Swollman "Unable to prepare error header for %s", raddr); 81186222Swollman goto close; 8122742Swollman } 8132742Swollman if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) { 814114173Swollman pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr); 815114173Swollman goto close; 816114173Swollman } 817114173Swollmanclose: 81821217Swollman if (nvin != NULL) 819114173Swollman nv_free(nvin); 820114173Swollman if (nvout != NULL) 82121217Swollman nv_free(nvout); 822114173Swollman if (nverr != NULL) 823114173Swollman nv_free(nverr); 824114173Swollman proto_close(conn); 825114173Swollman pjdlog_prefix_set("%s", ""); 826114173Swollman} 827114173Swollman 828114173Swollmanstatic void 829114173Swollmanconnection_migrate(struct hast_resource *res) 830114173Swollman{ 831114173Swollman struct proto_conn *conn; 832114173Swollman int16_t val = 0; 833114173Swollman 834114173Swollman if (proto_recv(res->hr_conn, &val, sizeof(val)) < 0) { 835114173Swollman pjdlog_errno(LOG_WARNING, 836114173Swollman "Unable to receive connection command"); 837114173Swollman return; 838114173Swollman } 839114173Swollman if (proto_client(res->hr_remoteaddr, &conn) < 0) { 840114173Swollman val = errno; 841114173Swollman pjdlog_errno(LOG_WARNING, 842114173Swollman "Unable to create outgoing connection to %s", 843114173Swollman res->hr_remoteaddr); 844149514Swollman goto out; 845149514Swollman } 846149514Swollman if (proto_connect(conn, -1) < 0) { 847149514Swollman val = errno; 848149514Swollman pjdlog_errno(LOG_WARNING, "Unable to connect to %s", 849149514Swollman res->hr_remoteaddr); 850149514Swollman proto_close(conn); 851158421Swollman goto out; 852158421Swollman } 853149514Swollman val = 0; 854149514Swollmanout: 855149514Swollman if (proto_send(res->hr_conn, &val, sizeof(val)) < 0) { 856149514Swollman pjdlog_errno(LOG_WARNING, 85721217Swollman "Unable to send reply to connection request"); 858149514Swollman } 859149514Swollman if (val == 0 && proto_connection_send(res->hr_conn, conn) < 0) 860149514Swollman pjdlog_errno(LOG_WARNING, "Unable to send connection"); 861149514Swollman} 862149514Swollman 863149514Swollmanstatic void 864149514Swollmanmain_loop(void) 865149514Swollman{ 866149514Swollman struct hast_resource *res; 867149514Swollman struct timeval seltimeout; 868149514Swollman struct timespec sigtimeout; 869149514Swollman int fd, maxfd, ret, signo; 870149514Swollman sigset_t mask; 871149514Swollman fd_set rfds; 872158421Swollman 873158421Swollman seltimeout.tv_sec = REPORT_INTERVAL; 874158421Swollman seltimeout.tv_usec = 0; 875158421Swollman sigtimeout.tv_sec = 0; 876172479Sedwin sigtimeout.tv_nsec = 0; 877172479Sedwin 878172479Sedwin PJDLOG_VERIFY(sigemptyset(&mask) == 0); 879172479Sedwin PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0); 880172479Sedwin PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0); 881174242Sedwin PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0); 882174242Sedwin PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0); 883174242Sedwin 884174242Sedwin pjdlog_info("Started successfully, running protocol version %d.", 885174242Sedwin HAST_PROTO_VERSION); 886174242Sedwin 887174242Sedwin for (;;) { 888174242Sedwin while ((signo = sigtimedwait(&mask, NULL, &sigtimeout)) != -1) { 889174242Sedwin switch (signo) { 8902742Swollman case SIGINT: 891114173Swollman case SIGTERM: 892114173Swollman sigexit_received = true; 893114173Swollman terminate_workers(); 894114173Swollman proto_close(cfg->hc_controlconn); 895114173Swollman exit(EX_OK); 896114173Swollman break; 897114173Swollman case SIGCHLD: 898114173Swollman child_exit(); 899114173Swollman break; 900114173Swollman case SIGHUP: 901114173Swollman hastd_reload(); 902114173Swollman break; 903114173Swollman default: 904114173Swollman PJDLOG_ABORT("Unexpected signal (%d).", signo); 905114173Swollman } 906114173Swollman } 907114173Swollman 908158421Swollman /* Setup descriptors for select(2). */ 909158421Swollman FD_ZERO(&rfds); 910172479Sedwin maxfd = fd = proto_descriptor(cfg->hc_controlconn); 911172479Sedwin PJDLOG_ASSERT(fd >= 0); 912172479Sedwin FD_SET(fd, &rfds); 913172479Sedwin fd = proto_descriptor(cfg->hc_listenconn); 914172479Sedwin PJDLOG_ASSERT(fd >= 0); 915172479Sedwin FD_SET(fd, &rfds); 916172479Sedwin maxfd = fd > maxfd ? fd : maxfd; 917172479Sedwin TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 918172479Sedwin if (res->hr_event == NULL) 919172479Sedwin continue; 920172479Sedwin PJDLOG_ASSERT(res->hr_conn != NULL); 921172479Sedwin fd = proto_descriptor(res->hr_event); 922172479Sedwin PJDLOG_ASSERT(fd >= 0); 923172479Sedwin FD_SET(fd, &rfds); 924172479Sedwin maxfd = fd > maxfd ? fd : maxfd; 925172479Sedwin if (res->hr_role == HAST_ROLE_PRIMARY) { 926172479Sedwin /* Only primary workers asks for connections. */ 927172479Sedwin fd = proto_descriptor(res->hr_conn); 928172479Sedwin PJDLOG_ASSERT(fd >= 0); 929172479Sedwin FD_SET(fd, &rfds); 930172479Sedwin maxfd = fd > maxfd ? fd : maxfd; 931172479Sedwin } 932172479Sedwin } 933172479Sedwin 934172479Sedwin PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE); 935172479Sedwin ret = select(maxfd + 1, &rfds, NULL, NULL, &seltimeout); 936172479Sedwin if (ret == 0) 937172479Sedwin hook_check(); 938172479Sedwin else if (ret == -1) { 939172479Sedwin if (errno == EINTR) 9402742Swollman continue; 9412742Swollman KEEP_ERRNO((void)pidfile_remove(pfh)); 94219878Swollman pjdlog_exit(EX_OSERR, "select() failed"); 943114173Swollman } 94419878Swollman 94519878Swollman if (FD_ISSET(proto_descriptor(cfg->hc_controlconn), &rfds)) 9462742Swollman control_handle(cfg); 94764499Swollman if (FD_ISSET(proto_descriptor(cfg->hc_listenconn), &rfds)) 9482742Swollman listen_accept(); 94964499Swollman TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 950149514Swollman if (res->hr_event == NULL) 95164499Swollman continue; 95275267Swollman PJDLOG_ASSERT(res->hr_conn != NULL); 95364499Swollman if (FD_ISSET(proto_descriptor(res->hr_event), &rfds)) { 95464499Swollman if (event_recv(res) == 0) 95564499Swollman continue; 95664499Swollman /* The worker process exited? */ 95764499Swollman proto_close(res->hr_event); 95864499Swollman res->hr_event = NULL; 95964499Swollman proto_close(res->hr_conn); 96064499Swollman res->hr_conn = NULL; 96164499Swollman continue; 96264499Swollman } 96364499Swollman if (res->hr_role == HAST_ROLE_PRIMARY && 964177591Sedwin FD_ISSET(proto_descriptor(res->hr_conn), &rfds)) { 965177591Sedwin connection_migrate(res); 966177591Sedwin } 967177591Sedwin } 968177591Sedwin } 969177591Sedwin} 970177591Sedwin 971177591Sedwinstatic void 972177591Sedwindummy_sighandler(int sig __unused) 973177591Sedwin{ 974177591Sedwin /* Nothing to do. */ 975177591Sedwin} 976177591Sedwin 977177591Sedwinint 978177591Sedwinmain(int argc, char *argv[]) 9792742Swollman{ 9802742Swollman const char *pidfile; 9812742Swollman pid_t otherpid; 9822742Swollman bool foreground; 9832742Swollman int debuglevel; 98420094Swollman sigset_t mask; 98520094Swollman 98620094Swollman foreground = false; 987158421Swollman debuglevel = 0; 988169811Swollman pidfile = HASTD_PIDFILE; 989177591Sedwin 990177591Sedwin for (;;) { 9912742Swollman int ch; 9922742Swollman 99319878Swollman ch = getopt(argc, argv, "c:dFhP:"); 9942742Swollman if (ch == -1) 9952742Swollman break; 9962742Swollman switch (ch) { 9972742Swollman case 'c': 9982742Swollman cfgpath = optarg; 9992742Swollman break; 10002742Swollman case 'd': 10012742Swollman debuglevel++; 100275267Swollman break; 100375267Swollman case 'F': 100475267Swollman foreground = true; 100575267Swollman break; 100675267Swollman case 'P': 100775267Swollman pidfile = optarg; 100875267Swollman break; 100975267Swollman case 'h': 101075267Swollman default: 101175267Swollman usage(); 101275267Swollman } 101375267Swollman } 101475267Swollman argc -= optind; 101575267Swollman argv += optind; 101675267Swollman 101775267Swollman pjdlog_init(PJDLOG_MODE_STD); 101875267Swollman pjdlog_debug_set(debuglevel); 101975267Swollman 102075267Swollman g_gate_load(); 10212742Swollman 1022158421Swollman pfh = pidfile_open(pidfile, 0600, &otherpid); 10232742Swollman if (pfh == NULL) { 10242742Swollman if (errno == EEXIST) { 10252742Swollman pjdlog_exitx(EX_TEMPFAIL, 10262742Swollman "Another hastd is already running, pid: %jd.", 10272742Swollman (intmax_t)otherpid); 10282742Swollman } 10292742Swollman /* If we cannot create pidfile from other reasons, only warn. */ 10302742Swollman pjdlog_errno(LOG_WARNING, "Unable to open or create pidfile"); 10312742Swollman } 10322742Swollman 10332742Swollman cfg = yy_config_parse(cfgpath, true); 10342742Swollman PJDLOG_ASSERT(cfg != NULL); 10352742Swollman 10362742Swollman /* 10372742Swollman * Restore default actions for interesting signals in case parent 10382742Swollman * process (like init(8)) decided to ignore some of them (like SIGHUP). 10392742Swollman */ 10402742Swollman PJDLOG_VERIFY(signal(SIGHUP, SIG_DFL) != SIG_ERR); 10412742Swollman PJDLOG_VERIFY(signal(SIGINT, SIG_DFL) != SIG_ERR); 10422742Swollman PJDLOG_VERIFY(signal(SIGTERM, SIG_DFL) != SIG_ERR); 10432742Swollman /* 10442742Swollman * Because SIGCHLD is ignored by default, setup dummy handler for it, 10452742Swollman * so we can mask it. 10462742Swollman */ 10472742Swollman PJDLOG_VERIFY(signal(SIGCHLD, dummy_sighandler) != SIG_ERR); 10482742Swollman 10492742Swollman PJDLOG_VERIFY(sigemptyset(&mask) == 0); 10502742Swollman PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0); 10512742Swollman PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0); 10522742Swollman PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0); 10532742Swollman PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0); 10542742Swollman PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); 10552742Swollman 10562742Swollman /* Listen on control address. */ 10572742Swollman if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) { 10582742Swollman KEEP_ERRNO((void)pidfile_remove(pfh)); 10592742Swollman pjdlog_exit(EX_OSERR, "Unable to listen on control address %s", 10602742Swollman cfg->hc_controladdr); 10612742Swollman } 10622742Swollman /* Listen for remote connections. */ 10632742Swollman if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) { 10642742Swollman KEEP_ERRNO((void)pidfile_remove(pfh)); 1065149514Swollman pjdlog_exit(EX_OSERR, "Unable to listen on address %s", 1066149514Swollman cfg->hc_listenaddr); 1067149514Swollman } 106830711Swollman 10692742Swollman if (!foreground) { 10702742Swollman if (daemon(0, 0) < 0) { 107143543Swollman KEEP_ERRNO((void)pidfile_remove(pfh)); 107243543Swollman pjdlog_exit(EX_OSERR, "Unable to daemonize"); 107343543Swollman } 107443543Swollman 107543543Swollman /* Start logging to syslog. */ 107643543Swollman pjdlog_mode_set(PJDLOG_MODE_SYSLOG); 107743543Swollman 107864499Swollman /* Write PID to a file. */ 107964499Swollman if (pidfile_write(pfh) < 0) { 108043543Swollman pjdlog_errno(LOG_WARNING, 108164499Swollman "Unable to write PID to a file"); 108264499Swollman } 108364499Swollman } 108464499Swollman 108564499Swollman hook_init(); 108664499Swollman 108764499Swollman main_loop(); 108864499Swollman 108964499Swollman exit(0); 109064499Swollman} 10912742Swollman