1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) 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 /* relay signal to child */
76 static void log_reopen_handler(int num)
83 static void sigusr2_handler(int num)
85 child_got_signal_from_us = 1;
88 static pid_t keepalive_pid = 0;
90 static void keepalive(void (*work)(void *data), void *data)
98 void (*old_sigterm)(int);
99 void (*old_sigusr1)(int);
100 struct sigaction sa2, sa1;
102 keepalive_pid = getpid();
104 /* keep signals in their original state and make sure that some signals
105 to parent process also gets sent to the child.. */
106 old_sigterm = signal(SIGTERM, normal_stop_handler);
107 old_sigusr1 = signal(SIGUSR1, normal_stop_handler);
109 sigemptyset(&sa2.sa_mask);
110 sa2.sa_handler = sigusr2_handler;
112 sigaction(SIGUSR2, &sa2, &sa1);
114 while (cont && !child_got_signal_from_us)
119 if (p == (pid_t) (-1))
121 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
127 signal(SIGTERM, old_sigterm);/* restore */
128 signal(SIGUSR1, old_sigusr1);/* restore */
129 sigaction(SIGUSR2, &sa1, NULL);
135 /* enable signalling in kill_child_handler */
138 p1 = waitpid(p, &status, 0);
140 /* disable signalling in kill_child_handler */
143 if (p1 == (pid_t)(-1))
147 yaz_log(YLOG_FATAL|YLOG_ERRNO, "waitpid");
154 yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
157 if (WIFSIGNALED(status))
159 /* keep the child alive in case of errors, but _log_ */
160 switch (WTERMSIG(status))
163 yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
168 yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
173 yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
178 yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
183 yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
188 yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
189 WTERMSIG(status), (long) p);
193 else if (WIFEXITED(status))
196 if (WEXITSTATUS(status) != 0)
197 { /* child exited with error */
198 yaz_log(YLOG_LOG, "Exit %d from child %ld",
199 WEXITSTATUS(status), (long) p);
202 if (cont) /* respawn slower as we get more errors */
207 yaz_log(YLOG_WARN, "keepalive stop. %d SIGILL signal(s)", no_sigill);
209 yaz_log(YLOG_WARN, "keepalive stop. %d SIGABRT signal(s)", no_sigabrt);
211 yaz_log(YLOG_WARN, "keepalive stop. %d SIGSEGV signal(s)", no_sigsegv);
213 yaz_log(YLOG_WARN, "keepalive stop. %d SIGBUS signal(s)", no_sigbus);
214 yaz_log(YLOG_LOG, "keepalive stop");
218 void yaz_daemon_stop(void)
222 kill(keepalive_pid, SIGUSR2); /* invoke immediate_exit_handler */
227 int yaz_daemon(const char *progname,
229 void (*work)(void *data), void *data,
230 const char *pidfile, const char *uid)
235 /* open pidfile .. defer write until in child and after setuid */
238 pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666);
241 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile);
246 if (flags & YAZ_DAEMON_DEBUG)
248 /* in debug mode.. it's quite simple */
249 write_pidfile(pid_fd);
254 /* running in production mode. */
257 /* OK to use the non-thread version here */
258 struct passwd *pw = getpwnam(uid);
261 yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
264 if (flags & YAZ_DAEMON_LOG_REOPEN)
266 FILE *f = yaz_log_file();
269 if (fchown(fileno(f), pw->pw_uid, -1))
270 yaz_log(YLOG_WARN|YLOG_ERRNO, "fchown logfile");
273 if (setuid(pw->pw_uid) < 0)
275 yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
278 /* Linux don't produce core dumps evern if the limit is right and
279 files are writable.. This fixes this. See prctl(2) */
281 #ifdef PR_SET_DUMPABLE
282 prctl(PR_SET_DUMPABLE, 1, 0, 0);
287 if (flags & YAZ_DAEMON_FORK)
289 /* create pipe so that parent waits until child has created
291 static int hand[2]; /* hand shake for child */
294 yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
308 int res = read(hand[0], dummy, 1);
309 if (res < 0 && errno != EINTR)
311 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
328 open("/dev/null", O_RDWR);
336 write_pidfile(pid_fd);
338 if (flags & YAZ_DAEMON_LOG_REOPEN)
340 signal(SIGHUP, log_reopen_handler);
342 if (flags & YAZ_DAEMON_KEEPALIVE)
344 keepalive(work, data);
360 * c-file-style: "Stroustrup"
361 * indent-tabs-mode: nil
363 * vim: shiftwidth=4 tabstop=8 expandtab