hastd.c revision 207372
1204792Srdivacky/*- 2204792Srdivacky * Copyright (c) 2009-2010 The FreeBSD Foundation 3204792Srdivacky * All rights reserved. 4204792Srdivacky * 5204792Srdivacky * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204792Srdivacky * the FreeBSD Foundation. 7204792Srdivacky * 8204792Srdivacky * Redistribution and use in source and binary forms, with or without 9204792Srdivacky * modification, are permitted provided that the following conditions 10204792Srdivacky * are met: 11204792Srdivacky * 1. Redistributions of source code must retain the above copyright 12204792Srdivacky * notice, this list of conditions and the following disclaimer. 13204792Srdivacky * 2. Redistributions in binary form must reproduce the above copyright 14204792Srdivacky * notice, this list of conditions and the following disclaimer in the 15204792Srdivacky * documentation and/or other materials provided with the distribution. 16204792Srdivacky * 17204792Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204792Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204792Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204792Srdivacky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204792Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204792Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204792Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204792Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204792Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204792Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204792Srdivacky * SUCH DAMAGE. 28204792Srdivacky */ 29204792Srdivacky 30204792Srdivacky#include <sys/cdefs.h> 31204792Srdivacky__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 207372 2010-04-29 15:42:24Z pjd $"); 32204792Srdivacky 33204792Srdivacky#include <sys/param.h> 34204792Srdivacky#include <sys/linker.h> 35204792Srdivacky#include <sys/module.h> 36204792Srdivacky#include <sys/wait.h> 37204792Srdivacky 38204792Srdivacky#include <assert.h> 39204792Srdivacky#include <err.h> 40204792Srdivacky#include <errno.h> 41204792Srdivacky#include <libutil.h> 42204792Srdivacky#include <signal.h> 43204792Srdivacky#include <stdbool.h> 44204792Srdivacky#include <stdio.h> 45204792Srdivacky#include <stdlib.h> 46204792Srdivacky#include <string.h> 47204792Srdivacky#include <sysexits.h> 48204792Srdivacky#include <unistd.h> 49204792Srdivacky 50204792Srdivacky#include <activemap.h> 51204792Srdivacky#include <pjdlog.h> 52204792Srdivacky 53204792Srdivacky#include "control.h" 54204792Srdivacky#include "hast.h" 55204792Srdivacky#include "hast_proto.h" 56204792Srdivacky#include "hastd.h" 57204792Srdivacky#include "subr.h" 58204792Srdivacky 59204792Srdivacky/* Path to configuration file. */ 60204792Srdivackystatic const char *cfgpath = HAST_CONFIG; 61226633Sdim/* Hastd configuration. */ 62226633Sdimstatic struct hastd_config *cfg; 63204792Srdivacky/* Was SIGCHLD signal received? */ 64204792Srdivackystatic bool sigchld_received = false; 65204792Srdivacky/* Was SIGHUP signal received? */ 66204792Srdivackystatic bool sighup_received = false; 67204792Srdivacky/* Was SIGINT or SIGTERM signal received? */ 68204792Srdivackybool sigexit_received = false; 69204792Srdivacky/* PID file handle. */ 70204792Srdivackystruct pidfh *pfh; 71204792Srdivacky 72210299Sedstatic void 73210299Sedusage(void) 74210299Sed{ 75210299Sed 76210299Sed errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]"); 77210299Sed} 78210299Sed 79210299Sedstatic void 80210299Sedsighandler(int sig) 81210299Sed{ 82210299Sed 83210299Sed switch (sig) { 84210299Sed case SIGCHLD: 85210299Sed sigchld_received = true; 86210299Sed break; 87210299Sed case SIGHUP: 88210299Sed sighup_received = true; 89210299Sed break; 90210299Sed default: 91210299Sed assert(!"invalid condition"); 92210299Sed } 93210299Sed} 94210299Sed 95210299Sedstatic void 96210299Sedg_gate_load(void) 97204792Srdivacky{ 98204792Srdivacky 99204792Srdivacky if (modfind("g_gate") == -1) { 100205218Srdivacky /* Not present in kernel, try loading it. */ 101204792Srdivacky if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) { 102204792Srdivacky if (errno != EEXIST) { 103204792Srdivacky pjdlog_exit(EX_OSERR, 104204792Srdivacky "Unable to load geom_gate module"); 105226633Sdim } 106205218Srdivacky } 107204792Srdivacky } 108204792Srdivacky} 109205218Srdivacky 110204792Srdivackystatic void 111204792Srdivackychild_exit_log(unsigned int pid, int status) 112204792Srdivacky{ 113204792Srdivacky 114204792Srdivacky if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 115205218Srdivacky pjdlog_debug(1, "Worker process exited gracefully (pid=%u).", 116205218Srdivacky pid); 117205218Srdivacky } else if (WIFSIGNALED(status)) { 118207618Srdivacky pjdlog_error("Worker process killed (pid=%u, signal=%d).", 119205218Srdivacky pid, WTERMSIG(status)); 120205218Srdivacky } else { 121205218Srdivacky pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).", 122205218Srdivacky pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1); 123226633Sdim } 124207618Srdivacky} 125207618Srdivacky 126207618Srdivackystatic void 127205218Srdivackychild_exit(void) 128205218Srdivacky{ 129205218Srdivacky struct hast_resource *res; 130205218Srdivacky int status; 131205218Srdivacky pid_t pid; 132205218Srdivacky 133205218Srdivacky while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 134206083Srdivacky /* Find resource related to the process that just exited. */ 135206083Srdivacky TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 136206083Srdivacky if (pid == res->hr_workerpid) 137206083Srdivacky break; 138206083Srdivacky } 139206083Srdivacky if (res == NULL) { 140206083Srdivacky /* 141206083Srdivacky * This can happen when new connection arrives and we 142206083Srdivacky * cancel child responsible for the old one. 143206083Srdivacky */ 144206083Srdivacky continue; 145206083Srdivacky } 146206083Srdivacky pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 147206083Srdivacky role2str(res->hr_role)); 148206083Srdivacky child_exit_log(pid, status); 149206083Srdivacky proto_close(res->hr_ctrl); 150206083Srdivacky res->hr_workerpid = 0; 151206083Srdivacky if (res->hr_role == HAST_ROLE_PRIMARY) { 152206083Srdivacky /* 153206083Srdivacky * Restart child process if it was killed by signal 154206083Srdivacky * or exited because of temporary problem. 155206083Srdivacky */ 156206083Srdivacky if (WIFSIGNALED(status) || 157206083Srdivacky (WIFEXITED(status) && 158204792Srdivacky WEXITSTATUS(status) == EX_TEMPFAIL)) { 159204792Srdivacky sleep(1); 160204792Srdivacky pjdlog_info("Restarting worker process."); 161204792Srdivacky hastd_primary(res); 162204792Srdivacky } else { 163204792Srdivacky res->hr_role = HAST_ROLE_INIT; 164204792Srdivacky pjdlog_info("Changing resource role back to %s.", 165204792Srdivacky role2str(res->hr_role)); 166204792Srdivacky } 167204792Srdivacky } 168204792Srdivacky pjdlog_prefix_set("%s", ""); 169204792Srdivacky } 170204792Srdivacky} 171204792Srdivacky 172204792Srdivackystatic void 173204792Srdivackyhastd_reload(void) 174204792Srdivacky{ 175204792Srdivacky 176204792Srdivacky /* TODO */ 177204792Srdivacky pjdlog_warning("Configuration reload is not implemented."); 178204792Srdivacky} 179204792Srdivacky 180204792Srdivackystatic void 181204792Srdivackylisten_accept(void) 182204792Srdivacky{ 183204792Srdivacky struct hast_resource *res; 184204792Srdivacky struct proto_conn *conn; 185204792Srdivacky struct nv *nvin, *nvout, *nverr; 186204792Srdivacky const char *resname; 187204792Srdivacky const unsigned char *token; 188204792Srdivacky char laddr[256], raddr[256]; 189204792Srdivacky size_t size; 190204792Srdivacky pid_t pid; 191204792Srdivacky int status; 192204792Srdivacky 193204792Srdivacky proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr)); 194204792Srdivacky pjdlog_debug(1, "Accepting connection to %s.", laddr); 195204792Srdivacky 196204792Srdivacky if (proto_accept(cfg->hc_listenconn, &conn) < 0) { 197204792Srdivacky pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr); 198204792Srdivacky return; 199204792Srdivacky } 200204792Srdivacky 201204792Srdivacky proto_local_address(conn, laddr, sizeof(laddr)); 202204792Srdivacky proto_remote_address(conn, raddr, sizeof(raddr)); 203204792Srdivacky pjdlog_info("Connection from %s to %s.", laddr, raddr); 204204792Srdivacky 205204792Srdivacky /* Error in setting timeout is not critical, but why should it fail? */ 206204792Srdivacky if (proto_timeout(conn, HAST_TIMEOUT) < 0) 207204792Srdivacky pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); 208204792Srdivacky 209204792Srdivacky nvin = nvout = nverr = NULL; 210204792Srdivacky 211204792Srdivacky /* 212204792Srdivacky * Before receiving any data see if remote host have access to any 213204792Srdivacky * resource. 214204792Srdivacky */ 215204792Srdivacky TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 216204792Srdivacky if (proto_address_match(conn, res->hr_remoteaddr)) 217204792Srdivacky break; 218204792Srdivacky } 219204792Srdivacky if (res == NULL) { 220204792Srdivacky pjdlog_error("Client %s isn't known.", raddr); 221204792Srdivacky goto close; 222204792Srdivacky } 223204792Srdivacky /* Ok, remote host can access at least one resource. */ 224204792Srdivacky 225204792Srdivacky if (hast_proto_recv_hdr(conn, &nvin) < 0) { 226204792Srdivacky pjdlog_errno(LOG_ERR, "Unable to receive header from %s", 227204792Srdivacky raddr); 228204792Srdivacky goto close; 229204792Srdivacky } 230204792Srdivacky 231204792Srdivacky resname = nv_get_string(nvin, "resource"); 232204792Srdivacky if (resname == NULL) { 233204792Srdivacky pjdlog_error("No 'resource' field in the header received from %s.", 234204792Srdivacky raddr); 235204792Srdivacky goto close; 236204792Srdivacky } 237204792Srdivacky pjdlog_debug(2, "%s: resource=%s", raddr, resname); 238204792Srdivacky token = nv_get_uint8_array(nvin, &size, "token"); 239204792Srdivacky /* 240204792Srdivacky * NULL token means that this is first conection. 241204792Srdivacky */ 242204792Srdivacky if (token != NULL && size != sizeof(res->hr_token)) { 243204792Srdivacky pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).", 244204792Srdivacky raddr, sizeof(res->hr_token), size); 245204792Srdivacky goto close; 246204792Srdivacky } 247204792Srdivacky 248204792Srdivacky /* 249204792Srdivacky * From now on we want to send errors to the remote node. 250204792Srdivacky */ 251204792Srdivacky nverr = nv_alloc(); 252204792Srdivacky 253204792Srdivacky /* Find resource related to this connection. */ 254204792Srdivacky TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 255204792Srdivacky if (strcmp(resname, res->hr_name) == 0) 256204792Srdivacky break; 257204792Srdivacky } 258204792Srdivacky /* Have we found the resource? */ 259204792Srdivacky if (res == NULL) { 260204792Srdivacky pjdlog_error("No resource '%s' as requested by %s.", 261204792Srdivacky resname, raddr); 262204792Srdivacky nv_add_stringf(nverr, "errmsg", "Resource not configured."); 263204792Srdivacky goto fail; 264204792Srdivacky } 265204792Srdivacky 266204792Srdivacky /* Now that we know resource name setup log prefix. */ 267204792Srdivacky pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 268204792Srdivacky 269204792Srdivacky /* Does the remote host have access to this resource? */ 270204792Srdivacky if (!proto_address_match(conn, res->hr_remoteaddr)) { 271204792Srdivacky pjdlog_error("Client %s has no access to the resource.", raddr); 272204792Srdivacky nv_add_stringf(nverr, "errmsg", "No access to the resource."); 273204792Srdivacky goto fail; 274204792Srdivacky } 275204792Srdivacky /* Is the resource marked as secondary? */ 276204792Srdivacky if (res->hr_role != HAST_ROLE_SECONDARY) { 277204792Srdivacky pjdlog_error("We act as %s for the resource and not as %s as requested by %s.", 278204792Srdivacky role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY), 279204792Srdivacky raddr); 280204792Srdivacky nv_add_stringf(nverr, "errmsg", 281204792Srdivacky "Remote node acts as %s for the resource and not as %s.", 282204792Srdivacky role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY)); 283204792Srdivacky goto fail; 284204792Srdivacky } 285204792Srdivacky /* Does token (if exists) match? */ 286204792Srdivacky if (token != NULL && memcmp(token, res->hr_token, 287204792Srdivacky sizeof(res->hr_token)) != 0) { 288204792Srdivacky pjdlog_error("Token received from %s doesn't match.", raddr); 289204792Srdivacky nv_add_stringf(nverr, "errmsg", "Toke doesn't match."); 290204792Srdivacky goto fail; 291204792Srdivacky } 292204792Srdivacky /* 293204792Srdivacky * If there is no token, but we have half-open connection 294204792Srdivacky * (only remotein) or full connection (worker process is running) 295204792Srdivacky * we have to cancel those and accept the new connection. 296204792Srdivacky */ 297204792Srdivacky if (token == NULL) { 298204792Srdivacky assert(res->hr_remoteout == NULL); 299204792Srdivacky pjdlog_debug(1, "Initial connection from %s.", raddr); 300204792Srdivacky if (res->hr_workerpid != 0) { 301204792Srdivacky assert(res->hr_remotein == NULL); 302204792Srdivacky pjdlog_debug(1, 303204792Srdivacky "Worker process exists (pid=%u), stopping it.", 304204792Srdivacky (unsigned int)res->hr_workerpid); 305204792Srdivacky /* Stop child process. */ 306204792Srdivacky if (kill(res->hr_workerpid, SIGINT) < 0) { 307204792Srdivacky pjdlog_errno(LOG_ERR, 308204792Srdivacky "Unable to stop worker process (pid=%u)", 309204792Srdivacky (unsigned int)res->hr_workerpid); 310204792Srdivacky /* 311204792Srdivacky * Other than logging the problem we 312204792Srdivacky * ignore it - nothing smart to do. 313204792Srdivacky */ 314204792Srdivacky } 315204792Srdivacky /* Wait for it to exit. */ 316204792Srdivacky else if ((pid = waitpid(res->hr_workerpid, 317204792Srdivacky &status, 0)) != res->hr_workerpid) { 318204792Srdivacky /* We can only log the problem. */ 319204792Srdivacky pjdlog_errno(LOG_ERR, 320204792Srdivacky "Waiting for worker process (pid=%u) failed", 321204792Srdivacky (unsigned int)res->hr_workerpid); 322204792Srdivacky } else { 323204792Srdivacky child_exit_log(res->hr_workerpid, status); 324204792Srdivacky } 325204792Srdivacky res->hr_workerpid = 0; 326204792Srdivacky } else if (res->hr_remotein != NULL) { 327204792Srdivacky char oaddr[256]; 328204792Srdivacky 329204792Srdivacky proto_remote_address(conn, oaddr, sizeof(oaddr)); 330204792Srdivacky pjdlog_debug(1, 331204792Srdivacky "Canceling half-open connection from %s on connection from %s.", 332204792Srdivacky oaddr, raddr); 333204792Srdivacky proto_close(res->hr_remotein); 334204792Srdivacky res->hr_remotein = NULL; 335204792Srdivacky } 336204792Srdivacky } 337204792Srdivacky 338204792Srdivacky /* 339204792Srdivacky * Checks and cleanups are done. 340204792Srdivacky */ 341204792Srdivacky 342204792Srdivacky if (token == NULL) { 343204792Srdivacky arc4random_buf(res->hr_token, sizeof(res->hr_token)); 344204792Srdivacky nvout = nv_alloc(); 345204792Srdivacky nv_add_uint8_array(nvout, res->hr_token, 346204792Srdivacky sizeof(res->hr_token), "token"); 347204792Srdivacky if (nv_error(nvout) != 0) { 348204792Srdivacky pjdlog_common(LOG_ERR, 0, nv_error(nvout), 349204792Srdivacky "Unable to prepare return header for %s", raddr); 350204792Srdivacky nv_add_stringf(nverr, "errmsg", 351204792Srdivacky "Remote node was unable to prepare return header: %s.", 352204792Srdivacky strerror(nv_error(nvout))); 353204792Srdivacky goto fail; 354205218Srdivacky } 355205218Srdivacky if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) { 356205218Srdivacky int error = errno; 357205218Srdivacky 358207618Srdivacky pjdlog_errno(LOG_ERR, "Unable to send response to %s", 359207618Srdivacky raddr); 360207618Srdivacky nv_add_stringf(nverr, "errmsg", 361205218Srdivacky "Remote node was unable to send response: %s.", 362207618Srdivacky strerror(error)); 363207618Srdivacky goto fail; 364226633Sdim } 365207618Srdivacky res->hr_remotein = conn; 366223017Sdim pjdlog_debug(1, "Incoming connection from %s configured.", 367205218Srdivacky raddr); 368205218Srdivacky } else { 369207618Srdivacky res->hr_remoteout = conn; 370207618Srdivacky pjdlog_debug(1, "Outgoing connection to %s configured.", raddr); 371207618Srdivacky hastd_secondary(res, nvin); 372207618Srdivacky } 373207618Srdivacky nv_free(nvin); 374207618Srdivacky nv_free(nvout); 375207618Srdivacky nv_free(nverr); 376210299Sed pjdlog_prefix_set("%s", ""); 377212904Sdim return; 378218893Sdimfail: 379218893Sdim if (nv_error(nverr) != 0) { 380210299Sed pjdlog_common(LOG_ERR, 0, nv_error(nverr), 381205218Srdivacky "Unable to prepare error header for %s", raddr); 382205218Srdivacky goto close; 383205218Srdivacky } 384205218Srdivacky if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) { 385205218Srdivacky pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr); 386205218Srdivacky goto close; 387205218Srdivacky } 388205218Srdivackyclose: 389205218Srdivacky if (nvin != NULL) 390205218Srdivacky nv_free(nvin); 391205218Srdivacky if (nvout != NULL) 392207618Srdivacky nv_free(nvout); 393207618Srdivacky if (nverr != NULL) 394207618Srdivacky nv_free(nverr); 395207618Srdivacky proto_close(conn); 396207618Srdivacky pjdlog_prefix_set("%s", ""); 397207618Srdivacky} 398207618Srdivacky 399210299Sedstatic void 400212904Sdimmain_loop(void) 401218893Sdim{ 402218893Sdim fd_set rfds, wfds; 403210299Sed int fd, maxfd, ret; 404205218Srdivacky 405205218Srdivacky for (;;) { 406205218Srdivacky if (sigchld_received) { 407205218Srdivacky sigchld_received = false; 408205218Srdivacky child_exit(); 409205218Srdivacky } 410207618Srdivacky if (sighup_received) { 411207618Srdivacky sighup_received = false; 412207618Srdivacky hastd_reload(); 413207618Srdivacky } 414207618Srdivacky 415207618Srdivacky maxfd = 0; 416207618Srdivacky FD_ZERO(&rfds); 417210299Sed FD_ZERO(&wfds); 418212904Sdim 419210299Sed /* Setup descriptors for select(2). */ 420205218Srdivacky#define SETUP_FD(conn) do { \ 421218893Sdim fd = proto_descriptor(conn); \ 422210299Sed if (fd >= 0) { \ 423205218Srdivacky maxfd = fd > maxfd ? fd : maxfd; \ 424205218Srdivacky FD_SET(fd, &rfds); \ 425205218Srdivacky FD_SET(fd, &wfds); \ 426205218Srdivacky } \ 427205218Srdivacky} while (0) 428205218Srdivacky SETUP_FD(cfg->hc_controlconn); 429207618Srdivacky SETUP_FD(cfg->hc_listenconn); 430207618Srdivacky#undef SETUP_FD 431207618Srdivacky 432207618Srdivacky ret = select(maxfd + 1, &rfds, &wfds, NULL, NULL); 433207618Srdivacky if (ret == -1) { 434207618Srdivacky if (errno == EINTR) 435207618Srdivacky continue; 436207618Srdivacky KEEP_ERRNO((void)pidfile_remove(pfh)); 437207618Srdivacky pjdlog_exit(EX_OSERR, "select() failed"); 438205218Srdivacky } 439205218Srdivacky 440205218Srdivacky#define ISSET_FD(conn) \ 441205218Srdivacky (FD_ISSET((fd = proto_descriptor(conn)), &rfds) || FD_ISSET(fd, &wfds)) 442205218Srdivacky if (ISSET_FD(cfg->hc_controlconn)) 443212904Sdim control_handle(cfg); 444210299Sed if (ISSET_FD(cfg->hc_listenconn)) 445205218Srdivacky listen_accept(); 446205218Srdivacky#undef ISSET_FD 447205218Srdivacky } 448205218Srdivacky} 449205218Srdivacky 450205218Srdivackyint 451205218Srdivackymain(int argc, char *argv[]) 452207618Srdivacky{ 453207618Srdivacky const char *pidfile; 454207618Srdivacky pid_t otherpid; 455207618Srdivacky bool foreground; 456207618Srdivacky int debuglevel; 457207618Srdivacky 458207618Srdivacky g_gate_load(); 459207618Srdivacky 460210299Sed foreground = false; 461212904Sdim debuglevel = 0; 462210299Sed pidfile = HASTD_PIDFILE; 463210299Sed 464205218Srdivacky for (;;) { 465205218Srdivacky int ch; 466205218Srdivacky 467205218Srdivacky ch = getopt(argc, argv, "c:dFhP:"); 468205218Srdivacky if (ch == -1) 469205218Srdivacky break; 470205218Srdivacky switch (ch) { 471205218Srdivacky case 'c': 472205218Srdivacky cfgpath = optarg; 473205218Srdivacky break; 474205218Srdivacky case 'd': 475205218Srdivacky debuglevel++; 476205218Srdivacky break; 477205218Srdivacky case 'F': 478205218Srdivacky foreground = true; 479205218Srdivacky break; 480 case 'P': 481 pidfile = optarg; 482 break; 483 case 'h': 484 default: 485 usage(); 486 } 487 } 488 argc -= optind; 489 argv += optind; 490 491 pjdlog_debug_set(debuglevel); 492 493 pfh = pidfile_open(pidfile, 0600, &otherpid); 494 if (pfh == NULL) { 495 if (errno == EEXIST) { 496 pjdlog_exitx(EX_TEMPFAIL, 497 "Another hastd is already running, pid: %jd.", 498 (intmax_t)otherpid); 499 } 500 /* If we cannot create pidfile from other reasons, only warn. */ 501 pjdlog_errno(LOG_WARNING, "Cannot open or create pidfile"); 502 } 503 504 cfg = yy_config_parse(cfgpath); 505 assert(cfg != NULL); 506 507 signal(SIGHUP, sighandler); 508 signal(SIGCHLD, sighandler); 509 510 /* Listen on control address. */ 511 if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) { 512 KEEP_ERRNO((void)pidfile_remove(pfh)); 513 pjdlog_exit(EX_OSERR, "Unable to listen on control address %s", 514 cfg->hc_controladdr); 515 } 516 /* Listen for remote connections. */ 517 if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) { 518 KEEP_ERRNO((void)pidfile_remove(pfh)); 519 pjdlog_exit(EX_OSERR, "Unable to listen on address %s", 520 cfg->hc_listenaddr); 521 } 522 523 if (!foreground) { 524 if (daemon(0, 0) < 0) { 525 KEEP_ERRNO((void)pidfile_remove(pfh)); 526 pjdlog_exit(EX_OSERR, "Unable to daemonize"); 527 } 528 529 /* Start logging to syslog. */ 530 pjdlog_mode_set(PJDLOG_MODE_SYSLOG); 531 532 /* Write PID to a file. */ 533 if (pidfile_write(pfh) < 0) { 534 pjdlog_errno(LOG_WARNING, 535 "Unable to write PID to a file"); 536 } 537 } 538 539 main_loop(); 540 541 exit(0); 542} 543