hastd.c revision 218138
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3218041Spjd * Copyright (c) 2010-2011 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4204076Spjd * All rights reserved. 5204076Spjd * 6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 7204076Spjd * the FreeBSD Foundation. 8204076Spjd * 9204076Spjd * Redistribution and use in source and binary forms, with or without 10204076Spjd * modification, are permitted provided that the following conditions 11204076Spjd * are met: 12204076Spjd * 1. Redistributions of source code must retain the above copyright 13204076Spjd * notice, this list of conditions and the following disclaimer. 14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 15204076Spjd * notice, this list of conditions and the following disclaimer in the 16204076Spjd * documentation and/or other materials provided with the distribution. 17204076Spjd * 18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28204076Spjd * SUCH DAMAGE. 29204076Spjd */ 30204076Spjd 31204076Spjd#include <sys/cdefs.h> 32204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 218138 2011-01-31 18:32:17Z pjd $"); 33204076Spjd 34204076Spjd#include <sys/param.h> 35204076Spjd#include <sys/linker.h> 36204076Spjd#include <sys/module.h> 37218044Spjd#include <sys/stat.h> 38204076Spjd#include <sys/wait.h> 39204076Spjd 40204076Spjd#include <err.h> 41204076Spjd#include <errno.h> 42204076Spjd#include <libutil.h> 43204076Spjd#include <signal.h> 44204076Spjd#include <stdbool.h> 45204076Spjd#include <stdio.h> 46204076Spjd#include <stdlib.h> 47204076Spjd#include <string.h> 48204076Spjd#include <sysexits.h> 49204076Spjd#include <unistd.h> 50204076Spjd 51204076Spjd#include <activemap.h> 52204076Spjd#include <pjdlog.h> 53204076Spjd 54204076Spjd#include "control.h" 55212038Spjd#include "event.h" 56204076Spjd#include "hast.h" 57204076Spjd#include "hast_proto.h" 58204076Spjd#include "hastd.h" 59211977Spjd#include "hooks.h" 60204076Spjd#include "subr.h" 61204076Spjd 62204076Spjd/* Path to configuration file. */ 63210886Spjdconst char *cfgpath = HAST_CONFIG; 64204076Spjd/* Hastd configuration. */ 65204076Spjdstatic struct hastd_config *cfg; 66204076Spjd/* Was SIGINT or SIGTERM signal received? */ 67204076Spjdbool sigexit_received = false; 68204076Spjd/* PID file handle. */ 69204076Spjdstruct pidfh *pfh; 70204076Spjd 71211977Spjd/* How often check for hooks running for too long. */ 72213430Spjd#define REPORT_INTERVAL 5 73211977Spjd 74204076Spjdstatic void 75204076Spjdusage(void) 76204076Spjd{ 77204076Spjd 78204076Spjd errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]"); 79204076Spjd} 80204076Spjd 81204076Spjdstatic void 82204076Spjdg_gate_load(void) 83204076Spjd{ 84204076Spjd 85204076Spjd if (modfind("g_gate") == -1) { 86204076Spjd /* Not present in kernel, try loading it. */ 87204076Spjd if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) { 88204076Spjd if (errno != EEXIST) { 89204076Spjd pjdlog_exit(EX_OSERR, 90204076Spjd "Unable to load geom_gate module"); 91204076Spjd } 92204076Spjd } 93204076Spjd } 94204076Spjd} 95204076Spjd 96218041Spjdvoid 97218041Spjddescriptors_cleanup(struct hast_resource *res) 98218041Spjd{ 99218041Spjd struct hast_resource *tres; 100218041Spjd 101218041Spjd TAILQ_FOREACH(tres, &cfg->hc_resources, hr_next) { 102218041Spjd if (tres == res) { 103218041Spjd PJDLOG_VERIFY(res->hr_role == HAST_ROLE_SECONDARY || 104218041Spjd (res->hr_remotein == NULL && 105218041Spjd res->hr_remoteout == NULL)); 106218041Spjd continue; 107218041Spjd } 108218041Spjd if (tres->hr_remotein != NULL) 109218041Spjd proto_close(tres->hr_remotein); 110218041Spjd if (tres->hr_remoteout != NULL) 111218041Spjd proto_close(tres->hr_remoteout); 112218041Spjd } 113218041Spjd if (cfg->hc_controlin != NULL) 114218041Spjd proto_close(cfg->hc_controlin); 115218041Spjd proto_close(cfg->hc_controlconn); 116218041Spjd proto_close(cfg->hc_listenconn); 117218041Spjd (void)pidfile_close(pfh); 118218041Spjd hook_fini(); 119218041Spjd pjdlog_fini(); 120218041Spjd} 121218041Spjd 122218044Spjdstatic const char * 123218044Spjddtype2str(mode_t mode) 124218044Spjd{ 125218044Spjd 126218044Spjd if (S_ISBLK(mode)) 127218044Spjd return ("block device"); 128218044Spjd else if (S_ISCHR(mode)) 129218044Spjd return ("character device"); 130218044Spjd else if (S_ISDIR(mode)) 131218044Spjd return ("directory"); 132218044Spjd else if (S_ISFIFO(mode)) 133218044Spjd return ("pipe or FIFO"); 134218044Spjd else if (S_ISLNK(mode)) 135218044Spjd return ("symbolic link"); 136218044Spjd else if (S_ISREG(mode)) 137218044Spjd return ("regular file"); 138218044Spjd else if (S_ISSOCK(mode)) 139218044Spjd return ("socket"); 140218044Spjd else if (S_ISWHT(mode)) 141218044Spjd return ("whiteout"); 142218044Spjd else 143218044Spjd return ("unknown"); 144218044Spjd} 145218044Spjd 146218044Spjdvoid 147218044Spjddescriptors_assert(const struct hast_resource *res, int pjdlogmode) 148218044Spjd{ 149218044Spjd char msg[256]; 150218044Spjd struct stat sb; 151218044Spjd long maxfd; 152218044Spjd bool isopen; 153218044Spjd mode_t mode; 154218044Spjd int fd; 155218044Spjd 156218044Spjd /* 157218044Spjd * At this point descriptor to syslog socket is closed, so if we want 158218044Spjd * to log assertion message, we have to first store it in 'msg' local 159218044Spjd * buffer and then open syslog socket and log it. 160218044Spjd */ 161218044Spjd msg[0] = '\0'; 162218044Spjd 163218044Spjd maxfd = sysconf(_SC_OPEN_MAX); 164218044Spjd if (maxfd < 0) { 165218044Spjd pjdlog_errno(LOG_WARNING, "sysconf(_SC_OPEN_MAX) failed"); 166218044Spjd maxfd = 16384; 167218044Spjd } 168218044Spjd for (fd = 0; fd <= maxfd; fd++) { 169218044Spjd if (fstat(fd, &sb) == 0) { 170218044Spjd isopen = true; 171218044Spjd mode = sb.st_mode; 172218044Spjd } else if (errno == EBADF) { 173218044Spjd isopen = false; 174218044Spjd mode = 0; 175218044Spjd } else { 176218044Spjd isopen = true; /* silence gcc */ 177218044Spjd mode = 0; /* silence gcc */ 178218044Spjd snprintf(msg, sizeof(msg), 179218044Spjd "Unable to fstat descriptor %d: %s", fd, 180218044Spjd strerror(errno)); 181218044Spjd } 182218044Spjd if (fd == STDIN_FILENO || fd == STDOUT_FILENO || 183218044Spjd fd == STDERR_FILENO) { 184218044Spjd if (!isopen) { 185218044Spjd snprintf(msg, sizeof(msg), 186218044Spjd "Descriptor %d (%s) is closed, but should be open.", 187218044Spjd fd, (fd == STDIN_FILENO ? "stdin" : 188218044Spjd (fd == STDOUT_FILENO ? "stdout" : "stderr"))); 189218044Spjd break; 190218044Spjd } 191218044Spjd } else if (fd == proto_descriptor(res->hr_event)) { 192218044Spjd if (!isopen) { 193218044Spjd snprintf(msg, sizeof(msg), 194218044Spjd "Descriptor %d (event) is closed, but should be open.", 195218044Spjd fd); 196218044Spjd break; 197218044Spjd } 198218044Spjd if (!S_ISSOCK(mode)) { 199218044Spjd snprintf(msg, sizeof(msg), 200218044Spjd "Descriptor %d (event) is %s, but should be %s.", 201218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 202218044Spjd break; 203218044Spjd } 204218044Spjd } else if (fd == proto_descriptor(res->hr_ctrl)) { 205218044Spjd if (!isopen) { 206218044Spjd snprintf(msg, sizeof(msg), 207218044Spjd "Descriptor %d (ctrl) is closed, but should be open.", 208218044Spjd fd); 209218044Spjd break; 210218044Spjd } 211218044Spjd if (!S_ISSOCK(mode)) { 212218044Spjd snprintf(msg, sizeof(msg), 213218044Spjd "Descriptor %d (ctrl) is %s, but should be %s.", 214218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 215218044Spjd break; 216218044Spjd } 217218044Spjd } else if (res->hr_role == HAST_ROLE_SECONDARY && 218218044Spjd fd == proto_descriptor(res->hr_remotein)) { 219218044Spjd if (!isopen) { 220218044Spjd snprintf(msg, sizeof(msg), 221218044Spjd "Descriptor %d (remote in) is closed, but should be open.", 222218044Spjd fd); 223218044Spjd break; 224218044Spjd } 225218044Spjd if (!S_ISSOCK(mode)) { 226218044Spjd snprintf(msg, sizeof(msg), 227218044Spjd "Descriptor %d (remote in) is %s, but should be %s.", 228218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 229218044Spjd break; 230218044Spjd } 231218044Spjd } else if (res->hr_role == HAST_ROLE_SECONDARY && 232218044Spjd fd == proto_descriptor(res->hr_remoteout)) { 233218044Spjd if (!isopen) { 234218044Spjd snprintf(msg, sizeof(msg), 235218044Spjd "Descriptor %d (remote out) is closed, but should be open.", 236218044Spjd fd); 237218044Spjd break; 238218044Spjd } 239218044Spjd if (!S_ISSOCK(mode)) { 240218044Spjd snprintf(msg, sizeof(msg), 241218044Spjd "Descriptor %d (remote out) is %s, but should be %s.", 242218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 243218044Spjd break; 244218044Spjd } 245218044Spjd } else { 246218044Spjd if (isopen) { 247218044Spjd snprintf(msg, sizeof(msg), 248218044Spjd "Descriptor %d is open (%s), but should be closed.", 249218044Spjd fd, dtype2str(mode)); 250218044Spjd break; 251218044Spjd } 252218044Spjd } 253218044Spjd } 254218044Spjd if (msg[0] != '\0') { 255218044Spjd pjdlog_init(pjdlogmode); 256218044Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 257218044Spjd role2str(res->hr_role)); 258218044Spjd PJDLOG_ABORT("%s", msg); 259218044Spjd } 260218044Spjd} 261218044Spjd 262204076Spjdstatic void 263207372Spjdchild_exit_log(unsigned int pid, int status) 264207372Spjd{ 265207372Spjd 266207372Spjd if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 267207372Spjd pjdlog_debug(1, "Worker process exited gracefully (pid=%u).", 268207372Spjd pid); 269207372Spjd } else if (WIFSIGNALED(status)) { 270207372Spjd pjdlog_error("Worker process killed (pid=%u, signal=%d).", 271207372Spjd pid, WTERMSIG(status)); 272207372Spjd } else { 273207372Spjd pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).", 274207372Spjd pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1); 275207372Spjd } 276207372Spjd} 277207372Spjd 278207372Spjdstatic void 279204076Spjdchild_exit(void) 280204076Spjd{ 281204076Spjd struct hast_resource *res; 282204076Spjd int status; 283204076Spjd pid_t pid; 284204076Spjd 285204076Spjd while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 286204076Spjd /* Find resource related to the process that just exited. */ 287204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 288204076Spjd if (pid == res->hr_workerpid) 289204076Spjd break; 290204076Spjd } 291204076Spjd if (res == NULL) { 292204076Spjd /* 293204076Spjd * This can happen when new connection arrives and we 294211977Spjd * cancel child responsible for the old one or if this 295211977Spjd * was hook which we executed. 296204076Spjd */ 297211977Spjd hook_check_one(pid, status); 298204076Spjd continue; 299204076Spjd } 300204076Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 301204076Spjd role2str(res->hr_role)); 302207372Spjd child_exit_log(pid, status); 303213006Spjd child_cleanup(res); 304204076Spjd if (res->hr_role == HAST_ROLE_PRIMARY) { 305207372Spjd /* 306207372Spjd * Restart child process if it was killed by signal 307207372Spjd * or exited because of temporary problem. 308207372Spjd */ 309207372Spjd if (WIFSIGNALED(status) || 310207372Spjd (WIFEXITED(status) && 311207372Spjd WEXITSTATUS(status) == EX_TEMPFAIL)) { 312207348Spjd sleep(1); 313207348Spjd pjdlog_info("Restarting worker process."); 314207348Spjd hastd_primary(res); 315207348Spjd } else { 316207348Spjd res->hr_role = HAST_ROLE_INIT; 317207348Spjd pjdlog_info("Changing resource role back to %s.", 318207348Spjd role2str(res->hr_role)); 319207348Spjd } 320204076Spjd } 321204076Spjd pjdlog_prefix_set("%s", ""); 322204076Spjd } 323204076Spjd} 324204076Spjd 325210886Spjdstatic bool 326210886Spjdresource_needs_restart(const struct hast_resource *res0, 327210886Spjd const struct hast_resource *res1) 328210886Spjd{ 329210886Spjd 330218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0); 331210886Spjd 332210886Spjd if (strcmp(res0->hr_provname, res1->hr_provname) != 0) 333210886Spjd return (true); 334210886Spjd if (strcmp(res0->hr_localpath, res1->hr_localpath) != 0) 335210886Spjd return (true); 336210886Spjd if (res0->hr_role == HAST_ROLE_INIT || 337210886Spjd res0->hr_role == HAST_ROLE_SECONDARY) { 338210886Spjd if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 339210886Spjd return (true); 340210886Spjd if (res0->hr_replication != res1->hr_replication) 341210886Spjd return (true); 342210886Spjd if (res0->hr_timeout != res1->hr_timeout) 343210886Spjd return (true); 344211886Spjd if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 345211886Spjd return (true); 346210886Spjd } 347210886Spjd return (false); 348210886Spjd} 349210886Spjd 350210886Spjdstatic bool 351210886Spjdresource_needs_reload(const struct hast_resource *res0, 352210886Spjd const struct hast_resource *res1) 353210886Spjd{ 354210886Spjd 355218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0); 356218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_provname, res1->hr_provname) == 0); 357218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_localpath, res1->hr_localpath) == 0); 358210886Spjd 359210886Spjd if (res0->hr_role != HAST_ROLE_PRIMARY) 360210886Spjd return (false); 361210886Spjd 362210886Spjd if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 363210886Spjd return (true); 364210886Spjd if (res0->hr_replication != res1->hr_replication) 365210886Spjd return (true); 366210886Spjd if (res0->hr_timeout != res1->hr_timeout) 367210886Spjd return (true); 368211886Spjd if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 369211886Spjd return (true); 370210886Spjd return (false); 371210886Spjd} 372210886Spjd 373204076Spjdstatic void 374217784Spjdresource_reload(const struct hast_resource *res) 375217784Spjd{ 376217784Spjd struct nv *nvin, *nvout; 377217784Spjd int error; 378217784Spjd 379218138Spjd PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY); 380217784Spjd 381217784Spjd nvout = nv_alloc(); 382217784Spjd nv_add_uint8(nvout, HASTCTL_RELOAD, "cmd"); 383217784Spjd nv_add_string(nvout, res->hr_remoteaddr, "remoteaddr"); 384217784Spjd nv_add_int32(nvout, (int32_t)res->hr_replication, "replication"); 385217784Spjd nv_add_int32(nvout, (int32_t)res->hr_timeout, "timeout"); 386217784Spjd nv_add_string(nvout, res->hr_exec, "exec"); 387217784Spjd if (nv_error(nvout) != 0) { 388217784Spjd nv_free(nvout); 389217784Spjd pjdlog_error("Unable to allocate header for reload message."); 390217784Spjd return; 391217784Spjd } 392217784Spjd if (hast_proto_send(res, res->hr_ctrl, nvout, NULL, 0) < 0) { 393217784Spjd pjdlog_errno(LOG_ERR, "Unable to send reload message"); 394217784Spjd nv_free(nvout); 395217784Spjd return; 396217784Spjd } 397217784Spjd nv_free(nvout); 398217784Spjd 399217784Spjd /* Receive response. */ 400217784Spjd if (hast_proto_recv_hdr(res->hr_ctrl, &nvin) < 0) { 401217784Spjd pjdlog_errno(LOG_ERR, "Unable to receive reload reply"); 402217784Spjd return; 403217784Spjd } 404217784Spjd error = nv_get_int16(nvin, "error"); 405217784Spjd nv_free(nvin); 406217784Spjd if (error != 0) { 407217784Spjd pjdlog_common(LOG_ERR, 0, error, "Reload failed"); 408217784Spjd return; 409217784Spjd } 410217784Spjd} 411217784Spjd 412217784Spjdstatic void 413204076Spjdhastd_reload(void) 414204076Spjd{ 415210886Spjd struct hastd_config *newcfg; 416210886Spjd struct hast_resource *nres, *cres, *tres; 417210886Spjd uint8_t role; 418204076Spjd 419210886Spjd pjdlog_info("Reloading configuration..."); 420210886Spjd 421210886Spjd newcfg = yy_config_parse(cfgpath, false); 422210886Spjd if (newcfg == NULL) 423210886Spjd goto failed; 424210886Spjd 425210886Spjd /* 426210886Spjd * Check if control address has changed. 427210886Spjd */ 428210886Spjd if (strcmp(cfg->hc_controladdr, newcfg->hc_controladdr) != 0) { 429210886Spjd if (proto_server(newcfg->hc_controladdr, 430210886Spjd &newcfg->hc_controlconn) < 0) { 431210886Spjd pjdlog_errno(LOG_ERR, 432210886Spjd "Unable to listen on control address %s", 433210886Spjd newcfg->hc_controladdr); 434210886Spjd goto failed; 435210886Spjd } 436210886Spjd } 437210886Spjd /* 438210886Spjd * Check if listen address has changed. 439210886Spjd */ 440210886Spjd if (strcmp(cfg->hc_listenaddr, newcfg->hc_listenaddr) != 0) { 441210886Spjd if (proto_server(newcfg->hc_listenaddr, 442210886Spjd &newcfg->hc_listenconn) < 0) { 443210886Spjd pjdlog_errno(LOG_ERR, "Unable to listen on address %s", 444210886Spjd newcfg->hc_listenaddr); 445210886Spjd goto failed; 446210886Spjd } 447210886Spjd } 448210886Spjd /* 449210886Spjd * Only when both control and listen sockets are successfully 450210886Spjd * initialized switch them to new configuration. 451210886Spjd */ 452210886Spjd if (newcfg->hc_controlconn != NULL) { 453210886Spjd pjdlog_info("Control socket changed from %s to %s.", 454210886Spjd cfg->hc_controladdr, newcfg->hc_controladdr); 455210886Spjd proto_close(cfg->hc_controlconn); 456210886Spjd cfg->hc_controlconn = newcfg->hc_controlconn; 457210886Spjd newcfg->hc_controlconn = NULL; 458210886Spjd strlcpy(cfg->hc_controladdr, newcfg->hc_controladdr, 459210886Spjd sizeof(cfg->hc_controladdr)); 460210886Spjd } 461210886Spjd if (newcfg->hc_listenconn != NULL) { 462210886Spjd pjdlog_info("Listen socket changed from %s to %s.", 463210886Spjd cfg->hc_listenaddr, newcfg->hc_listenaddr); 464210886Spjd proto_close(cfg->hc_listenconn); 465210886Spjd cfg->hc_listenconn = newcfg->hc_listenconn; 466210886Spjd newcfg->hc_listenconn = NULL; 467210886Spjd strlcpy(cfg->hc_listenaddr, newcfg->hc_listenaddr, 468210886Spjd sizeof(cfg->hc_listenaddr)); 469210886Spjd } 470210886Spjd 471210886Spjd /* 472210886Spjd * Stop and remove resources that were removed from the configuration. 473210886Spjd */ 474210886Spjd TAILQ_FOREACH_SAFE(cres, &cfg->hc_resources, hr_next, tres) { 475210886Spjd TAILQ_FOREACH(nres, &newcfg->hc_resources, hr_next) { 476210886Spjd if (strcmp(cres->hr_name, nres->hr_name) == 0) 477210886Spjd break; 478210886Spjd } 479210886Spjd if (nres == NULL) { 480210886Spjd control_set_role(cres, HAST_ROLE_INIT); 481210886Spjd TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 482210886Spjd pjdlog_info("Resource %s removed.", cres->hr_name); 483210886Spjd free(cres); 484210886Spjd } 485210886Spjd } 486210886Spjd /* 487210886Spjd * Move new resources to the current configuration. 488210886Spjd */ 489210886Spjd TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 490210886Spjd TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 491210886Spjd if (strcmp(cres->hr_name, nres->hr_name) == 0) 492210886Spjd break; 493210886Spjd } 494210886Spjd if (cres == NULL) { 495210886Spjd TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 496210886Spjd TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 497210886Spjd pjdlog_info("Resource %s added.", nres->hr_name); 498210886Spjd } 499210886Spjd } 500210886Spjd /* 501210886Spjd * Deal with modified resources. 502210886Spjd * Depending on what has changed exactly we might want to perform 503210886Spjd * different actions. 504210886Spjd * 505210886Spjd * We do full resource restart in the following situations: 506210886Spjd * Resource role is INIT or SECONDARY. 507210886Spjd * Resource role is PRIMARY and path to local component or provider 508210886Spjd * name has changed. 509210886Spjd * In case of PRIMARY, the worker process will be killed and restarted, 510210886Spjd * which also means removing /dev/hast/<name> provider and 511210886Spjd * recreating it. 512210886Spjd * 513210886Spjd * We do just reload (send SIGHUP to worker process) if we act as 514217729Spjd * PRIMARY, but only if remote address, replication mode, timeout or 515217729Spjd * execution path has changed. For those, there is no need to restart 516217729Spjd * worker process. 517210886Spjd * If PRIMARY receives SIGHUP, it will reconnect if remote address or 518210886Spjd * replication mode has changed or simply set new timeout if only 519210886Spjd * timeout has changed. 520210886Spjd */ 521210886Spjd TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 522210886Spjd TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 523210886Spjd if (strcmp(cres->hr_name, nres->hr_name) == 0) 524210886Spjd break; 525210886Spjd } 526218138Spjd PJDLOG_ASSERT(cres != NULL); 527210886Spjd if (resource_needs_restart(cres, nres)) { 528210886Spjd pjdlog_info("Resource %s configuration was modified, restarting it.", 529210886Spjd cres->hr_name); 530210886Spjd role = cres->hr_role; 531210886Spjd control_set_role(cres, HAST_ROLE_INIT); 532210886Spjd TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 533210886Spjd free(cres); 534210886Spjd TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 535210886Spjd TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 536210886Spjd control_set_role(nres, role); 537210886Spjd } else if (resource_needs_reload(cres, nres)) { 538210886Spjd pjdlog_info("Resource %s configuration was modified, reloading it.", 539210886Spjd cres->hr_name); 540210886Spjd strlcpy(cres->hr_remoteaddr, nres->hr_remoteaddr, 541210886Spjd sizeof(cres->hr_remoteaddr)); 542210886Spjd cres->hr_replication = nres->hr_replication; 543210886Spjd cres->hr_timeout = nres->hr_timeout; 544217729Spjd strlcpy(cres->hr_exec, nres->hr_exec, 545217729Spjd sizeof(cres->hr_exec)); 546217784Spjd if (cres->hr_workerpid != 0) 547217784Spjd resource_reload(cres); 548210886Spjd } 549210886Spjd } 550210886Spjd 551210886Spjd yy_config_free(newcfg); 552210886Spjd pjdlog_info("Configuration reloaded successfully."); 553210886Spjd return; 554210886Spjdfailed: 555210886Spjd if (newcfg != NULL) { 556210886Spjd if (newcfg->hc_controlconn != NULL) 557210886Spjd proto_close(newcfg->hc_controlconn); 558210886Spjd if (newcfg->hc_listenconn != NULL) 559210886Spjd proto_close(newcfg->hc_listenconn); 560210886Spjd yy_config_free(newcfg); 561210886Spjd } 562210886Spjd pjdlog_warning("Configuration not reloaded."); 563204076Spjd} 564204076Spjd 565204076Spjdstatic void 566211899Spjdterminate_workers(void) 567211899Spjd{ 568211899Spjd struct hast_resource *res; 569211899Spjd 570211899Spjd pjdlog_info("Termination signal received, exiting."); 571211899Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 572211899Spjd if (res->hr_workerpid == 0) 573211899Spjd continue; 574211899Spjd pjdlog_info("Terminating worker process (resource=%s, role=%s, pid=%u).", 575211899Spjd res->hr_name, role2str(res->hr_role), res->hr_workerpid); 576211899Spjd if (kill(res->hr_workerpid, SIGTERM) == 0) 577211899Spjd continue; 578211899Spjd pjdlog_errno(LOG_WARNING, 579211899Spjd "Unable to send signal to worker process (resource=%s, role=%s, pid=%u).", 580211899Spjd res->hr_name, role2str(res->hr_role), res->hr_workerpid); 581211899Spjd } 582211899Spjd} 583211899Spjd 584211899Spjdstatic void 585204076Spjdlisten_accept(void) 586204076Spjd{ 587204076Spjd struct hast_resource *res; 588204076Spjd struct proto_conn *conn; 589204076Spjd struct nv *nvin, *nvout, *nverr; 590204076Spjd const char *resname; 591204076Spjd const unsigned char *token; 592204076Spjd char laddr[256], raddr[256]; 593204076Spjd size_t size; 594204076Spjd pid_t pid; 595204076Spjd int status; 596204076Spjd 597204076Spjd proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr)); 598204076Spjd pjdlog_debug(1, "Accepting connection to %s.", laddr); 599204076Spjd 600204076Spjd if (proto_accept(cfg->hc_listenconn, &conn) < 0) { 601204076Spjd pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr); 602204076Spjd return; 603204076Spjd } 604204076Spjd 605204076Spjd proto_local_address(conn, laddr, sizeof(laddr)); 606204076Spjd proto_remote_address(conn, raddr, sizeof(raddr)); 607209185Spjd pjdlog_info("Connection from %s to %s.", raddr, laddr); 608204076Spjd 609207371Spjd /* Error in setting timeout is not critical, but why should it fail? */ 610207371Spjd if (proto_timeout(conn, HAST_TIMEOUT) < 0) 611207371Spjd pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); 612207371Spjd 613204076Spjd nvin = nvout = nverr = NULL; 614204076Spjd 615204076Spjd /* 616204076Spjd * Before receiving any data see if remote host have access to any 617204076Spjd * resource. 618204076Spjd */ 619204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 620204076Spjd if (proto_address_match(conn, res->hr_remoteaddr)) 621204076Spjd break; 622204076Spjd } 623204076Spjd if (res == NULL) { 624204076Spjd pjdlog_error("Client %s isn't known.", raddr); 625204076Spjd goto close; 626204076Spjd } 627204076Spjd /* Ok, remote host can access at least one resource. */ 628204076Spjd 629204076Spjd if (hast_proto_recv_hdr(conn, &nvin) < 0) { 630204076Spjd pjdlog_errno(LOG_ERR, "Unable to receive header from %s", 631204076Spjd raddr); 632204076Spjd goto close; 633204076Spjd } 634204076Spjd 635204076Spjd resname = nv_get_string(nvin, "resource"); 636204076Spjd if (resname == NULL) { 637204076Spjd pjdlog_error("No 'resource' field in the header received from %s.", 638204076Spjd raddr); 639204076Spjd goto close; 640204076Spjd } 641204076Spjd pjdlog_debug(2, "%s: resource=%s", raddr, resname); 642204076Spjd token = nv_get_uint8_array(nvin, &size, "token"); 643204076Spjd /* 644204076Spjd * NULL token means that this is first conection. 645204076Spjd */ 646204076Spjd if (token != NULL && size != sizeof(res->hr_token)) { 647204076Spjd pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).", 648204076Spjd raddr, sizeof(res->hr_token), size); 649204076Spjd goto close; 650204076Spjd } 651204076Spjd 652204076Spjd /* 653204076Spjd * From now on we want to send errors to the remote node. 654204076Spjd */ 655204076Spjd nverr = nv_alloc(); 656204076Spjd 657204076Spjd /* Find resource related to this connection. */ 658204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 659204076Spjd if (strcmp(resname, res->hr_name) == 0) 660204076Spjd break; 661204076Spjd } 662204076Spjd /* Have we found the resource? */ 663204076Spjd if (res == NULL) { 664204076Spjd pjdlog_error("No resource '%s' as requested by %s.", 665204076Spjd resname, raddr); 666204076Spjd nv_add_stringf(nverr, "errmsg", "Resource not configured."); 667204076Spjd goto fail; 668204076Spjd } 669204076Spjd 670204076Spjd /* Now that we know resource name setup log prefix. */ 671204076Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 672204076Spjd 673204076Spjd /* Does the remote host have access to this resource? */ 674204076Spjd if (!proto_address_match(conn, res->hr_remoteaddr)) { 675204076Spjd pjdlog_error("Client %s has no access to the resource.", raddr); 676204076Spjd nv_add_stringf(nverr, "errmsg", "No access to the resource."); 677204076Spjd goto fail; 678204076Spjd } 679204076Spjd /* Is the resource marked as secondary? */ 680204076Spjd if (res->hr_role != HAST_ROLE_SECONDARY) { 681204076Spjd pjdlog_error("We act as %s for the resource and not as %s as requested by %s.", 682204076Spjd role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY), 683204076Spjd raddr); 684204076Spjd nv_add_stringf(nverr, "errmsg", 685204076Spjd "Remote node acts as %s for the resource and not as %s.", 686204076Spjd role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY)); 687204076Spjd goto fail; 688204076Spjd } 689204076Spjd /* Does token (if exists) match? */ 690204076Spjd if (token != NULL && memcmp(token, res->hr_token, 691204076Spjd sizeof(res->hr_token)) != 0) { 692204076Spjd pjdlog_error("Token received from %s doesn't match.", raddr); 693209185Spjd nv_add_stringf(nverr, "errmsg", "Token doesn't match."); 694204076Spjd goto fail; 695204076Spjd } 696204076Spjd /* 697204076Spjd * If there is no token, but we have half-open connection 698204076Spjd * (only remotein) or full connection (worker process is running) 699204076Spjd * we have to cancel those and accept the new connection. 700204076Spjd */ 701204076Spjd if (token == NULL) { 702218138Spjd PJDLOG_ASSERT(res->hr_remoteout == NULL); 703204076Spjd pjdlog_debug(1, "Initial connection from %s.", raddr); 704204076Spjd if (res->hr_workerpid != 0) { 705218138Spjd PJDLOG_ASSERT(res->hr_remotein == NULL); 706204076Spjd pjdlog_debug(1, 707204076Spjd "Worker process exists (pid=%u), stopping it.", 708204076Spjd (unsigned int)res->hr_workerpid); 709204076Spjd /* Stop child process. */ 710204076Spjd if (kill(res->hr_workerpid, SIGINT) < 0) { 711204076Spjd pjdlog_errno(LOG_ERR, 712204076Spjd "Unable to stop worker process (pid=%u)", 713204076Spjd (unsigned int)res->hr_workerpid); 714204076Spjd /* 715204076Spjd * Other than logging the problem we 716204076Spjd * ignore it - nothing smart to do. 717204076Spjd */ 718204076Spjd } 719204076Spjd /* Wait for it to exit. */ 720204076Spjd else if ((pid = waitpid(res->hr_workerpid, 721204076Spjd &status, 0)) != res->hr_workerpid) { 722207372Spjd /* We can only log the problem. */ 723204076Spjd pjdlog_errno(LOG_ERR, 724204076Spjd "Waiting for worker process (pid=%u) failed", 725204076Spjd (unsigned int)res->hr_workerpid); 726204076Spjd } else { 727207372Spjd child_exit_log(res->hr_workerpid, status); 728204076Spjd } 729213006Spjd child_cleanup(res); 730204076Spjd } else if (res->hr_remotein != NULL) { 731204076Spjd char oaddr[256]; 732204076Spjd 733213981Spjd proto_remote_address(res->hr_remotein, oaddr, 734213981Spjd sizeof(oaddr)); 735204076Spjd pjdlog_debug(1, 736204076Spjd "Canceling half-open connection from %s on connection from %s.", 737204076Spjd oaddr, raddr); 738204076Spjd proto_close(res->hr_remotein); 739204076Spjd res->hr_remotein = NULL; 740204076Spjd } 741204076Spjd } 742204076Spjd 743204076Spjd /* 744204076Spjd * Checks and cleanups are done. 745204076Spjd */ 746204076Spjd 747204076Spjd if (token == NULL) { 748204076Spjd arc4random_buf(res->hr_token, sizeof(res->hr_token)); 749204076Spjd nvout = nv_alloc(); 750204076Spjd nv_add_uint8_array(nvout, res->hr_token, 751204076Spjd sizeof(res->hr_token), "token"); 752204076Spjd if (nv_error(nvout) != 0) { 753204076Spjd pjdlog_common(LOG_ERR, 0, nv_error(nvout), 754204076Spjd "Unable to prepare return header for %s", raddr); 755204076Spjd nv_add_stringf(nverr, "errmsg", 756204076Spjd "Remote node was unable to prepare return header: %s.", 757204076Spjd strerror(nv_error(nvout))); 758204076Spjd goto fail; 759204076Spjd } 760204076Spjd if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) { 761204076Spjd int error = errno; 762204076Spjd 763204076Spjd pjdlog_errno(LOG_ERR, "Unable to send response to %s", 764204076Spjd raddr); 765204076Spjd nv_add_stringf(nverr, "errmsg", 766204076Spjd "Remote node was unable to send response: %s.", 767204076Spjd strerror(error)); 768204076Spjd goto fail; 769204076Spjd } 770204076Spjd res->hr_remotein = conn; 771204076Spjd pjdlog_debug(1, "Incoming connection from %s configured.", 772204076Spjd raddr); 773204076Spjd } else { 774204076Spjd res->hr_remoteout = conn; 775204076Spjd pjdlog_debug(1, "Outgoing connection to %s configured.", raddr); 776204076Spjd hastd_secondary(res, nvin); 777204076Spjd } 778204076Spjd nv_free(nvin); 779204076Spjd nv_free(nvout); 780204076Spjd nv_free(nverr); 781204076Spjd pjdlog_prefix_set("%s", ""); 782204076Spjd return; 783204076Spjdfail: 784204076Spjd if (nv_error(nverr) != 0) { 785204076Spjd pjdlog_common(LOG_ERR, 0, nv_error(nverr), 786204076Spjd "Unable to prepare error header for %s", raddr); 787204076Spjd goto close; 788204076Spjd } 789204076Spjd if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) { 790204076Spjd pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr); 791204076Spjd goto close; 792204076Spjd } 793204076Spjdclose: 794204076Spjd if (nvin != NULL) 795204076Spjd nv_free(nvin); 796204076Spjd if (nvout != NULL) 797204076Spjd nv_free(nvout); 798204076Spjd if (nverr != NULL) 799204076Spjd nv_free(nverr); 800204076Spjd proto_close(conn); 801204076Spjd pjdlog_prefix_set("%s", ""); 802204076Spjd} 803204076Spjd 804204076Spjdstatic void 805204076Spjdmain_loop(void) 806204076Spjd{ 807212038Spjd struct hast_resource *res; 808213009Spjd struct timeval seltimeout; 809213009Spjd struct timespec sigtimeout; 810213009Spjd int fd, maxfd, ret, signo; 811213009Spjd sigset_t mask; 812212037Spjd fd_set rfds; 813204076Spjd 814213009Spjd seltimeout.tv_sec = REPORT_INTERVAL; 815213009Spjd seltimeout.tv_usec = 0; 816213009Spjd sigtimeout.tv_sec = 0; 817213009Spjd sigtimeout.tv_nsec = 0; 818211977Spjd 819213009Spjd PJDLOG_VERIFY(sigemptyset(&mask) == 0); 820213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0); 821213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0); 822213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0); 823213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0); 824213009Spjd 825216477Spjd pjdlog_info("Started successfully, running protocol version %d.", 826216477Spjd HAST_PROTO_VERSION); 827216477Spjd 828204076Spjd for (;;) { 829213009Spjd while ((signo = sigtimedwait(&mask, NULL, &sigtimeout)) != -1) { 830213009Spjd switch (signo) { 831213009Spjd case SIGINT: 832213009Spjd case SIGTERM: 833213009Spjd sigexit_received = true; 834213009Spjd terminate_workers(); 835217967Spjd proto_close(cfg->hc_controlconn); 836213009Spjd exit(EX_OK); 837213009Spjd break; 838213009Spjd case SIGCHLD: 839213009Spjd child_exit(); 840213009Spjd break; 841213009Spjd case SIGHUP: 842213009Spjd hastd_reload(); 843213009Spjd break; 844213009Spjd default: 845218138Spjd PJDLOG_ABORT("Unexpected signal (%d).", signo); 846213009Spjd } 847211899Spjd } 848204076Spjd 849209177Spjd /* Setup descriptors for select(2). */ 850204076Spjd FD_ZERO(&rfds); 851212038Spjd maxfd = fd = proto_descriptor(cfg->hc_controlconn); 852218138Spjd PJDLOG_ASSERT(fd >= 0); 853212038Spjd FD_SET(fd, &rfds); 854212038Spjd fd = proto_descriptor(cfg->hc_listenconn); 855218138Spjd PJDLOG_ASSERT(fd >= 0); 856212038Spjd FD_SET(fd, &rfds); 857212038Spjd maxfd = fd > maxfd ? fd : maxfd; 858212038Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 859212038Spjd if (res->hr_event == NULL) 860212038Spjd continue; 861212038Spjd fd = proto_descriptor(res->hr_event); 862218138Spjd PJDLOG_ASSERT(fd >= 0); 863212038Spjd FD_SET(fd, &rfds); 864212038Spjd maxfd = fd > maxfd ? fd : maxfd; 865212038Spjd } 866204076Spjd 867218138Spjd PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE); 868213009Spjd ret = select(maxfd + 1, &rfds, NULL, NULL, &seltimeout); 869211977Spjd if (ret == 0) 870213429Spjd hook_check(); 871211977Spjd else if (ret == -1) { 872204076Spjd if (errno == EINTR) 873204076Spjd continue; 874204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 875204076Spjd pjdlog_exit(EX_OSERR, "select() failed"); 876204076Spjd } 877204076Spjd 878212038Spjd if (FD_ISSET(proto_descriptor(cfg->hc_controlconn), &rfds)) 879204076Spjd control_handle(cfg); 880212038Spjd if (FD_ISSET(proto_descriptor(cfg->hc_listenconn), &rfds)) 881204076Spjd listen_accept(); 882212038Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 883212038Spjd if (res->hr_event == NULL) 884212038Spjd continue; 885212038Spjd if (FD_ISSET(proto_descriptor(res->hr_event), &rfds)) { 886212038Spjd if (event_recv(res) == 0) 887212038Spjd continue; 888212038Spjd /* The worker process exited? */ 889212038Spjd proto_close(res->hr_event); 890212038Spjd res->hr_event = NULL; 891212038Spjd } 892212038Spjd } 893204076Spjd } 894204076Spjd} 895204076Spjd 896213428Spjdstatic void 897213428Spjddummy_sighandler(int sig __unused) 898213428Spjd{ 899213428Spjd /* Nothing to do. */ 900213428Spjd} 901213428Spjd 902204076Spjdint 903204076Spjdmain(int argc, char *argv[]) 904204076Spjd{ 905204076Spjd const char *pidfile; 906204076Spjd pid_t otherpid; 907204076Spjd bool foreground; 908204076Spjd int debuglevel; 909213009Spjd sigset_t mask; 910204076Spjd 911204076Spjd foreground = false; 912204076Spjd debuglevel = 0; 913204076Spjd pidfile = HASTD_PIDFILE; 914204076Spjd 915204076Spjd for (;;) { 916204076Spjd int ch; 917204076Spjd 918204076Spjd ch = getopt(argc, argv, "c:dFhP:"); 919204076Spjd if (ch == -1) 920204076Spjd break; 921204076Spjd switch (ch) { 922204076Spjd case 'c': 923204076Spjd cfgpath = optarg; 924204076Spjd break; 925204076Spjd case 'd': 926204076Spjd debuglevel++; 927204076Spjd break; 928204076Spjd case 'F': 929204076Spjd foreground = true; 930204076Spjd break; 931204076Spjd case 'P': 932204076Spjd pidfile = optarg; 933204076Spjd break; 934204076Spjd case 'h': 935204076Spjd default: 936204076Spjd usage(); 937204076Spjd } 938204076Spjd } 939204076Spjd argc -= optind; 940204076Spjd argv += optind; 941204076Spjd 942217965Spjd pjdlog_init(PJDLOG_MODE_STD); 943204076Spjd pjdlog_debug_set(debuglevel); 944204076Spjd 945214273Spjd g_gate_load(); 946214273Spjd 947204076Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 948204076Spjd if (pfh == NULL) { 949204076Spjd if (errno == EEXIST) { 950204076Spjd pjdlog_exitx(EX_TEMPFAIL, 951204076Spjd "Another hastd is already running, pid: %jd.", 952204076Spjd (intmax_t)otherpid); 953204076Spjd } 954204076Spjd /* If we cannot create pidfile from other reasons, only warn. */ 955210879Spjd pjdlog_errno(LOG_WARNING, "Unable to open or create pidfile"); 956204076Spjd } 957204076Spjd 958210883Spjd cfg = yy_config_parse(cfgpath, true); 959218138Spjd PJDLOG_ASSERT(cfg != NULL); 960204076Spjd 961213428Spjd /* 962217307Spjd * Restore default actions for interesting signals in case parent 963217307Spjd * process (like init(8)) decided to ignore some of them (like SIGHUP). 964217307Spjd */ 965217307Spjd PJDLOG_VERIFY(signal(SIGHUP, SIG_DFL) != SIG_ERR); 966217307Spjd PJDLOG_VERIFY(signal(SIGINT, SIG_DFL) != SIG_ERR); 967217307Spjd PJDLOG_VERIFY(signal(SIGTERM, SIG_DFL) != SIG_ERR); 968217307Spjd /* 969213428Spjd * Because SIGCHLD is ignored by default, setup dummy handler for it, 970213428Spjd * so we can mask it. 971213428Spjd */ 972213428Spjd PJDLOG_VERIFY(signal(SIGCHLD, dummy_sighandler) != SIG_ERR); 973217307Spjd 974213009Spjd PJDLOG_VERIFY(sigemptyset(&mask) == 0); 975213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0); 976213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0); 977213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0); 978213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0); 979213009Spjd PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); 980204076Spjd 981204076Spjd /* Listen on control address. */ 982204076Spjd if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) { 983204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 984204076Spjd pjdlog_exit(EX_OSERR, "Unable to listen on control address %s", 985204076Spjd cfg->hc_controladdr); 986204076Spjd } 987204076Spjd /* Listen for remote connections. */ 988204076Spjd if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) { 989204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 990204076Spjd pjdlog_exit(EX_OSERR, "Unable to listen on address %s", 991204076Spjd cfg->hc_listenaddr); 992204076Spjd } 993204076Spjd 994204076Spjd if (!foreground) { 995204076Spjd if (daemon(0, 0) < 0) { 996204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 997204076Spjd pjdlog_exit(EX_OSERR, "Unable to daemonize"); 998204076Spjd } 999204076Spjd 1000204076Spjd /* Start logging to syslog. */ 1001204076Spjd pjdlog_mode_set(PJDLOG_MODE_SYSLOG); 1002204076Spjd 1003204076Spjd /* Write PID to a file. */ 1004204076Spjd if (pidfile_write(pfh) < 0) { 1005204076Spjd pjdlog_errno(LOG_WARNING, 1006204076Spjd "Unable to write PID to a file"); 1007204076Spjd } 1008204076Spjd } 1009204076Spjd 1010211977Spjd hook_init(); 1011211977Spjd 1012204076Spjd main_loop(); 1013204076Spjd 1014204076Spjd exit(0); 1015204076Spjd} 1016