From 906a9c59f8c77f4002616a34b0b98d91d8b1bbc5 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 18 Feb 2008 17:07:05 +0000 Subject: [PATCH 1/1] Added daemon utility (yaz_daemon). --- include/yaz/Makefile.am | 4 +- include/yaz/daemon.h | 61 +++++++++++ src/Makefile.am | 4 +- src/daemon.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 include/yaz/daemon.h create mode 100644 src/daemon.c diff --git a/include/yaz/Makefile.am b/include/yaz/Makefile.am index 76a8e0a..a5d4fef 100644 --- a/include/yaz/Makefile.am +++ b/include/yaz/Makefile.am @@ -1,4 +1,4 @@ -## $Id: Makefile.am,v 1.50 2007-11-09 16:46:43 adam Exp $ +## $Id: Makefile.am,v 1.51 2008-02-18 17:07:05 adam Exp $ noinst_HEADERS = icu_I18N.h @@ -11,7 +11,7 @@ pkginclude_HEADERS= backend.h ccl.h ccl_xml.h cql.h comstack.h \ yaz-ccl.h yaz-iconv.h yaz-util.h yaz-version.h yconfig.h proto.h \ xmlquery.h libxml2_error.h xmltypes.h snprintf.h query-charset.h \ mutex.h oid_db.h oid_util.h oid_std.h tokenizer.h copy_types.h \ - icu.h match_glob.h poll.h \ + icu.h match_glob.h poll.h daemon.h \ \ ill.h ill-core.h item-req.h oclc-ill-req-ext.h z-accdes1.h z-accform1.h \ z-acckrb1.h z-core.h z-date.h z-diag1.h z-espec1.h z-estask.h z-exp.h \ diff --git a/include/yaz/daemon.h b/include/yaz/daemon.h new file mode 100644 index 0000000..981327d --- /dev/null +++ b/include/yaz/daemon.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1995-2008, Index Data + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Index Data nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* $Id: daemon.h,v 1.1 2008-02-18 17:07:05 adam Exp $ */ + +/** + * \file daemon.h + * \brief Unix daemon management + */ + +#ifndef YAZ_DAEMON_H +#define YAZ_DAEMON_H + +#include +#include + +YAZ_BEGIN_CDECL + +#define YAZ_DAEMON_FORK 1 +#define YAZ_DAEMON_DEBUG 2 +#define YAZ_DAEMON_KEEPALIVE 4 + +int yaz_daemon(const char *progname, + unsigned int flags, + void (*work)(void *data), void *data, + const char *pidfile, const char *uid); + +YAZ_END_CDECL + +#endif +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ + diff --git a/src/Makefile.am b/src/Makefile.am index bab47ee..03dbca2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ ## This file is part of the YAZ toolkit. ## Copyright (C) 1995-2007, Index Data, All rights reserved. -## $Id: Makefile.am,v 1.72 2008-01-17 14:44:57 adam Exp $ +## $Id: Makefile.am,v 1.73 2008-02-18 17:07:06 adam Exp $ YAZ_VERSION_INFO=3:0:0 @@ -95,7 +95,7 @@ libyaz_la_SOURCES=version.c options.c log.c \ xmlquery.c http.c \ mime.c mime.h oid_util.c tokenizer.c \ record_conv.c retrieval.c elementset.c snprintf.c query-charset.c \ - copy_types.c match_glob.c poll.c + copy_types.c match_glob.c poll.c daemon.c libyaz_la_LDFLAGS=-version-info $(YAZ_VERSION_INFO) diff --git a/src/daemon.c b/src/daemon.c new file mode 100644 index 0000000..a3a6ab1 --- /dev/null +++ b/src/daemon.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 1995-2008, Index Data ApS + * See the file LICENSE for details. + * + * $Id: daemon.c,v 1.1 2008-02-18 17:07:06 adam Exp $ + */ + +/** + * \file + * \brief Unix daemon management + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void write_pidfile(int pid_fd) +{ + if (pid_fd != -1) + { + char buf[40]; + yaz_snprintf(buf, sizeof(buf), "%ld", (long) getpid()); + if (ftruncate(pid_fd, 0)) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "ftruncate"); + exit(1); + } + if (write(pid_fd, buf, strlen(buf)) != strlen(buf)) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "write"); + exit(1); + } + close(pid_fd); + } +} + +pid_t child_pid = 0; +static void kill_child_handler(int num) +{ + if (child_pid) + kill(child_pid, num); +} + +static void keepalive(void (*work)(void *data), void *data) +{ + int run = 1; + int cont = 1; + void (*old_sighup)(int); + void (*old_sigterm)(int); + + /* keep signals in their original state and make sure that some signals + to parent process also gets sent to the child.. + */ + old_sighup = signal(SIGHUP, kill_child_handler); + old_sigterm = signal(SIGTERM, kill_child_handler); + while (cont) + { + pid_t p = fork(); + pid_t p1; + int status; + if (p == (pid_t) (-1)) + { + + yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork"); + exit(1); + } + else if (p == 0) + { + /* child */ + signal(SIGHUP, old_sighup); /* restore */ + signal(SIGTERM, old_sigterm);/* restore */ + + work(data); + exit(0); + } + + /* enable signalling in kill_child_handler */ + child_pid = p; + + p1 = wait(&status); + + /* disable signalling in kill_child_handler */ + child_pid = 0; + + if (p1 != p) + { + yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p); + exit(1); + } + + if (WIFSIGNALED(status)) + { + /* keep the child alive in case of errors, but _log_ */ + switch(WTERMSIG(status)) { + case SIGILL: + yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p); + cont = 1; + break; + case SIGABRT: + yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p); + cont = 1; + break ; + case SIGSEGV: + yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p); + cont = 1; + break; + case SIGBUS: + yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p); + cont = 1; + break; + case SIGTERM: + yaz_log(YLOG_LOG, "Received SIGTERM from child %ld", + (long) p); + cont = 0; + break; + default: + yaz_log(YLOG_WARN, "Received SIG %d from child %ld", + WTERMSIG(status), (long) p); + cont = 0; + } + } + else if (status == 0) + cont = 0; /* child exited normally */ + else + { /* child exited with error */ + yaz_log(YLOG_LOG, "Exit %d from child %ld", status, (long) p); + cont = 0; + } + if (cont) /* respawn slower as we get more errors */ + sleep(1 + run/5); + run++; + } +} + +int yaz_daemon(const char *progname, + unsigned int flags, + void (*work)(void *data), void *data, + const char *pidfile, const char *uid) +{ + int pid_fd = -1; + + /* open pidfile .. defer write until in child and after setuid */ + if (pidfile) + { + pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666); + if (pid_fd == -1) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile); + exit(1); + } + } + + if (flags & YAZ_DAEMON_DEBUG) + { + /* in debug mode.. it's quite simple */ + write_pidfile(pid_fd); + work(data); + exit(0); + } + + /* running in production mode. */ + if (uid) + { + /* OK to use the non-thread version here */ + struct passwd *pw = getpwnam(uid); + if (!pw) + { + yaz_log(YLOG_FATAL, "%s: Unknown user", uid); + exit(1); + } + if (setuid(pw->pw_uid) < 0) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid"); + exit(1); + } + } + + if (flags & YAZ_DAEMON_FORK) + { + /* create pipe so that parent waits until child has created + PID (or failed) */ + static int hand[2]; /* hand shake for child */ + if (pipe(hand) < 0) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe"); + return 1; + } + switch (fork()) + { + case 0: + break; + case -1: + return 1; + default: + close(hand[1]); + while(1) + { + char dummy[1]; + int res = read(hand[0], dummy, 1); + if (res < 0 && errno != EINTR) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake"); + break; + } + else if (res >= 0) + break; + } + close(hand[0]); + _exit(0); + } + /* child */ + close(hand[0]); + if (setsid() < 0) + return 1; + + close(0); + close(1); + close(2); + open("/dev/null", O_RDWR); + dup(0); dup(0); + close(hand[1]); + } + + write_pidfile(pid_fd); + + if (flags & YAZ_DAEMON_KEEPALIVE) + { + keepalive(work, data); + } + else + { + work(data); + } + return 0; +} + +/* + * Local variables: + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ -- 1.7.10.4