Better check of marc leader info YAZ-691
[yaz-moved-to-github.git] / src / daemon.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file
8  * \brief Unix daemon management
9  */
10
11 #if HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <signal.h>
16 #include <string.h>
17 #include <errno.h>
18 #if HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <stdlib.h>
22 #if HAVE_SYS_WAIT_H
23 #include <sys/wait.h>
24 #endif
25
26 #if HAVE_SYS_TYPES_H
27 #include <sys/types.h>
28 #endif
29
30 #include <fcntl.h>
31
32 #if HAVE_PWD_H
33 #include <pwd.h>
34 #endif
35
36 #if HAVE_SYS_PRCTL_H
37 #include <sys/prctl.h>
38 #endif
39
40 #include <yaz/daemon.h>
41 #include <yaz/log.h>
42 #include <yaz/snprintf.h>
43
44 #if HAVE_PWD_H
45 static void write_pidfile(int pid_fd)
46 {
47     if (pid_fd != -1)
48     {
49         char buf[40];
50         yaz_snprintf(buf, sizeof(buf), "%ld", (long) getpid());
51         if (ftruncate(pid_fd, 0))
52         {
53             yaz_log(YLOG_FATAL|YLOG_ERRNO, "ftruncate");
54             exit(1);
55         }
56         if (write(pid_fd, buf, strlen(buf)) != (int) strlen(buf))
57         {
58             yaz_log(YLOG_FATAL|YLOG_ERRNO, "write");
59             exit(1);
60         }
61         close(pid_fd);
62     }
63 }
64
65 int child_got_signal_from_us = 0;
66 pid_t child_pid = 0;
67 static void normal_stop_handler(int num)
68 {
69     if (child_pid)
70     {
71         /* relay signal to child */
72         child_got_signal_from_us = 1;
73         kill(child_pid, num);
74     }
75 }
76
77 static void immediate_exit_handler(int num)
78 {
79     _exit(0);
80 }
81
82 static pid_t keepalive_pid = 0;
83
84 static void keepalive(void (*work)(void *data), void *data)
85 {
86     int no_sigill = 0;
87     int no_sigabrt = 0;
88     int no_sigsegv = 0;
89     int no_sigbus = 0;
90     int run = 1;
91     int cont = 1;
92     void (*old_sighup)(int);
93     void (*old_sigterm)(int);
94     void (*old_sigusr1)(int);
95     void (*old_sigusr2)(int);
96
97     keepalive_pid = getpid();
98
99     /* keep signals in their original state and make sure that some signals
100        to parent process also gets sent to the child..  */
101     old_sighup = signal(SIGHUP, normal_stop_handler);
102     old_sigterm = signal(SIGTERM, normal_stop_handler);
103     old_sigusr1 = signal(SIGUSR1, normal_stop_handler);
104     old_sigusr2 = signal(SIGUSR2, immediate_exit_handler);
105     while (cont && !child_got_signal_from_us)
106     {
107         pid_t p = fork();
108         pid_t p1;
109         int status;
110         if (p == (pid_t) (-1))
111         {
112             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
113             exit(1);
114         }
115         else if (p == 0)
116         {
117             /* child */
118             signal(SIGHUP, old_sighup);  /* restore */
119             signal(SIGTERM, old_sigterm);/* restore */
120             signal(SIGUSR1, old_sigusr1);/* restore */
121             signal(SIGUSR2, old_sigusr2);/* restore */
122
123             work(data);
124             exit(0);
125         }
126
127         /* enable signalling in kill_child_handler */
128         child_pid = p;
129
130         p1 = wait(&status);
131
132         /* disable signalling in kill_child_handler */
133         child_pid = 0;
134
135         if (p1 != p)
136         {
137             yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
138             exit(1);
139         }
140
141         if (WIFSIGNALED(status))
142         {
143             /*  keep the child alive in case of errors, but _log_ */
144             switch (WTERMSIG(status))
145             {
146             case SIGILL:
147                 yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
148                 cont = 1;
149                 no_sigill++;
150                 break;
151             case SIGABRT:
152                 yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
153                 cont = 1;
154                 no_sigabrt++;
155                 break ;
156             case SIGSEGV:
157                 yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
158                 cont = 1;
159                 ++no_sigsegv;
160                 break;
161             case SIGBUS:
162                 yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
163                 cont = 1;
164                 no_sigbus++;
165                 break;
166             case SIGTERM:
167                 yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
168                         (long) p);
169                 cont = 0;
170                 break;
171             default:
172                 yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
173                         WTERMSIG(status), (long) p);
174                 cont = 0;
175             }
176         }
177         else if (WIFEXITED(status))
178         {
179             cont = 0;
180             if (WEXITSTATUS(status) != 0)
181             {   /* child exited with error */
182                 yaz_log(YLOG_LOG, "Exit %d from child %ld",
183                         WEXITSTATUS(status), (long) p);
184             }
185         }
186         if (cont) /* respawn slower as we get more errors */
187             sleep(1 + run/5);
188         run++;
189     }
190     if (no_sigill)
191         yaz_log(YLOG_WARN, "keepalive stop. %d SIGILL signal(s)", no_sigill);
192     if (no_sigabrt)
193         yaz_log(YLOG_WARN, "keepalive stop. %d SIGABRT signal(s)", no_sigabrt);
194     if (no_sigsegv)
195         yaz_log(YLOG_WARN, "keepalive stop. %d SIGSEGV signal(s)", no_sigsegv);
196     if (no_sigbus)
197         yaz_log(YLOG_WARN, "keepalive stop. %d SIGBUS signal(s)", no_sigbus);
198 }
199 #endif
200
201 void yaz_daemon_stop(void)
202 {
203 #if HAVE_PWD_H
204     if (keepalive_pid)
205         kill(keepalive_pid, SIGUSR2); /* invoke immediate_exit_handler */
206 #endif
207 }
208
209
210 int yaz_daemon(const char *progname,
211                unsigned int flags,
212                void (*work)(void *data), void *data,
213                const char *pidfile, const char *uid)
214 {
215 #if HAVE_PWD_H
216     int pid_fd = -1;
217
218     /* open pidfile .. defer write until in child and after setuid */
219     if (pidfile)
220     {
221         pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666);
222         if (pid_fd == -1)
223         {
224             yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile);
225             exit(1);
226         }
227     }
228
229     if (flags & YAZ_DAEMON_DEBUG)
230     {
231         /* in debug mode.. it's quite simple */
232         write_pidfile(pid_fd);
233         work(data);
234         exit(0);
235     }
236
237     /* running in production mode. */
238     if (uid)
239     {
240         /* OK to use the non-thread version here */
241         struct passwd *pw = getpwnam(uid);
242         if (!pw)
243         {
244             yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
245             exit(1);
246         }
247         if (setuid(pw->pw_uid) < 0)
248         {
249             yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
250             exit(1);
251         }
252         /* Linux don't produce core dumps evern if the limit is right and
253            files are writable.. This fixes this. See prctl(2) */
254 #if HAVE_SYS_PRCTL_H
255 #ifdef PR_SET_DUMPABLE
256         prctl(PR_SET_DUMPABLE, 1, 0, 0);
257 #endif
258 #endif
259     }
260
261     if (flags & YAZ_DAEMON_FORK)
262     {
263         /* create pipe so that parent waits until child has created
264            PID (or failed) */
265         static int hand[2]; /* hand shake for child */
266         if (pipe(hand) < 0)
267         {
268             yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
269             return 1;
270         }
271         switch (fork())
272         {
273         case 0:
274             break;
275         case -1:
276             return 1;
277         default:
278             close(hand[1]);
279             while(1)
280             {
281                 char dummy[1];
282                 int res = read(hand[0], dummy, 1);
283                 if (res < 0 && errno != EINTR)
284                 {
285                     yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
286                     break;
287                 }
288                 else if (res >= 0)
289                     break;
290             }
291             close(hand[0]);
292             _exit(0);
293         }
294         /* child */
295         close(hand[0]);
296         if (setsid() < 0)
297             return 1;
298
299         close(0);
300         close(1);
301         close(2);
302         open("/dev/null", O_RDWR);
303         if (dup(0) == -1)
304             return 1;
305         if (dup(0) == -1)
306             return 1;
307         close(hand[1]);
308     }
309
310     write_pidfile(pid_fd);
311
312     if (flags & YAZ_DAEMON_KEEPALIVE)
313     {
314         keepalive(work, data);
315     }
316     else
317     {
318         work(data);
319     }
320     return 0;
321 #else
322     work(data);
323     return 0;
324 #endif
325 }
326
327 /*
328  * Local variables:
329  * c-basic-offset: 4
330  * c-file-style: "Stroustrup"
331  * indent-tabs-mode: nil
332  * End:
333  * vim: shiftwidth=4 tabstop=8 expandtab
334  */
335