hastd.c revision 211886
1/*- 2 * Copyright (c) 2009-2010 The FreeBSD Foundation 3 * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 211886 2010-08-27 15:16:52Z pjd $"); 33 34#include <sys/param.h> 35#include <sys/linker.h> 36#include <sys/module.h> 37#include <sys/wait.h> 38 39#include <assert.h> 40#include <err.h> 41#include <errno.h> 42#include <libutil.h> 43#include <signal.h> 44#include <stdbool.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <sysexits.h> 49#include <unistd.h> 50 51#include <activemap.h> 52#include <pjdlog.h> 53 54#include "control.h" 55#include "hast.h" 56#include "hast_proto.h" 57#include "hastd.h" 58#include "subr.h" 59 60/* Path to configuration file. */ 61const char *cfgpath = HAST_CONFIG; 62/* Hastd configuration. */ 63static struct hastd_config *cfg; 64/* Was SIGCHLD signal received? */ 65bool sigchld_received = false; 66/* Was SIGHUP signal received? */ 67bool sighup_received = false; 68/* Was SIGINT or SIGTERM signal received? */ 69bool sigexit_received = false; 70/* PID file handle. */ 71struct pidfh *pfh; 72 73static void 74usage(void) 75{ 76 77 errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]"); 78} 79 80static void 81sighandler(int sig) 82{ 83 84 switch (sig) { 85 case SIGCHLD: 86 sigchld_received = true; 87 break; 88 case SIGHUP: 89 sighup_received = true; 90 break; 91 default: 92 assert(!"invalid condition"); 93 } 94} 95 96static void 97g_gate_load(void) 98{ 99 100 if (modfind("g_gate") == -1) { 101 /* Not present in kernel, try loading it. */ 102 if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) { 103 if (errno != EEXIST) { 104 pjdlog_exit(EX_OSERR, 105 "Unable to load geom_gate module"); 106 } 107 } 108 } 109} 110 111static void 112child_exit_log(unsigned int pid, int status) 113{ 114 115 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 116 pjdlog_debug(1, "Worker process exited gracefully (pid=%u).", 117 pid); 118 } else if (WIFSIGNALED(status)) { 119 pjdlog_error("Worker process killed (pid=%u, signal=%d).", 120 pid, WTERMSIG(status)); 121 } else { 122 pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).", 123 pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1); 124 } 125} 126 127static void 128child_exit(void) 129{ 130 struct hast_resource *res; 131 int status; 132 pid_t pid; 133 134 while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 135 /* Find resource related to the process that just exited. */ 136 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 137 if (pid == res->hr_workerpid) 138 break; 139 } 140 if (res == NULL) { 141 /* 142 * This can happen when new connection arrives and we 143 * cancel child responsible for the old one. 144 */ 145 continue; 146 } 147 pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 148 role2str(res->hr_role)); 149 child_exit_log(pid, status); 150 proto_close(res->hr_ctrl); 151 res->hr_workerpid = 0; 152 if (res->hr_role == HAST_ROLE_PRIMARY) { 153 /* 154 * Restart child process if it was killed by signal 155 * or exited because of temporary problem. 156 */ 157 if (WIFSIGNALED(status) || 158 (WIFEXITED(status) && 159 WEXITSTATUS(status) == EX_TEMPFAIL)) { 160 sleep(1); 161 pjdlog_info("Restarting worker process."); 162 hastd_primary(res); 163 } else { 164 res->hr_role = HAST_ROLE_INIT; 165 pjdlog_info("Changing resource role back to %s.", 166 role2str(res->hr_role)); 167 } 168 } 169 pjdlog_prefix_set("%s", ""); 170 } 171} 172 173static bool 174resource_needs_restart(const struct hast_resource *res0, 175 const struct hast_resource *res1) 176{ 177 178 assert(strcmp(res0->hr_name, res1->hr_name) == 0); 179 180 if (strcmp(res0->hr_provname, res1->hr_provname) != 0) 181 return (true); 182 if (strcmp(res0->hr_localpath, res1->hr_localpath) != 0) 183 return (true); 184 if (res0->hr_role == HAST_ROLE_INIT || 185 res0->hr_role == HAST_ROLE_SECONDARY) { 186 if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 187 return (true); 188 if (res0->hr_replication != res1->hr_replication) 189 return (true); 190 if (res0->hr_timeout != res1->hr_timeout) 191 return (true); 192 if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 193 return (true); 194 } 195 return (false); 196} 197 198static bool 199resource_needs_reload(const struct hast_resource *res0, 200 const struct hast_resource *res1) 201{ 202 203 assert(strcmp(res0->hr_name, res1->hr_name) == 0); 204 assert(strcmp(res0->hr_provname, res1->hr_provname) == 0); 205 assert(strcmp(res0->hr_localpath, res1->hr_localpath) == 0); 206 207 if (res0->hr_role != HAST_ROLE_PRIMARY) 208 return (false); 209 210 if (strcmp(res0->hr_remoteaddr, res1->hr_remoteaddr) != 0) 211 return (true); 212 if (res0->hr_replication != res1->hr_replication) 213 return (true); 214 if (res0->hr_timeout != res1->hr_timeout) 215 return (true); 216 if (strcmp(res0->hr_exec, res1->hr_exec) != 0) 217 return (true); 218 return (false); 219} 220 221static void 222hastd_reload(void) 223{ 224 struct hastd_config *newcfg; 225 struct hast_resource *nres, *cres, *tres; 226 uint8_t role; 227 228 pjdlog_info("Reloading configuration..."); 229 230 newcfg = yy_config_parse(cfgpath, false); 231 if (newcfg == NULL) 232 goto failed; 233 234 /* 235 * Check if control address has changed. 236 */ 237 if (strcmp(cfg->hc_controladdr, newcfg->hc_controladdr) != 0) { 238 if (proto_server(newcfg->hc_controladdr, 239 &newcfg->hc_controlconn) < 0) { 240 pjdlog_errno(LOG_ERR, 241 "Unable to listen on control address %s", 242 newcfg->hc_controladdr); 243 goto failed; 244 } 245 } 246 /* 247 * Check if listen address has changed. 248 */ 249 if (strcmp(cfg->hc_listenaddr, newcfg->hc_listenaddr) != 0) { 250 if (proto_server(newcfg->hc_listenaddr, 251 &newcfg->hc_listenconn) < 0) { 252 pjdlog_errno(LOG_ERR, "Unable to listen on address %s", 253 newcfg->hc_listenaddr); 254 goto failed; 255 } 256 } 257 /* 258 * Only when both control and listen sockets are successfully 259 * initialized switch them to new configuration. 260 */ 261 if (newcfg->hc_controlconn != NULL) { 262 pjdlog_info("Control socket changed from %s to %s.", 263 cfg->hc_controladdr, newcfg->hc_controladdr); 264 proto_close(cfg->hc_controlconn); 265 cfg->hc_controlconn = newcfg->hc_controlconn; 266 newcfg->hc_controlconn = NULL; 267 strlcpy(cfg->hc_controladdr, newcfg->hc_controladdr, 268 sizeof(cfg->hc_controladdr)); 269 } 270 if (newcfg->hc_listenconn != NULL) { 271 pjdlog_info("Listen socket changed from %s to %s.", 272 cfg->hc_listenaddr, newcfg->hc_listenaddr); 273 proto_close(cfg->hc_listenconn); 274 cfg->hc_listenconn = newcfg->hc_listenconn; 275 newcfg->hc_listenconn = NULL; 276 strlcpy(cfg->hc_listenaddr, newcfg->hc_listenaddr, 277 sizeof(cfg->hc_listenaddr)); 278 } 279 280 /* 281 * Stop and remove resources that were removed from the configuration. 282 */ 283 TAILQ_FOREACH_SAFE(cres, &cfg->hc_resources, hr_next, tres) { 284 TAILQ_FOREACH(nres, &newcfg->hc_resources, hr_next) { 285 if (strcmp(cres->hr_name, nres->hr_name) == 0) 286 break; 287 } 288 if (nres == NULL) { 289 control_set_role(cres, HAST_ROLE_INIT); 290 TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 291 pjdlog_info("Resource %s removed.", cres->hr_name); 292 free(cres); 293 } 294 } 295 /* 296 * Move new resources to the current configuration. 297 */ 298 TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 299 TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 300 if (strcmp(cres->hr_name, nres->hr_name) == 0) 301 break; 302 } 303 if (cres == NULL) { 304 TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 305 TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 306 pjdlog_info("Resource %s added.", nres->hr_name); 307 } 308 } 309 /* 310 * Deal with modified resources. 311 * Depending on what has changed exactly we might want to perform 312 * different actions. 313 * 314 * We do full resource restart in the following situations: 315 * Resource role is INIT or SECONDARY. 316 * Resource role is PRIMARY and path to local component or provider 317 * name has changed. 318 * In case of PRIMARY, the worker process will be killed and restarted, 319 * which also means removing /dev/hast/<name> provider and 320 * recreating it. 321 * 322 * We do just reload (send SIGHUP to worker process) if we act as 323 * PRIMARY, but only remote address, replication mode and timeout 324 * has changed. For those, there is no need to restart worker process. 325 * If PRIMARY receives SIGHUP, it will reconnect if remote address or 326 * replication mode has changed or simply set new timeout if only 327 * timeout has changed. 328 */ 329 TAILQ_FOREACH_SAFE(nres, &newcfg->hc_resources, hr_next, tres) { 330 TAILQ_FOREACH(cres, &cfg->hc_resources, hr_next) { 331 if (strcmp(cres->hr_name, nres->hr_name) == 0) 332 break; 333 } 334 assert(cres != NULL); 335 if (resource_needs_restart(cres, nres)) { 336 pjdlog_info("Resource %s configuration was modified, restarting it.", 337 cres->hr_name); 338 role = cres->hr_role; 339 control_set_role(cres, HAST_ROLE_INIT); 340 TAILQ_REMOVE(&cfg->hc_resources, cres, hr_next); 341 free(cres); 342 TAILQ_REMOVE(&newcfg->hc_resources, nres, hr_next); 343 TAILQ_INSERT_TAIL(&cfg->hc_resources, nres, hr_next); 344 control_set_role(nres, role); 345 } else if (resource_needs_reload(cres, nres)) { 346 pjdlog_info("Resource %s configuration was modified, reloading it.", 347 cres->hr_name); 348 strlcpy(cres->hr_remoteaddr, nres->hr_remoteaddr, 349 sizeof(cres->hr_remoteaddr)); 350 cres->hr_replication = nres->hr_replication; 351 cres->hr_timeout = nres->hr_timeout; 352 if (cres->hr_workerpid != 0) { 353 if (kill(cres->hr_workerpid, SIGHUP) < 0) { 354 pjdlog_errno(LOG_WARNING, 355 "Unable to send SIGHUP to worker process %u", 356 (unsigned int)cres->hr_workerpid); 357 } 358 } 359 } 360 } 361 362 yy_config_free(newcfg); 363 pjdlog_info("Configuration reloaded successfully."); 364 return; 365failed: 366 if (newcfg != NULL) { 367 if (newcfg->hc_controlconn != NULL) 368 proto_close(newcfg->hc_controlconn); 369 if (newcfg->hc_listenconn != NULL) 370 proto_close(newcfg->hc_listenconn); 371 yy_config_free(newcfg); 372 } 373 pjdlog_warning("Configuration not reloaded."); 374} 375 376static void 377listen_accept(void) 378{ 379 struct hast_resource *res; 380 struct proto_conn *conn; 381 struct nv *nvin, *nvout, *nverr; 382 const char *resname; 383 const unsigned char *token; 384 char laddr[256], raddr[256]; 385 size_t size; 386 pid_t pid; 387 int status; 388 389 proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr)); 390 pjdlog_debug(1, "Accepting connection to %s.", laddr); 391 392 if (proto_accept(cfg->hc_listenconn, &conn) < 0) { 393 pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr); 394 return; 395 } 396 397 proto_local_address(conn, laddr, sizeof(laddr)); 398 proto_remote_address(conn, raddr, sizeof(raddr)); 399 pjdlog_info("Connection from %s to %s.", raddr, laddr); 400 401 /* Error in setting timeout is not critical, but why should it fail? */ 402 if (proto_timeout(conn, HAST_TIMEOUT) < 0) 403 pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); 404 405 nvin = nvout = nverr = NULL; 406 407 /* 408 * Before receiving any data see if remote host have access to any 409 * resource. 410 */ 411 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 412 if (proto_address_match(conn, res->hr_remoteaddr)) 413 break; 414 } 415 if (res == NULL) { 416 pjdlog_error("Client %s isn't known.", raddr); 417 goto close; 418 } 419 /* Ok, remote host can access at least one resource. */ 420 421 if (hast_proto_recv_hdr(conn, &nvin) < 0) { 422 pjdlog_errno(LOG_ERR, "Unable to receive header from %s", 423 raddr); 424 goto close; 425 } 426 427 resname = nv_get_string(nvin, "resource"); 428 if (resname == NULL) { 429 pjdlog_error("No 'resource' field in the header received from %s.", 430 raddr); 431 goto close; 432 } 433 pjdlog_debug(2, "%s: resource=%s", raddr, resname); 434 token = nv_get_uint8_array(nvin, &size, "token"); 435 /* 436 * NULL token means that this is first conection. 437 */ 438 if (token != NULL && size != sizeof(res->hr_token)) { 439 pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).", 440 raddr, sizeof(res->hr_token), size); 441 goto close; 442 } 443 444 /* 445 * From now on we want to send errors to the remote node. 446 */ 447 nverr = nv_alloc(); 448 449 /* Find resource related to this connection. */ 450 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 451 if (strcmp(resname, res->hr_name) == 0) 452 break; 453 } 454 /* Have we found the resource? */ 455 if (res == NULL) { 456 pjdlog_error("No resource '%s' as requested by %s.", 457 resname, raddr); 458 nv_add_stringf(nverr, "errmsg", "Resource not configured."); 459 goto fail; 460 } 461 462 /* Now that we know resource name setup log prefix. */ 463 pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 464 465 /* Does the remote host have access to this resource? */ 466 if (!proto_address_match(conn, res->hr_remoteaddr)) { 467 pjdlog_error("Client %s has no access to the resource.", raddr); 468 nv_add_stringf(nverr, "errmsg", "No access to the resource."); 469 goto fail; 470 } 471 /* Is the resource marked as secondary? */ 472 if (res->hr_role != HAST_ROLE_SECONDARY) { 473 pjdlog_error("We act as %s for the resource and not as %s as requested by %s.", 474 role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY), 475 raddr); 476 nv_add_stringf(nverr, "errmsg", 477 "Remote node acts as %s for the resource and not as %s.", 478 role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY)); 479 goto fail; 480 } 481 /* Does token (if exists) match? */ 482 if (token != NULL && memcmp(token, res->hr_token, 483 sizeof(res->hr_token)) != 0) { 484 pjdlog_error("Token received from %s doesn't match.", raddr); 485 nv_add_stringf(nverr, "errmsg", "Token doesn't match."); 486 goto fail; 487 } 488 /* 489 * If there is no token, but we have half-open connection 490 * (only remotein) or full connection (worker process is running) 491 * we have to cancel those and accept the new connection. 492 */ 493 if (token == NULL) { 494 assert(res->hr_remoteout == NULL); 495 pjdlog_debug(1, "Initial connection from %s.", raddr); 496 if (res->hr_workerpid != 0) { 497 assert(res->hr_remotein == NULL); 498 pjdlog_debug(1, 499 "Worker process exists (pid=%u), stopping it.", 500 (unsigned int)res->hr_workerpid); 501 /* Stop child process. */ 502 if (kill(res->hr_workerpid, SIGINT) < 0) { 503 pjdlog_errno(LOG_ERR, 504 "Unable to stop worker process (pid=%u)", 505 (unsigned int)res->hr_workerpid); 506 /* 507 * Other than logging the problem we 508 * ignore it - nothing smart to do. 509 */ 510 } 511 /* Wait for it to exit. */ 512 else if ((pid = waitpid(res->hr_workerpid, 513 &status, 0)) != res->hr_workerpid) { 514 /* We can only log the problem. */ 515 pjdlog_errno(LOG_ERR, 516 "Waiting for worker process (pid=%u) failed", 517 (unsigned int)res->hr_workerpid); 518 } else { 519 child_exit_log(res->hr_workerpid, status); 520 } 521 res->hr_workerpid = 0; 522 } else if (res->hr_remotein != NULL) { 523 char oaddr[256]; 524 525 proto_remote_address(conn, oaddr, sizeof(oaddr)); 526 pjdlog_debug(1, 527 "Canceling half-open connection from %s on connection from %s.", 528 oaddr, raddr); 529 proto_close(res->hr_remotein); 530 res->hr_remotein = NULL; 531 } 532 } 533 534 /* 535 * Checks and cleanups are done. 536 */ 537 538 if (token == NULL) { 539 arc4random_buf(res->hr_token, sizeof(res->hr_token)); 540 nvout = nv_alloc(); 541 nv_add_uint8_array(nvout, res->hr_token, 542 sizeof(res->hr_token), "token"); 543 if (nv_error(nvout) != 0) { 544 pjdlog_common(LOG_ERR, 0, nv_error(nvout), 545 "Unable to prepare return header for %s", raddr); 546 nv_add_stringf(nverr, "errmsg", 547 "Remote node was unable to prepare return header: %s.", 548 strerror(nv_error(nvout))); 549 goto fail; 550 } 551 if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) { 552 int error = errno; 553 554 pjdlog_errno(LOG_ERR, "Unable to send response to %s", 555 raddr); 556 nv_add_stringf(nverr, "errmsg", 557 "Remote node was unable to send response: %s.", 558 strerror(error)); 559 goto fail; 560 } 561 res->hr_remotein = conn; 562 pjdlog_debug(1, "Incoming connection from %s configured.", 563 raddr); 564 } else { 565 res->hr_remoteout = conn; 566 pjdlog_debug(1, "Outgoing connection to %s configured.", raddr); 567 hastd_secondary(res, nvin); 568 } 569 nv_free(nvin); 570 nv_free(nvout); 571 nv_free(nverr); 572 pjdlog_prefix_set("%s", ""); 573 return; 574fail: 575 if (nv_error(nverr) != 0) { 576 pjdlog_common(LOG_ERR, 0, nv_error(nverr), 577 "Unable to prepare error header for %s", raddr); 578 goto close; 579 } 580 if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) { 581 pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr); 582 goto close; 583 } 584close: 585 if (nvin != NULL) 586 nv_free(nvin); 587 if (nvout != NULL) 588 nv_free(nvout); 589 if (nverr != NULL) 590 nv_free(nverr); 591 proto_close(conn); 592 pjdlog_prefix_set("%s", ""); 593} 594 595static void 596main_loop(void) 597{ 598 fd_set rfds, wfds; 599 int cfd, lfd, maxfd, ret; 600 601 for (;;) { 602 if (sigchld_received) { 603 sigchld_received = false; 604 child_exit(); 605 } 606 if (sighup_received) { 607 sighup_received = false; 608 hastd_reload(); 609 } 610 611 cfd = proto_descriptor(cfg->hc_controlconn); 612 lfd = proto_descriptor(cfg->hc_listenconn); 613 maxfd = cfd > lfd ? cfd : lfd; 614 615 /* Setup descriptors for select(2). */ 616 FD_ZERO(&rfds); 617 FD_SET(cfd, &rfds); 618 FD_SET(lfd, &rfds); 619 FD_ZERO(&wfds); 620 FD_SET(cfd, &wfds); 621 FD_SET(lfd, &wfds); 622 623 ret = select(maxfd + 1, &rfds, &wfds, NULL, NULL); 624 if (ret == -1) { 625 if (errno == EINTR) 626 continue; 627 KEEP_ERRNO((void)pidfile_remove(pfh)); 628 pjdlog_exit(EX_OSERR, "select() failed"); 629 } 630 631 if (FD_ISSET(cfd, &rfds) || FD_ISSET(cfd, &wfds)) 632 control_handle(cfg); 633 if (FD_ISSET(lfd, &rfds) || FD_ISSET(lfd, &wfds)) 634 listen_accept(); 635 } 636} 637 638int 639main(int argc, char *argv[]) 640{ 641 const char *pidfile; 642 pid_t otherpid; 643 bool foreground; 644 int debuglevel; 645 646 g_gate_load(); 647 648 foreground = false; 649 debuglevel = 0; 650 pidfile = HASTD_PIDFILE; 651 652 for (;;) { 653 int ch; 654 655 ch = getopt(argc, argv, "c:dFhP:"); 656 if (ch == -1) 657 break; 658 switch (ch) { 659 case 'c': 660 cfgpath = optarg; 661 break; 662 case 'd': 663 debuglevel++; 664 break; 665 case 'F': 666 foreground = true; 667 break; 668 case 'P': 669 pidfile = optarg; 670 break; 671 case 'h': 672 default: 673 usage(); 674 } 675 } 676 argc -= optind; 677 argv += optind; 678 679 pjdlog_debug_set(debuglevel); 680 681 pfh = pidfile_open(pidfile, 0600, &otherpid); 682 if (pfh == NULL) { 683 if (errno == EEXIST) { 684 pjdlog_exitx(EX_TEMPFAIL, 685 "Another hastd is already running, pid: %jd.", 686 (intmax_t)otherpid); 687 } 688 /* If we cannot create pidfile from other reasons, only warn. */ 689 pjdlog_errno(LOG_WARNING, "Unable to open or create pidfile"); 690 } 691 692 cfg = yy_config_parse(cfgpath, true); 693 assert(cfg != NULL); 694 695 signal(SIGHUP, sighandler); 696 signal(SIGCHLD, sighandler); 697 698 /* Listen on control address. */ 699 if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) { 700 KEEP_ERRNO((void)pidfile_remove(pfh)); 701 pjdlog_exit(EX_OSERR, "Unable to listen on control address %s", 702 cfg->hc_controladdr); 703 } 704 /* Listen for remote connections. */ 705 if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) { 706 KEEP_ERRNO((void)pidfile_remove(pfh)); 707 pjdlog_exit(EX_OSERR, "Unable to listen on address %s", 708 cfg->hc_listenaddr); 709 } 710 711 if (!foreground) { 712 if (daemon(0, 0) < 0) { 713 KEEP_ERRNO((void)pidfile_remove(pfh)); 714 pjdlog_exit(EX_OSERR, "Unable to daemonize"); 715 } 716 717 /* Start logging to syslog. */ 718 pjdlog_mode_set(PJDLOG_MODE_SYSLOG); 719 720 /* Write PID to a file. */ 721 if (pidfile_write(pfh) < 0) { 722 pjdlog_errno(LOG_WARNING, 723 "Unable to write PID to a file"); 724 } 725 } 726 727 main_loop(); 728 729 exit(0); 730} 731