1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) 1995-2012 Index Data
3 * See the file LICENSE for details.
8 * \brief Unix daemon management
27 #include <sys/types.h>
37 #include <sys/prctl.h>
40 #include <yaz/daemon.h>
42 #include <yaz/snprintf.h>
45 static void write_pidfile(int pid_fd)
50 yaz_snprintf(buf, sizeof(buf), "%ld", (long) getpid());
51 if (ftruncate(pid_fd, 0))
53 yaz_log(YLOG_FATAL|YLOG_ERRNO, "ftruncate");
56 if (write(pid_fd, buf, strlen(buf)) != (int) strlen(buf))
58 yaz_log(YLOG_FATAL|YLOG_ERRNO, "write");
65 int child_got_signal_from_us = 0;
67 static void normal_stop_handler(int num)
71 /* tell child to terminate . ensure keepalive stops running -
72 let wait(2) wait once - so we exit AFTER the child */
73 child_got_signal_from_us = 1;
78 static void graceful_stop_handler(int num)
82 /* tell our child to stop listening and wait some in time
83 in the hope that it has stopped listening by then. Then
84 we exit - quite possibly the child is still running - serving
92 static void keepalive(void (*work)(void *data), void *data)
96 void (*old_sighup)(int);
97 void (*old_sigterm)(int);
98 void (*old_sigusr1)(int);
100 /* keep signals in their original state and make sure that some signals
101 to parent process also gets sent to the child..
103 old_sighup = signal(SIGHUP, normal_stop_handler);
104 old_sigterm = signal(SIGTERM, normal_stop_handler);
105 old_sigusr1 = signal(SIGUSR1, graceful_stop_handler);
106 while (cont && !child_got_signal_from_us)
111 if (p == (pid_t) (-1))
114 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
120 signal(SIGHUP, old_sighup); /* restore */
121 signal(SIGTERM, old_sigterm);/* restore */
122 signal(SIGUSR1, old_sigusr1);/* restore */
128 /* enable signalling in kill_child_handler */
133 /* disable signalling in kill_child_handler */
138 yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
142 if (WIFSIGNALED(status))
144 /* keep the child alive in case of errors, but _log_ */
145 switch(WTERMSIG(status)) {
147 yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
151 yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
155 yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
159 yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
163 yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
168 yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
169 WTERMSIG(status), (long) p);
173 else if (status == 0)
174 cont = 0; /* child exited normally */
176 { /* child exited with error */
177 yaz_log(YLOG_LOG, "Exit %d from child %ld", status, (long) p);
180 if (cont) /* respawn slower as we get more errors */
187 int yaz_daemon(const char *progname,
189 void (*work)(void *data), void *data,
190 const char *pidfile, const char *uid)
195 /* open pidfile .. defer write until in child and after setuid */
198 pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666);
201 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile);
206 if (flags & YAZ_DAEMON_DEBUG)
208 /* in debug mode.. it's quite simple */
209 write_pidfile(pid_fd);
214 /* running in production mode. */
217 /* OK to use the non-thread version here */
218 struct passwd *pw = getpwnam(uid);
221 yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
224 if (setuid(pw->pw_uid) < 0)
226 yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
229 /* Linux don't produce core dumps evern if the limit is right and
230 files are writable.. This fixes this. See prctl(2) */
232 #ifdef PR_SET_DUMPABLE
233 prctl(PR_SET_DUMPABLE, 1, 0, 0);
238 if (flags & YAZ_DAEMON_FORK)
240 /* create pipe so that parent waits until child has created
242 static int hand[2]; /* hand shake for child */
245 yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
259 int res = read(hand[0], dummy, 1);
260 if (res < 0 && errno != EINTR)
262 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
279 open("/dev/null", O_RDWR);
287 write_pidfile(pid_fd);
289 if (flags & YAZ_DAEMON_KEEPALIVE)
291 keepalive(work, data);
307 * c-file-style: "Stroustrup"
308 * indent-tabs-mode: nil
310 * vim: shiftwidth=4 tabstop=8 expandtab