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