1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3219351Spjd * Copyright (c) 2010-2011 Pawel Jakub Dawidek <pawel@dawidek.net> 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$"); 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> 49219813Spjd#include <time.h> 50204076Spjd#include <unistd.h> 51204076Spjd 52204076Spjd#include <activemap.h> 53204076Spjd#include <pjdlog.h> 54204076Spjd 55204076Spjd#include "control.h" 56212038Spjd#include "event.h" 57204076Spjd#include "hast.h" 58204076Spjd#include "hast_proto.h" 59204076Spjd#include "hastd.h" 60211977Spjd#include "hooks.h" 61204076Spjd#include "subr.h" 62204076Spjd 63204076Spjd/* Path to configuration file. */ 64210886Spjdconst char *cfgpath = HAST_CONFIG; 65204076Spjd/* Hastd configuration. */ 66204076Spjdstatic struct hastd_config *cfg; 67204076Spjd/* Was SIGINT or SIGTERM signal received? */ 68204076Spjdbool sigexit_received = false; 69234294Strociny/* Path to pidfile. */ 70234294Strocinystatic const char *pidfile; 71249236Strociny/* Pidfile handle. */ 72204076Spjdstruct pidfh *pfh; 73231017Strociny/* Do we run in foreground? */ 74231017Strocinystatic bool foreground; 75204076Spjd 76211977Spjd/* How often check for hooks running for too long. */ 77213430Spjd#define REPORT_INTERVAL 5 78211977Spjd 79204076Spjdstatic void 80204076Spjdusage(void) 81204076Spjd{ 82204076Spjd 83204076Spjd errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]"); 84204076Spjd} 85204076Spjd 86204076Spjdstatic void 87204076Spjdg_gate_load(void) 88204076Spjd{ 89204076Spjd 90204076Spjd if (modfind("g_gate") == -1) { 91204076Spjd /* Not present in kernel, try loading it. */ 92204076Spjd if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) { 93204076Spjd if (errno != EEXIST) { 94204076Spjd pjdlog_exit(EX_OSERR, 95204076Spjd "Unable to load geom_gate module"); 96204076Spjd } 97204076Spjd } 98204076Spjd } 99204076Spjd} 100204076Spjd 101218041Spjdvoid 102218041Spjddescriptors_cleanup(struct hast_resource *res) 103218041Spjd{ 104231017Strociny struct hast_resource *tres, *tmres; 105222108Spjd struct hastd_listen *lst; 106218041Spjd 107231017Strociny TAILQ_FOREACH_SAFE(tres, &cfg->hc_resources, hr_next, tmres) { 108218041Spjd if (tres == res) { 109218041Spjd PJDLOG_VERIFY(res->hr_role == HAST_ROLE_SECONDARY || 110218041Spjd (res->hr_remotein == NULL && 111218041Spjd res->hr_remoteout == NULL)); 112218041Spjd continue; 113218041Spjd } 114218041Spjd if (tres->hr_remotein != NULL) 115218041Spjd proto_close(tres->hr_remotein); 116218041Spjd if (tres->hr_remoteout != NULL) 117218041Spjd proto_close(tres->hr_remoteout); 118218370Spjd if (tres->hr_ctrl != NULL) 119218370Spjd proto_close(tres->hr_ctrl); 120218370Spjd if (tres->hr_event != NULL) 121218370Spjd proto_close(tres->hr_event); 122218370Spjd if (tres->hr_conn != NULL) 123218370Spjd proto_close(tres->hr_conn); 124231017Strociny TAILQ_REMOVE(&cfg->hc_resources, tres, hr_next); 125231017Strociny free(tres); 126218041Spjd } 127218041Spjd if (cfg->hc_controlin != NULL) 128218041Spjd proto_close(cfg->hc_controlin); 129218041Spjd proto_close(cfg->hc_controlconn); 130231017Strociny while ((lst = TAILQ_FIRST(&cfg->hc_listen)) != NULL) { 131231017Strociny TAILQ_REMOVE(&cfg->hc_listen, lst, hl_next); 132222108Spjd if (lst->hl_conn != NULL) 133222108Spjd proto_close(lst->hl_conn); 134231017Strociny free(lst); 135222108Spjd } 136218041Spjd (void)pidfile_close(pfh); 137218041Spjd hook_fini(); 138218041Spjd pjdlog_fini(); 139218041Spjd} 140218041Spjd 141218044Spjdstatic const char * 142218044Spjddtype2str(mode_t mode) 143218044Spjd{ 144218044Spjd 145218044Spjd if (S_ISBLK(mode)) 146218044Spjd return ("block device"); 147219864Spjd else if (S_ISCHR(mode)) 148218044Spjd return ("character device"); 149219864Spjd else if (S_ISDIR(mode)) 150218044Spjd return ("directory"); 151218044Spjd else if (S_ISFIFO(mode)) 152218044Spjd return ("pipe or FIFO"); 153219864Spjd else if (S_ISLNK(mode)) 154218044Spjd return ("symbolic link"); 155219864Spjd else if (S_ISREG(mode)) 156218044Spjd return ("regular file"); 157218044Spjd else if (S_ISSOCK(mode)) 158218044Spjd return ("socket"); 159219864Spjd else if (S_ISWHT(mode)) 160218044Spjd return ("whiteout"); 161218044Spjd else 162218044Spjd return ("unknown"); 163218044Spjd} 164218044Spjd 165218044Spjdvoid 166218044Spjddescriptors_assert(const struct hast_resource *res, int pjdlogmode) 167218044Spjd{ 168218044Spjd char msg[256]; 169218044Spjd struct stat sb; 170218044Spjd long maxfd; 171218044Spjd bool isopen; 172218044Spjd mode_t mode; 173218044Spjd int fd; 174218044Spjd 175218044Spjd /* 176218044Spjd * At this point descriptor to syslog socket is closed, so if we want 177218044Spjd * to log assertion message, we have to first store it in 'msg' local 178218044Spjd * buffer and then open syslog socket and log it. 179218044Spjd */ 180218044Spjd msg[0] = '\0'; 181218044Spjd 182218044Spjd maxfd = sysconf(_SC_OPEN_MAX); 183231017Strociny if (maxfd == -1) { 184218373Spjd pjdlog_init(pjdlogmode); 185218373Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 186218373Spjd role2str(res->hr_role)); 187218044Spjd pjdlog_errno(LOG_WARNING, "sysconf(_SC_OPEN_MAX) failed"); 188218373Spjd pjdlog_fini(); 189218044Spjd maxfd = 16384; 190218044Spjd } 191218044Spjd for (fd = 0; fd <= maxfd; fd++) { 192218044Spjd if (fstat(fd, &sb) == 0) { 193218044Spjd isopen = true; 194218044Spjd mode = sb.st_mode; 195218044Spjd } else if (errno == EBADF) { 196218044Spjd isopen = false; 197218044Spjd mode = 0; 198218044Spjd } else { 199218375Spjd (void)snprintf(msg, sizeof(msg), 200218044Spjd "Unable to fstat descriptor %d: %s", fd, 201218044Spjd strerror(errno)); 202218374Spjd break; 203218044Spjd } 204218044Spjd if (fd == STDIN_FILENO || fd == STDOUT_FILENO || 205218044Spjd fd == STDERR_FILENO) { 206218044Spjd if (!isopen) { 207218375Spjd (void)snprintf(msg, sizeof(msg), 208218044Spjd "Descriptor %d (%s) is closed, but should be open.", 209218044Spjd fd, (fd == STDIN_FILENO ? "stdin" : 210218044Spjd (fd == STDOUT_FILENO ? "stdout" : "stderr"))); 211218044Spjd break; 212218044Spjd } 213218044Spjd } else if (fd == proto_descriptor(res->hr_event)) { 214218044Spjd if (!isopen) { 215218375Spjd (void)snprintf(msg, sizeof(msg), 216218044Spjd "Descriptor %d (event) is closed, but should be open.", 217218044Spjd fd); 218218044Spjd break; 219218044Spjd } 220218044Spjd if (!S_ISSOCK(mode)) { 221218375Spjd (void)snprintf(msg, sizeof(msg), 222218044Spjd "Descriptor %d (event) is %s, but should be %s.", 223218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 224218044Spjd break; 225218044Spjd } 226218044Spjd } else if (fd == proto_descriptor(res->hr_ctrl)) { 227218044Spjd if (!isopen) { 228218375Spjd (void)snprintf(msg, sizeof(msg), 229218044Spjd "Descriptor %d (ctrl) is closed, but should be open.", 230218044Spjd fd); 231218044Spjd break; 232218044Spjd } 233218044Spjd if (!S_ISSOCK(mode)) { 234218375Spjd (void)snprintf(msg, sizeof(msg), 235218044Spjd "Descriptor %d (ctrl) is %s, but should be %s.", 236218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 237218044Spjd break; 238218044Spjd } 239219900Spjd } else if (res->hr_role == HAST_ROLE_PRIMARY && 240219900Spjd fd == proto_descriptor(res->hr_conn)) { 241218218Spjd if (!isopen) { 242218375Spjd (void)snprintf(msg, sizeof(msg), 243218218Spjd "Descriptor %d (conn) is closed, but should be open.", 244218218Spjd fd); 245218218Spjd break; 246218218Spjd } 247218218Spjd if (!S_ISSOCK(mode)) { 248218375Spjd (void)snprintf(msg, sizeof(msg), 249218218Spjd "Descriptor %d (conn) is %s, but should be %s.", 250218218Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 251218218Spjd break; 252218218Spjd } 253218044Spjd } else if (res->hr_role == HAST_ROLE_SECONDARY && 254219900Spjd res->hr_conn != NULL && 255219900Spjd fd == proto_descriptor(res->hr_conn)) { 256219900Spjd if (isopen) { 257219900Spjd (void)snprintf(msg, sizeof(msg), 258219900Spjd "Descriptor %d (conn) is open, but should be closed.", 259219900Spjd fd); 260219900Spjd break; 261219900Spjd } 262219900Spjd } else if (res->hr_role == HAST_ROLE_SECONDARY && 263218044Spjd fd == proto_descriptor(res->hr_remotein)) { 264218044Spjd if (!isopen) { 265218375Spjd (void)snprintf(msg, sizeof(msg), 266218044Spjd "Descriptor %d (remote in) is closed, but should be open.", 267218044Spjd fd); 268218044Spjd break; 269218044Spjd } 270218044Spjd if (!S_ISSOCK(mode)) { 271218375Spjd (void)snprintf(msg, sizeof(msg), 272218044Spjd "Descriptor %d (remote in) is %s, but should be %s.", 273218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 274218044Spjd break; 275218044Spjd } 276218044Spjd } else if (res->hr_role == HAST_ROLE_SECONDARY && 277218044Spjd fd == proto_descriptor(res->hr_remoteout)) { 278218044Spjd if (!isopen) { 279218375Spjd (void)snprintf(msg, sizeof(msg), 280218044Spjd "Descriptor %d (remote out) is closed, but should be open.", 281218044Spjd fd); 282218044Spjd break; 283218044Spjd } 284218044Spjd if (!S_ISSOCK(mode)) { 285218375Spjd (void)snprintf(msg, sizeof(msg), 286218044Spjd "Descriptor %d (remote out) is %s, but should be %s.", 287218044Spjd fd, dtype2str(mode), dtype2str(S_IFSOCK)); 288218044Spjd break; 289218044Spjd } 290218044Spjd } else { 291218044Spjd if (isopen) { 292218375Spjd (void)snprintf(msg, sizeof(msg), 293218044Spjd "Descriptor %d is open (%s), but should be closed.", 294218044Spjd fd, dtype2str(mode)); 295218044Spjd break; 296218044Spjd } 297218044Spjd } 298218044Spjd } 299218044Spjd if (msg[0] != '\0') { 300218044Spjd pjdlog_init(pjdlogmode); 301218044Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 302218044Spjd role2str(res->hr_role)); 303218044Spjd PJDLOG_ABORT("%s", msg); 304218044Spjd } 305218044Spjd} 306218044Spjd 307204076Spjdstatic void 308207372Spjdchild_exit_log(unsigned int pid, int status) 309207372Spjd{ 310207372Spjd 311207372Spjd if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 312207372Spjd pjdlog_debug(1, "Worker process exited gracefully (pid=%u).", 313207372Spjd pid); 314207372Spjd } else if (WIFSIGNALED(status)) { 315207372Spjd pjdlog_error("Worker process killed (pid=%u, signal=%d).", 316207372Spjd pid, WTERMSIG(status)); 317207372Spjd } else { 318207372Spjd pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).", 319207372Spjd pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1); 320207372Spjd } 321207372Spjd} 322207372Spjd 323207372Spjdstatic void 324204076Spjdchild_exit(void) 325204076Spjd{ 326204076Spjd struct hast_resource *res; 327204076Spjd int status; 328204076Spjd pid_t pid; 329204076Spjd 330204076Spjd while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 331204076Spjd /* Find resource related to the process that just exited. */ 332204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 333204076Spjd if (pid == res->hr_workerpid) 334204076Spjd break; 335204076Spjd } 336204076Spjd if (res == NULL) { 337204076Spjd /* 338204076Spjd * This can happen when new connection arrives and we 339211977Spjd * cancel child responsible for the old one or if this 340211977Spjd * was hook which we executed. 341204076Spjd */ 342211977Spjd hook_check_one(pid, status); 343204076Spjd continue; 344204076Spjd } 345204076Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 346204076Spjd role2str(res->hr_role)); 347207372Spjd child_exit_log(pid, status); 348213006Spjd child_cleanup(res); 349204076Spjd if (res->hr_role == HAST_ROLE_PRIMARY) { 350207372Spjd /* 351207372Spjd * Restart child process if it was killed by signal 352207372Spjd * or exited because of temporary problem. 353207372Spjd */ 354207372Spjd if (WIFSIGNALED(status) || 355207372Spjd (WIFEXITED(status) && 356207372Spjd WEXITSTATUS(status) == EX_TEMPFAIL)) { 357207348Spjd sleep(1); 358207348Spjd pjdlog_info("Restarting worker process."); 359207348Spjd hastd_primary(res); 360207348Spjd } else { 361207348Spjd res->hr_role = HAST_ROLE_INIT; 362207348Spjd pjdlog_info("Changing resource role back to %s.", 363207348Spjd role2str(res->hr_role)); 364207348Spjd } 365204076Spjd } 366204076Spjd pjdlog_prefix_set("%s", ""); 367204076Spjd } 368204076Spjd} 369204076Spjd 370210886Spjdstatic bool 371210886Spjdresource_needs_restart(const struct hast_resource *res0, 372210886Spjd const struct hast_resource *res1) 373210886Spjd{ 374210886Spjd 375218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0); 376210886Spjd 377210886Spjd if (strcmp(res0->hr_provname, res1->hr_provname) != 0) 378210886Spjd return (true); 379210886Spjd if (strcmp(res0->hr_localpath, res1->hr_localpath) != 0) 380210886Spjd return (true); 381210886Spjd if (res0->hr_role == HAST_ROLE_INIT || 382210886Spjd res0->hr_role == HAST_ROLE_SECONDARY) { 383210886Spjd if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 384210886Spjd return (true); 385219818Spjd if (strcmp(res0->hr_sourceaddr, res1->hr_sourceaddr) != 0) 386219818Spjd return (true); 387210886Spjd if (res0->hr_replication != res1->hr_replication) 388210886Spjd return (true); 389219351Spjd if (res0->hr_checksum != res1->hr_checksum) 390219351Spjd return (true); 391219354Spjd if (res0->hr_compression != res1->hr_compression) 392219354Spjd return (true); 393210886Spjd if (res0->hr_timeout != res1->hr_timeout) 394210886Spjd return (true); 395211886Spjd if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 396211886Spjd return (true); 397229509Strociny /* 398229509Strociny * When metaflush has changed we don't really need restart, 399229509Strociny * but it is just easier this way. 400229509Strociny */ 401229509Strociny if (res0->hr_metaflush != res1->hr_metaflush) 402229509Strociny return (true); 403210886Spjd } 404210886Spjd return (false); 405210886Spjd} 406210886Spjd 407210886Spjdstatic bool 408210886Spjdresource_needs_reload(const struct hast_resource *res0, 409210886Spjd const struct hast_resource *res1) 410210886Spjd{ 411210886Spjd 412218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_name, res1->hr_name) == 0); 413218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_provname, res1->hr_provname) == 0); 414218138Spjd PJDLOG_ASSERT(strcmp(res0->hr_localpath, res1->hr_localpath) == 0); 415210886Spjd 416210886Spjd if (res0->hr_role != HAST_ROLE_PRIMARY) 417210886Spjd return (false); 418210886Spjd 419210886Spjd if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 420210886Spjd return (true); 421219818Spjd if (strcmp(res0->hr_sourceaddr, res1->hr_sourceaddr) != 0) 422219818Spjd return (true); 423210886Spjd if (res0->hr_replication != res1->hr_replication) 424210886Spjd return (true); 425219351Spjd if (res0->hr_checksum != res1->hr_checksum) 426219351Spjd return (true); 427219354Spjd if (res0->hr_compression != res1->hr_compression) 428219354Spjd return (true); 429210886Spjd if (res0->hr_timeout != res1->hr_timeout) 430210886Spjd return (true); 431211886Spjd if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 432211886Spjd return (true); 433229509Strociny if (res0->hr_metaflush != res1->hr_metaflush) 434229509Strociny return (true); 435210886Spjd return (false); 436210886Spjd} 437210886Spjd 438204076Spjdstatic void 439217784Spjdresource_reload(const struct hast_resource *res) 440217784Spjd{ 441217784Spjd struct nv *nvin, *nvout; 442217784Spjd int error; 443217784Spjd 444218138Spjd PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY); 445217784Spjd 446217784Spjd nvout = nv_alloc(); 447221076Strociny nv_add_uint8(nvout, CONTROL_RELOAD, "cmd"); 448217784Spjd nv_add_string(nvout, res->hr_remoteaddr, "remoteaddr"); 449219818Spjd nv_add_string(nvout, res->hr_sourceaddr, "sourceaddr"); 450217784Spjd nv_add_int32(nvout, (int32_t)res->hr_replication, "replication"); 451219351Spjd nv_add_int32(nvout, (int32_t)res->hr_checksum, "checksum"); 452219354Spjd nv_add_int32(nvout, (int32_t)res->hr_compression, "compression"); 453217784Spjd nv_add_int32(nvout, (int32_t)res->hr_timeout, "timeout"); 454217784Spjd nv_add_string(nvout, res->hr_exec, "exec"); 455229509Strociny nv_add_int32(nvout, (int32_t)res->hr_metaflush, "metaflush"); 456217784Spjd if (nv_error(nvout) != 0) { 457217784Spjd nv_free(nvout); 458217784Spjd pjdlog_error("Unable to allocate header for reload message."); 459217784Spjd return; 460217784Spjd } 461231017Strociny if (hast_proto_send(res, res->hr_ctrl, nvout, NULL, 0) == -1) { 462217784Spjd pjdlog_errno(LOG_ERR, "Unable to send reload message"); 463217784Spjd nv_free(nvout); 464217784Spjd return; 465217784Spjd } 466217784Spjd nv_free(nvout); 467217784Spjd 468217784Spjd /* Receive response. */ 469231017Strociny if (hast_proto_recv_hdr(res->hr_ctrl, &nvin) == -1) { 470217784Spjd pjdlog_errno(LOG_ERR, "Unable to receive reload reply"); 471217784Spjd return; 472217784Spjd } 473217784Spjd error = nv_get_int16(nvin, "error"); 474217784Spjd nv_free(nvin); 475217784Spjd if (error != 0) { 476217784Spjd pjdlog_common(LOG_ERR, 0, error, "Reload failed"); 477217784Spjd return; 478217784Spjd } 479217784Spjd} 480217784Spjd 481217784Spjdstatic void 482204076Spjdhastd_reload(void) 483204076Spjd{ 484210886Spjd struct hastd_config *newcfg; 485210886Spjd struct hast_resource *nres, *cres, *tres; 486222108Spjd struct hastd_listen *nlst, *clst; 487229509Strociny struct pidfh *newpfh; 488222108Spjd unsigned int nlisten; 489210886Spjd uint8_t role; 490229509Strociny pid_t otherpid; 491204076Spjd 492210886Spjd pjdlog_info("Reloading configuration..."); 493210886Spjd 494229509Strociny newpfh = NULL; 495229509Strociny 496210886Spjd newcfg = yy_config_parse(cfgpath, false); 497210886Spjd if (newcfg == NULL) 498210886Spjd goto failed; 499210886Spjd 500210886Spjd /* 501210886Spjd * Check if control address has changed. 502210886Spjd */ 503210886Spjd if (strcmp(cfg->hc_controladdr, newcfg->hc_controladdr) != 0) { 504210886Spjd if (proto_server(newcfg->hc_controladdr, 505231017Strociny &newcfg->hc_controlconn) == -1) { 506210886Spjd pjdlog_errno(LOG_ERR, 507210886Spjd "Unable to listen on control address %s", 508210886Spjd newcfg->hc_controladdr); 509210886Spjd goto failed; 510210886Spjd } 511210886Spjd } 512210886Spjd /* 513222108Spjd * Check if any listen address has changed. 514210886Spjd */ 515222108Spjd nlisten = 0; 516222108Spjd TAILQ_FOREACH(nlst, &newcfg->hc_listen, hl_next) { 517222108Spjd TAILQ_FOREACH(clst, &cfg->hc_listen, hl_next) { 518222108Spjd if (strcmp(nlst->hl_addr, clst->hl_addr) == 0) 519222108Spjd break; 520210886Spjd } 521222108Spjd if (clst != NULL && clst->hl_conn != NULL) { 522222108Spjd pjdlog_info("Keep listening on address %s.", 523222108Spjd nlst->hl_addr); 524222108Spjd nlst->hl_conn = clst->hl_conn; 525222108Spjd nlisten++; 526222108Spjd } else if (proto_server(nlst->hl_addr, &nlst->hl_conn) == 0) { 527222108Spjd pjdlog_info("Listening on new address %s.", 528222108Spjd nlst->hl_addr); 529222108Spjd nlisten++; 530222108Spjd } else { 531222108Spjd pjdlog_errno(LOG_WARNING, 532222108Spjd "Unable to listen on address %s", nlst->hl_addr); 533222108Spjd } 534210886Spjd } 535222108Spjd if (nlisten == 0) { 536222108Spjd pjdlog_error("No addresses to listen on."); 537222108Spjd goto failed; 538222108Spjd } 539229509Strociny /* 540229509Strociny * Check if pidfile's path has changed. 541229509Strociny */ 542234294Strociny if (!foreground && pidfile == NULL && 543234294Strociny strcmp(cfg->hc_pidfile, newcfg->hc_pidfile) != 0) { 544229509Strociny newpfh = pidfile_open(newcfg->hc_pidfile, 0600, &otherpid); 545229509Strociny if (newpfh == NULL) { 546229509Strociny if (errno == EEXIST) { 547229509Strociny pjdlog_errno(LOG_WARNING, 548229509Strociny "Another hastd is already running, pidfile: %s, pid: %jd.", 549229509Strociny newcfg->hc_pidfile, (intmax_t)otherpid); 550229509Strociny } else { 551229509Strociny pjdlog_errno(LOG_WARNING, 552229509Strociny "Unable to open or create pidfile %s", 553229509Strociny newcfg->hc_pidfile); 554229509Strociny } 555231017Strociny } else if (pidfile_write(newpfh) == -1) { 556229509Strociny /* Write PID to a file. */ 557229509Strociny pjdlog_errno(LOG_WARNING, 558229509Strociny "Unable to write PID to file %s", 559229509Strociny newcfg->hc_pidfile); 560229509Strociny } else { 561229509Strociny pjdlog_debug(1, "PID stored in %s.", 562229509Strociny newcfg->hc_pidfile); 563229509Strociny } 564229509Strociny } 565222108Spjd 566222108Spjd /* No failures from now on. */ 567222108Spjd 568210886Spjd /* 569222108Spjd * Switch to new control socket. 570210886Spjd */ 571210886Spjd if (newcfg->hc_controlconn != NULL) { 572210886Spjd pjdlog_info("Control socket changed from %s to %s.", 573210886Spjd cfg->hc_controladdr, newcfg->hc_controladdr); 574210886Spjd proto_close(cfg->hc_controlconn); 575210886Spjd cfg->hc_controlconn = newcfg->hc_controlconn; 576210886Spjd newcfg->hc_controlconn = NULL; 577210886Spjd strlcpy(cfg->hc_controladdr, newcfg->hc_controladdr, 578210886Spjd sizeof(cfg->hc_controladdr)); 579210886Spjd } 580222108Spjd /* 581229509Strociny * Switch to new pidfile. 582229509Strociny */ 583231017Strociny if (newpfh != NULL) { 584231017Strociny pjdlog_info("Pidfile changed from %s to %s.", cfg->hc_pidfile, 585231017Strociny newcfg->hc_pidfile); 586231017Strociny (void)pidfile_remove(pfh); 587231017Strociny pfh = newpfh; 588231017Strociny (void)strlcpy(cfg->hc_pidfile, newcfg->hc_pidfile, 589231017Strociny sizeof(cfg->hc_pidfile)); 590231017Strociny } 591229509Strociny /* 592222108Spjd * Switch to new listen addresses. Close all that were removed. 593222108Spjd */ 594222108Spjd while ((clst = TAILQ_FIRST(&cfg->hc_listen)) != NULL) { 595222108Spjd TAILQ_FOREACH(nlst, &newcfg->hc_listen, hl_next) { 596222108Spjd if (strcmp(nlst->hl_addr, clst->hl_addr) == 0) 597222108Spjd break; 598222108Spjd } 599222108Spjd if (nlst == NULL && clst->hl_conn != NULL) { 600222108Spjd proto_close(clst->hl_conn); 601222108Spjd pjdlog_info("No longer listening on address %s.", 602222108Spjd clst->hl_addr); 603222108Spjd } 604222108Spjd TAILQ_REMOVE(&cfg->hc_listen, clst, hl_next); 605222108Spjd free(clst); 606210886Spjd } 607222108Spjd TAILQ_CONCAT(&cfg->hc_listen, &newcfg->hc_listen, hl_next); 608210886Spjd 609210886Spjd /* 610210886Spjd * Stop and remove resources that were removed from the configuration. 611210886Spjd */ 612210886Spjd TAILQ_FOREACH_SAFE(cres, &cfg->hc_resources, hr_next, tres) { 613210886Spjd TAILQ_FOREACH(nres, &newcfg->hc_resources, hr_next) { 614210886Spjd if (strcmp(cres->hr_name, nres->hr_name) == 0) 615210886Spjd break; 616210886Spjd } 617210886Spjd if (nres == NULL) { 618210886Spjd control_set_role(cres, HAST_ROLE_INIT); 619210886Spjd TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 620210886Spjd pjdlog_info("Resource %s removed.", cres->hr_name); 621210886Spjd free(cres); 622210886Spjd } 623210886Spjd } 624210886Spjd /* 625210886Spjd * Move new resources to the current configuration. 626210886Spjd */ 627210886Spjd TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 628210886Spjd TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 629210886Spjd if (strcmp(cres->hr_name, nres->hr_name) == 0) 630210886Spjd break; 631210886Spjd } 632210886Spjd if (cres == NULL) { 633210886Spjd TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 634210886Spjd TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 635210886Spjd pjdlog_info("Resource %s added.", nres->hr_name); 636210886Spjd } 637210886Spjd } 638210886Spjd /* 639210886Spjd * Deal with modified resources. 640210886Spjd * Depending on what has changed exactly we might want to perform 641210886Spjd * different actions. 642210886Spjd * 643210886Spjd * We do full resource restart in the following situations: 644210886Spjd * Resource role is INIT or SECONDARY. 645210886Spjd * Resource role is PRIMARY and path to local component or provider 646210886Spjd * name has changed. 647210886Spjd * In case of PRIMARY, the worker process will be killed and restarted, 648210886Spjd * which also means removing /dev/hast/<name> provider and 649210886Spjd * recreating it. 650210886Spjd * 651210886Spjd * We do just reload (send SIGHUP to worker process) if we act as 652229509Strociny * PRIMARY, but only if remote address, source address, replication 653229509Strociny * mode, timeout, execution path or metaflush has changed. 654229509Strociny * For those, there is no need to restart worker process. 655210886Spjd * If PRIMARY receives SIGHUP, it will reconnect if remote address or 656229509Strociny * source address has changed or it will set new timeout if only timeout 657229509Strociny * has changed or it will update metaflush if only metaflush has 658229509Strociny * changed. 659210886Spjd */ 660210886Spjd TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 661210886Spjd TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 662210886Spjd if (strcmp(cres->hr_name, nres->hr_name) == 0) 663210886Spjd break; 664210886Spjd } 665218138Spjd PJDLOG_ASSERT(cres != NULL); 666210886Spjd if (resource_needs_restart(cres, nres)) { 667210886Spjd pjdlog_info("Resource %s configuration was modified, restarting it.", 668210886Spjd cres->hr_name); 669210886Spjd role = cres->hr_role; 670210886Spjd control_set_role(cres, HAST_ROLE_INIT); 671210886Spjd TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 672210886Spjd free(cres); 673210886Spjd TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 674210886Spjd TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 675210886Spjd control_set_role(nres, role); 676210886Spjd } else if (resource_needs_reload(cres, nres)) { 677210886Spjd pjdlog_info("Resource %s configuration was modified, reloading it.", 678210886Spjd cres->hr_name); 679210886Spjd strlcpy(cres->hr_remoteaddr, nres->hr_remoteaddr, 680210886Spjd sizeof(cres->hr_remoteaddr)); 681219818Spjd strlcpy(cres->hr_sourceaddr, nres->hr_sourceaddr, 682219818Spjd sizeof(cres->hr_sourceaddr)); 683210886Spjd cres->hr_replication = nres->hr_replication; 684219351Spjd cres->hr_checksum = nres->hr_checksum; 685219354Spjd cres->hr_compression = nres->hr_compression; 686210886Spjd cres->hr_timeout = nres->hr_timeout; 687217729Spjd strlcpy(cres->hr_exec, nres->hr_exec, 688217729Spjd sizeof(cres->hr_exec)); 689229509Strociny cres->hr_metaflush = nres->hr_metaflush; 690217784Spjd if (cres->hr_workerpid != 0) 691217784Spjd resource_reload(cres); 692210886Spjd } 693210886Spjd } 694210886Spjd 695210886Spjd yy_config_free(newcfg); 696210886Spjd pjdlog_info("Configuration reloaded successfully."); 697210886Spjd return; 698210886Spjdfailed: 699210886Spjd if (newcfg != NULL) { 700210886Spjd if (newcfg->hc_controlconn != NULL) 701210886Spjd proto_close(newcfg->hc_controlconn); 702222108Spjd while ((nlst = TAILQ_FIRST(&newcfg->hc_listen)) != NULL) { 703222108Spjd if (nlst->hl_conn != NULL) { 704222108Spjd TAILQ_FOREACH(clst, &cfg->hc_listen, hl_next) { 705222108Spjd if (strcmp(nlst->hl_addr, 706222108Spjd clst->hl_addr) == 0) { 707222108Spjd break; 708222108Spjd } 709222108Spjd } 710222108Spjd if (clst == NULL || clst->hl_conn == NULL) 711222108Spjd proto_close(nlst->hl_conn); 712222108Spjd } 713222108Spjd TAILQ_REMOVE(&newcfg->hc_listen, nlst, hl_next); 714222108Spjd free(nlst); 715222108Spjd } 716210886Spjd yy_config_free(newcfg); 717210886Spjd } 718229509Strociny if (newpfh != NULL) 719229509Strociny (void)pidfile_remove(newpfh); 720210886Spjd pjdlog_warning("Configuration not reloaded."); 721204076Spjd} 722204076Spjd 723204076Spjdstatic void 724211899Spjdterminate_workers(void) 725211899Spjd{ 726211899Spjd struct hast_resource *res; 727211899Spjd 728211899Spjd pjdlog_info("Termination signal received, exiting."); 729211899Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 730211899Spjd if (res->hr_workerpid == 0) 731211899Spjd continue; 732211899Spjd pjdlog_info("Terminating worker process (resource=%s, role=%s, pid=%u).", 733211899Spjd res->hr_name, role2str(res->hr_role), res->hr_workerpid); 734211899Spjd if (kill(res->hr_workerpid, SIGTERM) == 0) 735211899Spjd continue; 736211899Spjd pjdlog_errno(LOG_WARNING, 737211899Spjd "Unable to send signal to worker process (resource=%s, role=%s, pid=%u).", 738211899Spjd res->hr_name, role2str(res->hr_role), res->hr_workerpid); 739211899Spjd } 740211899Spjd} 741211899Spjd 742211899Spjdstatic void 743222108Spjdlisten_accept(struct hastd_listen *lst) 744204076Spjd{ 745204076Spjd struct hast_resource *res; 746204076Spjd struct proto_conn *conn; 747204076Spjd struct nv *nvin, *nvout, *nverr; 748204076Spjd const char *resname; 749204076Spjd const unsigned char *token; 750204076Spjd char laddr[256], raddr[256]; 751249236Strociny uint8_t version; 752204076Spjd size_t size; 753204076Spjd pid_t pid; 754204076Spjd int status; 755204076Spjd 756222108Spjd proto_local_address(lst->hl_conn, laddr, sizeof(laddr)); 757204076Spjd pjdlog_debug(1, "Accepting connection to %s.", laddr); 758204076Spjd 759231017Strociny if (proto_accept(lst->hl_conn, &conn) == -1) { 760204076Spjd pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr); 761204076Spjd return; 762204076Spjd } 763204076Spjd 764204076Spjd proto_local_address(conn, laddr, sizeof(laddr)); 765204076Spjd proto_remote_address(conn, raddr, sizeof(raddr)); 766209185Spjd pjdlog_info("Connection from %s to %s.", raddr, laddr); 767204076Spjd 768207371Spjd /* Error in setting timeout is not critical, but why should it fail? */ 769231017Strociny if (proto_timeout(conn, HAST_TIMEOUT) == -1) 770207371Spjd pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); 771207371Spjd 772204076Spjd nvin = nvout = nverr = NULL; 773204076Spjd 774204076Spjd /* 775204076Spjd * Before receiving any data see if remote host have access to any 776204076Spjd * resource. 777204076Spjd */ 778204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 779204076Spjd if (proto_address_match(conn, res->hr_remoteaddr)) 780204076Spjd break; 781204076Spjd } 782204076Spjd if (res == NULL) { 783204076Spjd pjdlog_error("Client %s isn't known.", raddr); 784204076Spjd goto close; 785204076Spjd } 786204076Spjd /* Ok, remote host can access at least one resource. */ 787204076Spjd 788231017Strociny if (hast_proto_recv_hdr(conn, &nvin) == -1) { 789204076Spjd pjdlog_errno(LOG_ERR, "Unable to receive header from %s", 790204076Spjd raddr); 791204076Spjd goto close; 792204076Spjd } 793204076Spjd 794204076Spjd resname = nv_get_string(nvin, "resource"); 795204076Spjd if (resname == NULL) { 796204076Spjd pjdlog_error("No 'resource' field in the header received from %s.", 797204076Spjd raddr); 798204076Spjd goto close; 799204076Spjd } 800204076Spjd pjdlog_debug(2, "%s: resource=%s", raddr, resname); 801249236Strociny version = nv_get_uint8(nvin, "version"); 802249236Strociny pjdlog_debug(2, "%s: version=%hhu", raddr, version); 803249236Strociny if (version == 0) { 804249236Strociny /* 805249236Strociny * If no version is sent, it means this is protocol version 1. 806249236Strociny */ 807249236Strociny version = 1; 808249236Strociny } 809204076Spjd token = nv_get_uint8_array(nvin, &size, "token"); 810204076Spjd /* 811231017Strociny * NULL token means that this is first connection. 812204076Spjd */ 813204076Spjd if (token != NULL && size != sizeof(res->hr_token)) { 814204076Spjd pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).", 815204076Spjd raddr, sizeof(res->hr_token), size); 816204076Spjd goto close; 817204076Spjd } 818204076Spjd 819204076Spjd /* 820204076Spjd * From now on we want to send errors to the remote node. 821204076Spjd */ 822204076Spjd nverr = nv_alloc(); 823204076Spjd 824204076Spjd /* Find resource related to this connection. */ 825204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 826204076Spjd if (strcmp(resname, res->hr_name) == 0) 827204076Spjd break; 828204076Spjd } 829204076Spjd /* Have we found the resource? */ 830204076Spjd if (res == NULL) { 831204076Spjd pjdlog_error("No resource '%s' as requested by %s.", 832204076Spjd resname, raddr); 833204076Spjd nv_add_stringf(nverr, "errmsg", "Resource not configured."); 834204076Spjd goto fail; 835204076Spjd } 836204076Spjd 837204076Spjd /* Now that we know resource name setup log prefix. */ 838204076Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 839204076Spjd 840204076Spjd /* Does the remote host have access to this resource? */ 841204076Spjd if (!proto_address_match(conn, res->hr_remoteaddr)) { 842204076Spjd pjdlog_error("Client %s has no access to the resource.", raddr); 843204076Spjd nv_add_stringf(nverr, "errmsg", "No access to the resource."); 844204076Spjd goto fail; 845204076Spjd } 846204076Spjd /* Is the resource marked as secondary? */ 847204076Spjd if (res->hr_role != HAST_ROLE_SECONDARY) { 848220890Spjd pjdlog_warning("We act as %s for the resource and not as %s as requested by %s.", 849204076Spjd role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY), 850204076Spjd raddr); 851204076Spjd nv_add_stringf(nverr, "errmsg", 852204076Spjd "Remote node acts as %s for the resource and not as %s.", 853204076Spjd role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY)); 854220898Spjd if (res->hr_role == HAST_ROLE_PRIMARY) { 855220898Spjd /* 856220898Spjd * If we act as primary request the other side to wait 857220899Spjd * for us a bit, as we might be finishing cleanups. 858220898Spjd */ 859220898Spjd nv_add_uint8(nverr, 1, "wait"); 860220898Spjd } 861204076Spjd goto fail; 862204076Spjd } 863204076Spjd /* Does token (if exists) match? */ 864204076Spjd if (token != NULL && memcmp(token, res->hr_token, 865204076Spjd sizeof(res->hr_token)) != 0) { 866204076Spjd pjdlog_error("Token received from %s doesn't match.", raddr); 867209185Spjd nv_add_stringf(nverr, "errmsg", "Token doesn't match."); 868204076Spjd goto fail; 869204076Spjd } 870204076Spjd /* 871204076Spjd * If there is no token, but we have half-open connection 872204076Spjd * (only remotein) or full connection (worker process is running) 873204076Spjd * we have to cancel those and accept the new connection. 874204076Spjd */ 875204076Spjd if (token == NULL) { 876218138Spjd PJDLOG_ASSERT(res->hr_remoteout == NULL); 877204076Spjd pjdlog_debug(1, "Initial connection from %s.", raddr); 878204076Spjd if (res->hr_workerpid != 0) { 879218138Spjd PJDLOG_ASSERT(res->hr_remotein == NULL); 880204076Spjd pjdlog_debug(1, 881204076Spjd "Worker process exists (pid=%u), stopping it.", 882204076Spjd (unsigned int)res->hr_workerpid); 883204076Spjd /* Stop child process. */ 884231017Strociny if (kill(res->hr_workerpid, SIGINT) == -1) { 885204076Spjd pjdlog_errno(LOG_ERR, 886204076Spjd "Unable to stop worker process (pid=%u)", 887204076Spjd (unsigned int)res->hr_workerpid); 888204076Spjd /* 889204076Spjd * Other than logging the problem we 890204076Spjd * ignore it - nothing smart to do. 891204076Spjd */ 892204076Spjd } 893204076Spjd /* Wait for it to exit. */ 894204076Spjd else if ((pid = waitpid(res->hr_workerpid, 895204076Spjd &status, 0)) != res->hr_workerpid) { 896207372Spjd /* We can only log the problem. */ 897204076Spjd pjdlog_errno(LOG_ERR, 898204076Spjd "Waiting for worker process (pid=%u) failed", 899204076Spjd (unsigned int)res->hr_workerpid); 900204076Spjd } else { 901207372Spjd child_exit_log(res->hr_workerpid, status); 902204076Spjd } 903213006Spjd child_cleanup(res); 904204076Spjd } else if (res->hr_remotein != NULL) { 905204076Spjd char oaddr[256]; 906204076Spjd 907213981Spjd proto_remote_address(res->hr_remotein, oaddr, 908213981Spjd sizeof(oaddr)); 909204076Spjd pjdlog_debug(1, 910204076Spjd "Canceling half-open connection from %s on connection from %s.", 911204076Spjd oaddr, raddr); 912204076Spjd proto_close(res->hr_remotein); 913204076Spjd res->hr_remotein = NULL; 914204076Spjd } 915204076Spjd } 916204076Spjd 917204076Spjd /* 918204076Spjd * Checks and cleanups are done. 919204076Spjd */ 920204076Spjd 921204076Spjd if (token == NULL) { 922260007Strociny if (version > HAST_PROTO_VERSION) { 923260007Strociny pjdlog_info("Remote protocol version %hhu is not supported, falling back to version %hhu.", 924260007Strociny version, (unsigned char)HAST_PROTO_VERSION); 925260007Strociny version = HAST_PROTO_VERSION; 926260007Strociny } 927260007Strociny pjdlog_debug(1, "Negotiated protocol version %hhu.", version); 928249236Strociny res->hr_version = version; 929204076Spjd arc4random_buf(res->hr_token, sizeof(res->hr_token)); 930204076Spjd nvout = nv_alloc(); 931249236Strociny nv_add_uint8(nvout, version, "version"); 932204076Spjd nv_add_uint8_array(nvout, res->hr_token, 933204076Spjd sizeof(res->hr_token), "token"); 934204076Spjd if (nv_error(nvout) != 0) { 935204076Spjd pjdlog_common(LOG_ERR, 0, nv_error(nvout), 936204076Spjd "Unable to prepare return header for %s", raddr); 937204076Spjd nv_add_stringf(nverr, "errmsg", 938204076Spjd "Remote node was unable to prepare return header: %s.", 939204076Spjd strerror(nv_error(nvout))); 940204076Spjd goto fail; 941204076Spjd } 942249236Strociny if (hast_proto_send(res, conn, nvout, NULL, 0) == -1) { 943204076Spjd int error = errno; 944204076Spjd 945204076Spjd pjdlog_errno(LOG_ERR, "Unable to send response to %s", 946204076Spjd raddr); 947204076Spjd nv_add_stringf(nverr, "errmsg", 948204076Spjd "Remote node was unable to send response: %s.", 949204076Spjd strerror(error)); 950204076Spjd goto fail; 951204076Spjd } 952204076Spjd res->hr_remotein = conn; 953204076Spjd pjdlog_debug(1, "Incoming connection from %s configured.", 954204076Spjd raddr); 955204076Spjd } else { 956204076Spjd res->hr_remoteout = conn; 957204076Spjd pjdlog_debug(1, "Outgoing connection to %s configured.", raddr); 958204076Spjd hastd_secondary(res, nvin); 959204076Spjd } 960204076Spjd nv_free(nvin); 961204076Spjd nv_free(nvout); 962204076Spjd nv_free(nverr); 963204076Spjd pjdlog_prefix_set("%s", ""); 964204076Spjd return; 965204076Spjdfail: 966204076Spjd if (nv_error(nverr) != 0) { 967204076Spjd pjdlog_common(LOG_ERR, 0, nv_error(nverr), 968204076Spjd "Unable to prepare error header for %s", raddr); 969204076Spjd goto close; 970204076Spjd } 971231017Strociny if (hast_proto_send(NULL, conn, nverr, NULL, 0) == -1) { 972204076Spjd pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr); 973204076Spjd goto close; 974204076Spjd } 975204076Spjdclose: 976204076Spjd if (nvin != NULL) 977204076Spjd nv_free(nvin); 978204076Spjd if (nvout != NULL) 979204076Spjd nv_free(nvout); 980204076Spjd if (nverr != NULL) 981204076Spjd nv_free(nverr); 982204076Spjd proto_close(conn); 983204076Spjd pjdlog_prefix_set("%s", ""); 984204076Spjd} 985204076Spjd 986204076Spjdstatic void 987218218Spjdconnection_migrate(struct hast_resource *res) 988218218Spjd{ 989218218Spjd struct proto_conn *conn; 990218218Spjd int16_t val = 0; 991218218Spjd 992219814Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 993219814Spjd 994219900Spjd PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY); 995219900Spjd 996231017Strociny if (proto_recv(res->hr_conn, &val, sizeof(val)) == -1) { 997218218Spjd pjdlog_errno(LOG_WARNING, 998218218Spjd "Unable to receive connection command"); 999218218Spjd return; 1000218218Spjd } 1001219818Spjd if (proto_client(res->hr_sourceaddr[0] != '\0' ? res->hr_sourceaddr : NULL, 1002231017Strociny res->hr_remoteaddr, &conn) == -1) { 1003218218Spjd val = errno; 1004218218Spjd pjdlog_errno(LOG_WARNING, 1005218218Spjd "Unable to create outgoing connection to %s", 1006218218Spjd res->hr_remoteaddr); 1007218218Spjd goto out; 1008218218Spjd } 1009231017Strociny if (proto_connect(conn, -1) == -1) { 1010218218Spjd val = errno; 1011218218Spjd pjdlog_errno(LOG_WARNING, "Unable to connect to %s", 1012218218Spjd res->hr_remoteaddr); 1013218218Spjd proto_close(conn); 1014218218Spjd goto out; 1015218218Spjd } 1016218218Spjd val = 0; 1017218218Spjdout: 1018231017Strociny if (proto_send(res->hr_conn, &val, sizeof(val)) == -1) { 1019218218Spjd pjdlog_errno(LOG_WARNING, 1020218218Spjd "Unable to send reply to connection request"); 1021218218Spjd } 1022231017Strociny if (val == 0 && proto_connection_send(res->hr_conn, conn) == -1) 1023218218Spjd pjdlog_errno(LOG_WARNING, "Unable to send connection"); 1024219814Spjd 1025219814Spjd pjdlog_prefix_set("%s", ""); 1026218218Spjd} 1027218218Spjd 1028218218Spjdstatic void 1029219837Spjdcheck_signals(void) 1030204076Spjd{ 1031213009Spjd struct timespec sigtimeout; 1032213009Spjd sigset_t mask; 1033219837Spjd int signo; 1034204076Spjd 1035213009Spjd sigtimeout.tv_sec = 0; 1036213009Spjd sigtimeout.tv_nsec = 0; 1037211977Spjd 1038213009Spjd PJDLOG_VERIFY(sigemptyset(&mask) == 0); 1039213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0); 1040213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0); 1041213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0); 1042213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0); 1043213009Spjd 1044219837Spjd while ((signo = sigtimedwait(&mask, NULL, &sigtimeout)) != -1) { 1045219837Spjd switch (signo) { 1046219837Spjd case SIGINT: 1047219837Spjd case SIGTERM: 1048219837Spjd sigexit_received = true; 1049219837Spjd terminate_workers(); 1050219837Spjd proto_close(cfg->hc_controlconn); 1051219837Spjd exit(EX_OK); 1052219837Spjd break; 1053219837Spjd case SIGCHLD: 1054219837Spjd child_exit(); 1055219837Spjd break; 1056219837Spjd case SIGHUP: 1057219837Spjd hastd_reload(); 1058219837Spjd break; 1059219837Spjd default: 1060219837Spjd PJDLOG_ABORT("Unexpected signal (%d).", signo); 1061219837Spjd } 1062219837Spjd } 1063219837Spjd} 1064219837Spjd 1065219837Spjdstatic void 1066219837Spjdmain_loop(void) 1067219837Spjd{ 1068219837Spjd struct hast_resource *res; 1069222108Spjd struct hastd_listen *lst; 1070219837Spjd struct timeval seltimeout; 1071219837Spjd int fd, maxfd, ret; 1072219837Spjd time_t lastcheck, now; 1073219837Spjd fd_set rfds; 1074219837Spjd 1075219864Spjd lastcheck = time(NULL); 1076219837Spjd seltimeout.tv_sec = REPORT_INTERVAL; 1077219837Spjd seltimeout.tv_usec = 0; 1078219837Spjd 1079204076Spjd for (;;) { 1080219837Spjd check_signals(); 1081204076Spjd 1082209177Spjd /* Setup descriptors for select(2). */ 1083204076Spjd FD_ZERO(&rfds); 1084212038Spjd maxfd = fd = proto_descriptor(cfg->hc_controlconn); 1085218138Spjd PJDLOG_ASSERT(fd >= 0); 1086212038Spjd FD_SET(fd, &rfds); 1087222108Spjd TAILQ_FOREACH(lst, &cfg->hc_listen, hl_next) { 1088222108Spjd if (lst->hl_conn == NULL) 1089222108Spjd continue; 1090222108Spjd fd = proto_descriptor(lst->hl_conn); 1091222108Spjd PJDLOG_ASSERT(fd >= 0); 1092222108Spjd FD_SET(fd, &rfds); 1093222108Spjd maxfd = fd > maxfd ? fd : maxfd; 1094222108Spjd } 1095212038Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 1096212038Spjd if (res->hr_event == NULL) 1097212038Spjd continue; 1098212038Spjd fd = proto_descriptor(res->hr_event); 1099218138Spjd PJDLOG_ASSERT(fd >= 0); 1100212038Spjd FD_SET(fd, &rfds); 1101212038Spjd maxfd = fd > maxfd ? fd : maxfd; 1102218218Spjd if (res->hr_role == HAST_ROLE_PRIMARY) { 1103218218Spjd /* Only primary workers asks for connections. */ 1104219900Spjd PJDLOG_ASSERT(res->hr_conn != NULL); 1105218218Spjd fd = proto_descriptor(res->hr_conn); 1106218218Spjd PJDLOG_ASSERT(fd >= 0); 1107218218Spjd FD_SET(fd, &rfds); 1108218218Spjd maxfd = fd > maxfd ? fd : maxfd; 1109219900Spjd } else { 1110219900Spjd PJDLOG_ASSERT(res->hr_conn == NULL); 1111218218Spjd } 1112212038Spjd } 1113204076Spjd 1114218138Spjd PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE); 1115213009Spjd ret = select(maxfd + 1, &rfds, NULL, NULL, &seltimeout); 1116219813Spjd now = time(NULL); 1117219813Spjd if (lastcheck + REPORT_INTERVAL <= now) { 1118213429Spjd hook_check(); 1119219813Spjd lastcheck = now; 1120219813Spjd } 1121219813Spjd if (ret == 0) { 1122219813Spjd /* 1123219813Spjd * select(2) timed out, so there should be no 1124219813Spjd * descriptors to check. 1125219813Spjd */ 1126219813Spjd continue; 1127219813Spjd } else if (ret == -1) { 1128204076Spjd if (errno == EINTR) 1129204076Spjd continue; 1130204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 1131204076Spjd pjdlog_exit(EX_OSERR, "select() failed"); 1132204076Spjd } 1133204076Spjd 1134219837Spjd /* 1135219837Spjd * Check for signals before we do anything to update our 1136219837Spjd * info about terminated workers in the meantime. 1137219837Spjd */ 1138219837Spjd check_signals(); 1139219837Spjd 1140212038Spjd if (FD_ISSET(proto_descriptor(cfg->hc_controlconn), &rfds)) 1141204076Spjd control_handle(cfg); 1142222108Spjd TAILQ_FOREACH(lst, &cfg->hc_listen, hl_next) { 1143222108Spjd if (lst->hl_conn == NULL) 1144222108Spjd continue; 1145222108Spjd if (FD_ISSET(proto_descriptor(lst->hl_conn), &rfds)) 1146222108Spjd listen_accept(lst); 1147222108Spjd } 1148212038Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 1149212038Spjd if (res->hr_event == NULL) 1150212038Spjd continue; 1151212038Spjd if (FD_ISSET(proto_descriptor(res->hr_event), &rfds)) { 1152212038Spjd if (event_recv(res) == 0) 1153212038Spjd continue; 1154212038Spjd /* The worker process exited? */ 1155212038Spjd proto_close(res->hr_event); 1156212038Spjd res->hr_event = NULL; 1157219900Spjd if (res->hr_conn != NULL) { 1158219900Spjd proto_close(res->hr_conn); 1159219900Spjd res->hr_conn = NULL; 1160219900Spjd } 1161218218Spjd continue; 1162212038Spjd } 1163219900Spjd if (res->hr_role == HAST_ROLE_PRIMARY) { 1164219900Spjd PJDLOG_ASSERT(res->hr_conn != NULL); 1165219900Spjd if (FD_ISSET(proto_descriptor(res->hr_conn), 1166219900Spjd &rfds)) { 1167219900Spjd connection_migrate(res); 1168219900Spjd } 1169219900Spjd } else { 1170219900Spjd PJDLOG_ASSERT(res->hr_conn == NULL); 1171218218Spjd } 1172212038Spjd } 1173204076Spjd } 1174204076Spjd} 1175204076Spjd 1176213428Spjdstatic void 1177213428Spjddummy_sighandler(int sig __unused) 1178213428Spjd{ 1179213428Spjd /* Nothing to do. */ 1180213428Spjd} 1181213428Spjd 1182204076Spjdint 1183204076Spjdmain(int argc, char *argv[]) 1184204076Spjd{ 1185222108Spjd struct hastd_listen *lst; 1186204076Spjd pid_t otherpid; 1187204076Spjd int debuglevel; 1188213009Spjd sigset_t mask; 1189204076Spjd 1190204076Spjd foreground = false; 1191204076Spjd debuglevel = 0; 1192204076Spjd 1193204076Spjd for (;;) { 1194204076Spjd int ch; 1195204076Spjd 1196204076Spjd ch = getopt(argc, argv, "c:dFhP:"); 1197204076Spjd if (ch == -1) 1198204076Spjd break; 1199204076Spjd switch (ch) { 1200204076Spjd case 'c': 1201204076Spjd cfgpath = optarg; 1202204076Spjd break; 1203204076Spjd case 'd': 1204204076Spjd debuglevel++; 1205204076Spjd break; 1206204076Spjd case 'F': 1207204076Spjd foreground = true; 1208204076Spjd break; 1209204076Spjd case 'P': 1210204076Spjd pidfile = optarg; 1211204076Spjd break; 1212204076Spjd case 'h': 1213204076Spjd default: 1214204076Spjd usage(); 1215204076Spjd } 1216204076Spjd } 1217204076Spjd argc -= optind; 1218204076Spjd argv += optind; 1219204076Spjd 1220217965Spjd pjdlog_init(PJDLOG_MODE_STD); 1221204076Spjd pjdlog_debug_set(debuglevel); 1222204076Spjd 1223214273Spjd g_gate_load(); 1224214273Spjd 1225229509Strociny /* 1226229509Strociny * When path to the configuration file is relative, obtain full path, 1227229509Strociny * so we can always find the file, even after daemonizing and changing 1228229509Strociny * working directory to /. 1229229509Strociny */ 1230229509Strociny if (cfgpath[0] != '/') { 1231229509Strociny const char *newcfgpath; 1232229509Strociny 1233229509Strociny newcfgpath = realpath(cfgpath, NULL); 1234229509Strociny if (newcfgpath == NULL) { 1235229509Strociny pjdlog_exit(EX_CONFIG, 1236229509Strociny "Unable to obtain full path of %s", cfgpath); 1237229509Strociny } 1238229509Strociny cfgpath = newcfgpath; 1239229509Strociny } 1240229509Strociny 1241229509Strociny cfg = yy_config_parse(cfgpath, true); 1242229509Strociny PJDLOG_ASSERT(cfg != NULL); 1243229509Strociny 1244229509Strociny if (pidfile != NULL) { 1245229509Strociny if (strlcpy(cfg->hc_pidfile, pidfile, 1246229509Strociny sizeof(cfg->hc_pidfile)) >= sizeof(cfg->hc_pidfile)) { 1247229509Strociny pjdlog_exitx(EX_CONFIG, "Pidfile path is too long."); 1248229509Strociny } 1249229509Strociny } 1250231017Strociny 1251234294Strociny if (pidfile != NULL || !foreground) { 1252231017Strociny pfh = pidfile_open(cfg->hc_pidfile, 0600, &otherpid); 1253231017Strociny if (pfh == NULL) { 1254231017Strociny if (errno == EEXIST) { 1255231017Strociny pjdlog_exitx(EX_TEMPFAIL, 1256231017Strociny "Another hastd is already running, pidfile: %s, pid: %jd.", 1257231017Strociny cfg->hc_pidfile, (intmax_t)otherpid); 1258231017Strociny } 1259231017Strociny /* 1260231017Strociny * If we cannot create pidfile for other reasons, 1261231017Strociny * only warn. 1262231017Strociny */ 1263231017Strociny pjdlog_errno(LOG_WARNING, 1264231017Strociny "Unable to open or create pidfile %s", 1265231017Strociny cfg->hc_pidfile); 1266204076Spjd } 1267204076Spjd } 1268204076Spjd 1269213428Spjd /* 1270217307Spjd * Restore default actions for interesting signals in case parent 1271217307Spjd * process (like init(8)) decided to ignore some of them (like SIGHUP). 1272217307Spjd */ 1273217307Spjd PJDLOG_VERIFY(signal(SIGHUP, SIG_DFL) != SIG_ERR); 1274217307Spjd PJDLOG_VERIFY(signal(SIGINT, SIG_DFL) != SIG_ERR); 1275217307Spjd PJDLOG_VERIFY(signal(SIGTERM, SIG_DFL) != SIG_ERR); 1276217307Spjd /* 1277213428Spjd * Because SIGCHLD is ignored by default, setup dummy handler for it, 1278213428Spjd * so we can mask it. 1279213428Spjd */ 1280213428Spjd PJDLOG_VERIFY(signal(SIGCHLD, dummy_sighandler) != SIG_ERR); 1281217307Spjd 1282213009Spjd PJDLOG_VERIFY(sigemptyset(&mask) == 0); 1283213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGHUP) == 0); 1284213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGINT) == 0); 1285213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGTERM) == 0); 1286213009Spjd PJDLOG_VERIFY(sigaddset(&mask, SIGCHLD) == 0); 1287213009Spjd PJDLOG_VERIFY(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); 1288204076Spjd 1289204076Spjd /* Listen on control address. */ 1290231017Strociny if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) == -1) { 1291204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 1292204076Spjd pjdlog_exit(EX_OSERR, "Unable to listen on control address %s", 1293204076Spjd cfg->hc_controladdr); 1294204076Spjd } 1295204076Spjd /* Listen for remote connections. */ 1296222108Spjd TAILQ_FOREACH(lst, &cfg->hc_listen, hl_next) { 1297231017Strociny if (proto_server(lst->hl_addr, &lst->hl_conn) == -1) { 1298222108Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 1299222108Spjd pjdlog_exit(EX_OSERR, "Unable to listen on address %s", 1300222108Spjd lst->hl_addr); 1301222108Spjd } 1302204076Spjd } 1303204076Spjd 1304204076Spjd if (!foreground) { 1305231017Strociny if (daemon(0, 0) == -1) { 1306204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 1307204076Spjd pjdlog_exit(EX_OSERR, "Unable to daemonize"); 1308204076Spjd } 1309204076Spjd 1310204076Spjd /* Start logging to syslog. */ 1311204076Spjd pjdlog_mode_set(PJDLOG_MODE_SYSLOG); 1312234294Strociny } 1313234294Strociny if (pidfile != NULL || !foreground) { 1314204076Spjd /* Write PID to a file. */ 1315231017Strociny if (pidfile_write(pfh) == -1) { 1316204076Spjd pjdlog_errno(LOG_WARNING, 1317229509Strociny "Unable to write PID to a file %s", 1318229509Strociny cfg->hc_pidfile); 1319229509Strociny } else { 1320229509Strociny pjdlog_debug(1, "PID stored in %s.", cfg->hc_pidfile); 1321204076Spjd } 1322204076Spjd } 1323204076Spjd 1324222108Spjd pjdlog_info("Started successfully, running protocol version %d.", 1325222108Spjd HAST_PROTO_VERSION); 1326222108Spjd 1327222108Spjd pjdlog_debug(1, "Listening on control address %s.", 1328222108Spjd cfg->hc_controladdr); 1329222108Spjd TAILQ_FOREACH(lst, &cfg->hc_listen, hl_next) 1330222108Spjd pjdlog_info("Listening on address %s.", lst->hl_addr); 1331222108Spjd 1332211977Spjd hook_init(); 1333211977Spjd 1334204076Spjd main_loop(); 1335204076Spjd 1336204076Spjd exit(0); 1337204076Spjd} 1338