hastd.c revision 209177
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/hastd.c 209177 2010-06-14 21:18:58Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> 34204076Spjd#include <sys/linker.h> 35204076Spjd#include <sys/module.h> 36204076Spjd#include <sys/wait.h> 37204076Spjd 38204076Spjd#include <assert.h> 39204076Spjd#include <err.h> 40204076Spjd#include <errno.h> 41204076Spjd#include <libutil.h> 42204076Spjd#include <signal.h> 43204076Spjd#include <stdbool.h> 44204076Spjd#include <stdio.h> 45204076Spjd#include <stdlib.h> 46204076Spjd#include <string.h> 47204076Spjd#include <sysexits.h> 48204076Spjd#include <unistd.h> 49204076Spjd 50204076Spjd#include <activemap.h> 51204076Spjd#include <pjdlog.h> 52204076Spjd 53204076Spjd#include "control.h" 54204076Spjd#include "hast.h" 55204076Spjd#include "hast_proto.h" 56204076Spjd#include "hastd.h" 57204076Spjd#include "subr.h" 58204076Spjd 59204076Spjd/* Path to configuration file. */ 60204076Spjdstatic const char *cfgpath = HAST_CONFIG; 61204076Spjd/* Hastd configuration. */ 62204076Spjdstatic struct hastd_config *cfg; 63204076Spjd/* Was SIGCHLD signal received? */ 64204076Spjdstatic bool sigchld_received = false; 65204076Spjd/* Was SIGHUP signal received? */ 66204076Spjdstatic bool sighup_received = false; 67204076Spjd/* Was SIGINT or SIGTERM signal received? */ 68204076Spjdbool sigexit_received = false; 69204076Spjd/* PID file handle. */ 70204076Spjdstruct pidfh *pfh; 71204076Spjd 72204076Spjdstatic void 73204076Spjdusage(void) 74204076Spjd{ 75204076Spjd 76204076Spjd errx(EX_USAGE, "[-dFh] [-c config] [-P pidfile]"); 77204076Spjd} 78204076Spjd 79204076Spjdstatic void 80204076Spjdsighandler(int sig) 81204076Spjd{ 82204076Spjd 83204076Spjd switch (sig) { 84204076Spjd case SIGCHLD: 85204076Spjd sigchld_received = true; 86204076Spjd break; 87204076Spjd case SIGHUP: 88204076Spjd sighup_received = true; 89204076Spjd break; 90204076Spjd default: 91204076Spjd assert(!"invalid condition"); 92204076Spjd } 93204076Spjd} 94204076Spjd 95204076Spjdstatic void 96204076Spjdg_gate_load(void) 97204076Spjd{ 98204076Spjd 99204076Spjd if (modfind("g_gate") == -1) { 100204076Spjd /* Not present in kernel, try loading it. */ 101204076Spjd if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) { 102204076Spjd if (errno != EEXIST) { 103204076Spjd pjdlog_exit(EX_OSERR, 104204076Spjd "Unable to load geom_gate module"); 105204076Spjd } 106204076Spjd } 107204076Spjd } 108204076Spjd} 109204076Spjd 110204076Spjdstatic void 111207372Spjdchild_exit_log(unsigned int pid, int status) 112207372Spjd{ 113207372Spjd 114207372Spjd if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 115207372Spjd pjdlog_debug(1, "Worker process exited gracefully (pid=%u).", 116207372Spjd pid); 117207372Spjd } else if (WIFSIGNALED(status)) { 118207372Spjd pjdlog_error("Worker process killed (pid=%u, signal=%d).", 119207372Spjd pid, WTERMSIG(status)); 120207372Spjd } else { 121207372Spjd pjdlog_error("Worker process exited ungracefully (pid=%u, exitcode=%d).", 122207372Spjd pid, WIFEXITED(status) ? WEXITSTATUS(status) : -1); 123207372Spjd } 124207372Spjd} 125207372Spjd 126207372Spjdstatic void 127204076Spjdchild_exit(void) 128204076Spjd{ 129204076Spjd struct hast_resource *res; 130204076Spjd int status; 131204076Spjd pid_t pid; 132204076Spjd 133204076Spjd while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 134204076Spjd /* Find resource related to the process that just exited. */ 135204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 136204076Spjd if (pid == res->hr_workerpid) 137204076Spjd break; 138204076Spjd } 139204076Spjd if (res == NULL) { 140204076Spjd /* 141204076Spjd * This can happen when new connection arrives and we 142204076Spjd * cancel child responsible for the old one. 143204076Spjd */ 144204076Spjd continue; 145204076Spjd } 146204076Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, 147204076Spjd role2str(res->hr_role)); 148207372Spjd child_exit_log(pid, status); 149206696Spjd proto_close(res->hr_ctrl); 150204076Spjd res->hr_workerpid = 0; 151204076Spjd if (res->hr_role == HAST_ROLE_PRIMARY) { 152207372Spjd /* 153207372Spjd * Restart child process if it was killed by signal 154207372Spjd * or exited because of temporary problem. 155207372Spjd */ 156207372Spjd if (WIFSIGNALED(status) || 157207372Spjd (WIFEXITED(status) && 158207372Spjd WEXITSTATUS(status) == EX_TEMPFAIL)) { 159207348Spjd sleep(1); 160207348Spjd pjdlog_info("Restarting worker process."); 161207348Spjd hastd_primary(res); 162207348Spjd } else { 163207348Spjd res->hr_role = HAST_ROLE_INIT; 164207348Spjd pjdlog_info("Changing resource role back to %s.", 165207348Spjd role2str(res->hr_role)); 166207348Spjd } 167204076Spjd } 168204076Spjd pjdlog_prefix_set("%s", ""); 169204076Spjd } 170204076Spjd} 171204076Spjd 172204076Spjdstatic void 173204076Spjdhastd_reload(void) 174204076Spjd{ 175204076Spjd 176204076Spjd /* TODO */ 177204076Spjd pjdlog_warning("Configuration reload is not implemented."); 178204076Spjd} 179204076Spjd 180204076Spjdstatic void 181204076Spjdlisten_accept(void) 182204076Spjd{ 183204076Spjd struct hast_resource *res; 184204076Spjd struct proto_conn *conn; 185204076Spjd struct nv *nvin, *nvout, *nverr; 186204076Spjd const char *resname; 187204076Spjd const unsigned char *token; 188204076Spjd char laddr[256], raddr[256]; 189204076Spjd size_t size; 190204076Spjd pid_t pid; 191204076Spjd int status; 192204076Spjd 193204076Spjd proto_local_address(cfg->hc_listenconn, laddr, sizeof(laddr)); 194204076Spjd pjdlog_debug(1, "Accepting connection to %s.", laddr); 195204076Spjd 196204076Spjd if (proto_accept(cfg->hc_listenconn, &conn) < 0) { 197204076Spjd pjdlog_errno(LOG_ERR, "Unable to accept connection %s", laddr); 198204076Spjd return; 199204076Spjd } 200204076Spjd 201204076Spjd proto_local_address(conn, laddr, sizeof(laddr)); 202204076Spjd proto_remote_address(conn, raddr, sizeof(raddr)); 203204076Spjd pjdlog_info("Connection from %s to %s.", laddr, raddr); 204204076Spjd 205207371Spjd /* Error in setting timeout is not critical, but why should it fail? */ 206207371Spjd if (proto_timeout(conn, HAST_TIMEOUT) < 0) 207207371Spjd pjdlog_errno(LOG_WARNING, "Unable to set connection timeout"); 208207371Spjd 209204076Spjd nvin = nvout = nverr = NULL; 210204076Spjd 211204076Spjd /* 212204076Spjd * Before receiving any data see if remote host have access to any 213204076Spjd * resource. 214204076Spjd */ 215204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 216204076Spjd if (proto_address_match(conn, res->hr_remoteaddr)) 217204076Spjd break; 218204076Spjd } 219204076Spjd if (res == NULL) { 220204076Spjd pjdlog_error("Client %s isn't known.", raddr); 221204076Spjd goto close; 222204076Spjd } 223204076Spjd /* Ok, remote host can access at least one resource. */ 224204076Spjd 225204076Spjd if (hast_proto_recv_hdr(conn, &nvin) < 0) { 226204076Spjd pjdlog_errno(LOG_ERR, "Unable to receive header from %s", 227204076Spjd raddr); 228204076Spjd goto close; 229204076Spjd } 230204076Spjd 231204076Spjd resname = nv_get_string(nvin, "resource"); 232204076Spjd if (resname == NULL) { 233204076Spjd pjdlog_error("No 'resource' field in the header received from %s.", 234204076Spjd raddr); 235204076Spjd goto close; 236204076Spjd } 237204076Spjd pjdlog_debug(2, "%s: resource=%s", raddr, resname); 238204076Spjd token = nv_get_uint8_array(nvin, &size, "token"); 239204076Spjd /* 240204076Spjd * NULL token means that this is first conection. 241204076Spjd */ 242204076Spjd if (token != NULL && size != sizeof(res->hr_token)) { 243204076Spjd pjdlog_error("Received token of invalid size from %s (expected %zu, got %zu).", 244204076Spjd raddr, sizeof(res->hr_token), size); 245204076Spjd goto close; 246204076Spjd } 247204076Spjd 248204076Spjd /* 249204076Spjd * From now on we want to send errors to the remote node. 250204076Spjd */ 251204076Spjd nverr = nv_alloc(); 252204076Spjd 253204076Spjd /* Find resource related to this connection. */ 254204076Spjd TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { 255204076Spjd if (strcmp(resname, res->hr_name) == 0) 256204076Spjd break; 257204076Spjd } 258204076Spjd /* Have we found the resource? */ 259204076Spjd if (res == NULL) { 260204076Spjd pjdlog_error("No resource '%s' as requested by %s.", 261204076Spjd resname, raddr); 262204076Spjd nv_add_stringf(nverr, "errmsg", "Resource not configured."); 263204076Spjd goto fail; 264204076Spjd } 265204076Spjd 266204076Spjd /* Now that we know resource name setup log prefix. */ 267204076Spjd pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); 268204076Spjd 269204076Spjd /* Does the remote host have access to this resource? */ 270204076Spjd if (!proto_address_match(conn, res->hr_remoteaddr)) { 271204076Spjd pjdlog_error("Client %s has no access to the resource.", raddr); 272204076Spjd nv_add_stringf(nverr, "errmsg", "No access to the resource."); 273204076Spjd goto fail; 274204076Spjd } 275204076Spjd /* Is the resource marked as secondary? */ 276204076Spjd if (res->hr_role != HAST_ROLE_SECONDARY) { 277204076Spjd pjdlog_error("We act as %s for the resource and not as %s as requested by %s.", 278204076Spjd role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY), 279204076Spjd raddr); 280204076Spjd nv_add_stringf(nverr, "errmsg", 281204076Spjd "Remote node acts as %s for the resource and not as %s.", 282204076Spjd role2str(res->hr_role), role2str(HAST_ROLE_SECONDARY)); 283204076Spjd goto fail; 284204076Spjd } 285204076Spjd /* Does token (if exists) match? */ 286204076Spjd if (token != NULL && memcmp(token, res->hr_token, 287204076Spjd sizeof(res->hr_token)) != 0) { 288204076Spjd pjdlog_error("Token received from %s doesn't match.", raddr); 289204076Spjd nv_add_stringf(nverr, "errmsg", "Toke doesn't match."); 290204076Spjd goto fail; 291204076Spjd } 292204076Spjd /* 293204076Spjd * If there is no token, but we have half-open connection 294204076Spjd * (only remotein) or full connection (worker process is running) 295204076Spjd * we have to cancel those and accept the new connection. 296204076Spjd */ 297204076Spjd if (token == NULL) { 298204076Spjd assert(res->hr_remoteout == NULL); 299204076Spjd pjdlog_debug(1, "Initial connection from %s.", raddr); 300204076Spjd if (res->hr_workerpid != 0) { 301204076Spjd assert(res->hr_remotein == NULL); 302204076Spjd pjdlog_debug(1, 303204076Spjd "Worker process exists (pid=%u), stopping it.", 304204076Spjd (unsigned int)res->hr_workerpid); 305204076Spjd /* Stop child process. */ 306204076Spjd if (kill(res->hr_workerpid, SIGINT) < 0) { 307204076Spjd pjdlog_errno(LOG_ERR, 308204076Spjd "Unable to stop worker process (pid=%u)", 309204076Spjd (unsigned int)res->hr_workerpid); 310204076Spjd /* 311204076Spjd * Other than logging the problem we 312204076Spjd * ignore it - nothing smart to do. 313204076Spjd */ 314204076Spjd } 315204076Spjd /* Wait for it to exit. */ 316204076Spjd else if ((pid = waitpid(res->hr_workerpid, 317204076Spjd &status, 0)) != res->hr_workerpid) { 318207372Spjd /* We can only log the problem. */ 319204076Spjd pjdlog_errno(LOG_ERR, 320204076Spjd "Waiting for worker process (pid=%u) failed", 321204076Spjd (unsigned int)res->hr_workerpid); 322204076Spjd } else { 323207372Spjd child_exit_log(res->hr_workerpid, status); 324204076Spjd } 325204076Spjd res->hr_workerpid = 0; 326204076Spjd } else if (res->hr_remotein != NULL) { 327204076Spjd char oaddr[256]; 328204076Spjd 329204076Spjd proto_remote_address(conn, oaddr, sizeof(oaddr)); 330204076Spjd pjdlog_debug(1, 331204076Spjd "Canceling half-open connection from %s on connection from %s.", 332204076Spjd oaddr, raddr); 333204076Spjd proto_close(res->hr_remotein); 334204076Spjd res->hr_remotein = NULL; 335204076Spjd } 336204076Spjd } 337204076Spjd 338204076Spjd /* 339204076Spjd * Checks and cleanups are done. 340204076Spjd */ 341204076Spjd 342204076Spjd if (token == NULL) { 343204076Spjd arc4random_buf(res->hr_token, sizeof(res->hr_token)); 344204076Spjd nvout = nv_alloc(); 345204076Spjd nv_add_uint8_array(nvout, res->hr_token, 346204076Spjd sizeof(res->hr_token), "token"); 347204076Spjd if (nv_error(nvout) != 0) { 348204076Spjd pjdlog_common(LOG_ERR, 0, nv_error(nvout), 349204076Spjd "Unable to prepare return header for %s", raddr); 350204076Spjd nv_add_stringf(nverr, "errmsg", 351204076Spjd "Remote node was unable to prepare return header: %s.", 352204076Spjd strerror(nv_error(nvout))); 353204076Spjd goto fail; 354204076Spjd } 355204076Spjd if (hast_proto_send(NULL, conn, nvout, NULL, 0) < 0) { 356204076Spjd int error = errno; 357204076Spjd 358204076Spjd pjdlog_errno(LOG_ERR, "Unable to send response to %s", 359204076Spjd raddr); 360204076Spjd nv_add_stringf(nverr, "errmsg", 361204076Spjd "Remote node was unable to send response: %s.", 362204076Spjd strerror(error)); 363204076Spjd goto fail; 364204076Spjd } 365204076Spjd res->hr_remotein = conn; 366204076Spjd pjdlog_debug(1, "Incoming connection from %s configured.", 367204076Spjd raddr); 368204076Spjd } else { 369204076Spjd res->hr_remoteout = conn; 370204076Spjd pjdlog_debug(1, "Outgoing connection to %s configured.", raddr); 371204076Spjd hastd_secondary(res, nvin); 372204076Spjd } 373204076Spjd nv_free(nvin); 374204076Spjd nv_free(nvout); 375204076Spjd nv_free(nverr); 376204076Spjd pjdlog_prefix_set("%s", ""); 377204076Spjd return; 378204076Spjdfail: 379204076Spjd if (nv_error(nverr) != 0) { 380204076Spjd pjdlog_common(LOG_ERR, 0, nv_error(nverr), 381204076Spjd "Unable to prepare error header for %s", raddr); 382204076Spjd goto close; 383204076Spjd } 384204076Spjd if (hast_proto_send(NULL, conn, nverr, NULL, 0) < 0) { 385204076Spjd pjdlog_errno(LOG_ERR, "Unable to send error to %s", raddr); 386204076Spjd goto close; 387204076Spjd } 388204076Spjdclose: 389204076Spjd if (nvin != NULL) 390204076Spjd nv_free(nvin); 391204076Spjd if (nvout != NULL) 392204076Spjd nv_free(nvout); 393204076Spjd if (nverr != NULL) 394204076Spjd nv_free(nverr); 395204076Spjd proto_close(conn); 396204076Spjd pjdlog_prefix_set("%s", ""); 397204076Spjd} 398204076Spjd 399204076Spjdstatic void 400204076Spjdmain_loop(void) 401204076Spjd{ 402204076Spjd fd_set rfds, wfds; 403209177Spjd int cfd, lfd, maxfd, ret; 404204076Spjd 405209177Spjd cfd = proto_descriptor(cfg->hc_controlconn); 406209177Spjd lfd = proto_descriptor(cfg->hc_listenconn); 407209177Spjd maxfd = cfd > lfd ? cfd : lfd; 408209177Spjd 409204076Spjd for (;;) { 410204076Spjd if (sigchld_received) { 411204076Spjd sigchld_received = false; 412204076Spjd child_exit(); 413204076Spjd } 414204076Spjd if (sighup_received) { 415204076Spjd sighup_received = false; 416204076Spjd hastd_reload(); 417204076Spjd } 418204076Spjd 419209177Spjd /* Setup descriptors for select(2). */ 420204076Spjd FD_ZERO(&rfds); 421209177Spjd FD_SET(cfd, &rfds); 422209177Spjd FD_SET(lfd, &rfds); 423204076Spjd FD_ZERO(&wfds); 424209177Spjd FD_SET(cfd, &wfds); 425209177Spjd FD_SET(lfd, &wfds); 426204076Spjd 427204076Spjd ret = select(maxfd + 1, &rfds, &wfds, NULL, NULL); 428204076Spjd if (ret == -1) { 429204076Spjd if (errno == EINTR) 430204076Spjd continue; 431204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 432204076Spjd pjdlog_exit(EX_OSERR, "select() failed"); 433204076Spjd } 434204076Spjd 435209177Spjd if (FD_ISSET(cfd, &rfds) || FD_ISSET(cfd, &wfds)) 436204076Spjd control_handle(cfg); 437209177Spjd if (FD_ISSET(lfd, &rfds) || FD_ISSET(lfd, &wfds)) 438204076Spjd listen_accept(); 439204076Spjd } 440204076Spjd} 441204076Spjd 442204076Spjdint 443204076Spjdmain(int argc, char *argv[]) 444204076Spjd{ 445204076Spjd const char *pidfile; 446204076Spjd pid_t otherpid; 447204076Spjd bool foreground; 448204076Spjd int debuglevel; 449204076Spjd 450204076Spjd g_gate_load(); 451204076Spjd 452204076Spjd foreground = false; 453204076Spjd debuglevel = 0; 454204076Spjd pidfile = HASTD_PIDFILE; 455204076Spjd 456204076Spjd for (;;) { 457204076Spjd int ch; 458204076Spjd 459204076Spjd ch = getopt(argc, argv, "c:dFhP:"); 460204076Spjd if (ch == -1) 461204076Spjd break; 462204076Spjd switch (ch) { 463204076Spjd case 'c': 464204076Spjd cfgpath = optarg; 465204076Spjd break; 466204076Spjd case 'd': 467204076Spjd debuglevel++; 468204076Spjd break; 469204076Spjd case 'F': 470204076Spjd foreground = true; 471204076Spjd break; 472204076Spjd case 'P': 473204076Spjd pidfile = optarg; 474204076Spjd break; 475204076Spjd case 'h': 476204076Spjd default: 477204076Spjd usage(); 478204076Spjd } 479204076Spjd } 480204076Spjd argc -= optind; 481204076Spjd argv += optind; 482204076Spjd 483204076Spjd pjdlog_debug_set(debuglevel); 484204076Spjd 485204076Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 486204076Spjd if (pfh == NULL) { 487204076Spjd if (errno == EEXIST) { 488204076Spjd pjdlog_exitx(EX_TEMPFAIL, 489204076Spjd "Another hastd is already running, pid: %jd.", 490204076Spjd (intmax_t)otherpid); 491204076Spjd } 492204076Spjd /* If we cannot create pidfile from other reasons, only warn. */ 493204076Spjd pjdlog_errno(LOG_WARNING, "Cannot open or create pidfile"); 494204076Spjd } 495204076Spjd 496204076Spjd cfg = yy_config_parse(cfgpath); 497204076Spjd assert(cfg != NULL); 498204076Spjd 499204076Spjd signal(SIGHUP, sighandler); 500204076Spjd signal(SIGCHLD, sighandler); 501204076Spjd 502204076Spjd /* Listen on control address. */ 503204076Spjd if (proto_server(cfg->hc_controladdr, &cfg->hc_controlconn) < 0) { 504204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 505204076Spjd pjdlog_exit(EX_OSERR, "Unable to listen on control address %s", 506204076Spjd cfg->hc_controladdr); 507204076Spjd } 508204076Spjd /* Listen for remote connections. */ 509204076Spjd if (proto_server(cfg->hc_listenaddr, &cfg->hc_listenconn) < 0) { 510204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 511204076Spjd pjdlog_exit(EX_OSERR, "Unable to listen on address %s", 512204076Spjd cfg->hc_listenaddr); 513204076Spjd } 514204076Spjd 515204076Spjd if (!foreground) { 516204076Spjd if (daemon(0, 0) < 0) { 517204076Spjd KEEP_ERRNO((void)pidfile_remove(pfh)); 518204076Spjd pjdlog_exit(EX_OSERR, "Unable to daemonize"); 519204076Spjd } 520204076Spjd 521204076Spjd /* Start logging to syslog. */ 522204076Spjd pjdlog_mode_set(PJDLOG_MODE_SYSLOG); 523204076Spjd 524204076Spjd /* Write PID to a file. */ 525204076Spjd if (pidfile_write(pfh) < 0) { 526204076Spjd pjdlog_errno(LOG_WARNING, 527204076Spjd "Unable to write PID to a file"); 528204076Spjd } 529204076Spjd } 530204076Spjd 531204076Spjd main_loop(); 532204076Spjd 533204076Spjd exit(0); 534204076Spjd} 535