YAZ 5.35.1
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
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{
81 if (child_pid)
82 kill(child_pid, num);
83}
84
85static void sigusr2_handler(int num)
86{
87 child_got_signal_from_us = 1;
88}
89
90static pid_t keepalive_pid = 0;
91
92static void keepalive(void (*work)(void *data), void *data)
93{
94 int no_sigill = 0;
95 int no_sigabrt = 0;
96 int no_sigsegv = 0;
97 int no_sigbus = 0;
98 int run = 1;
99 int cont = 1;
100 void (*old_sigterm)(int);
101 void (*old_sigusr1)(int);
102 struct sigaction sa2, sa1;
103
104 keepalive_pid = getpid();
105
106 /* keep signals in their original state and make sure that some signals
107 to parent process also gets sent to the child.. */
108 old_sigterm = signal(SIGTERM, normal_stop_handler);
109 old_sigusr1 = signal(SIGUSR1, normal_stop_handler);
110
111 sigemptyset(&sa2.sa_mask);
112 sa2.sa_handler = sigusr2_handler;
113 sa2.sa_flags = 0;
114 sigaction(SIGUSR2, &sa2, &sa1);
115
116 while (cont && !child_got_signal_from_us)
117 {
118 pid_t p = fork();
119 pid_t p1;
120 int status;
121 if (p == (pid_t) (-1))
122 {
124 exit(1);
125 }
126 else if (p == 0)
127 {
128 /* child */
129 signal(SIGTERM, old_sigterm);/* restore */
130 signal(SIGUSR1, old_sigusr1);/* restore */
131 sigaction(SIGUSR2, &sa1, NULL);
132
133 work(data);
134 exit(0);
135 }
136
137 /* enable signalling in kill_child_handler */
138 child_pid = p;
139
140 // wait for child to finish and check status
141 while ((p1 = waitpid(p, &status, 0) == (pid_t ) (-1)) && errno == EINTR)
142 ;
143
144 if (p1 == (pid_t) (-1))
145 {
146 yaz_log(YLOG_FATAL|YLOG_ERRNO, "waitpid");
147 break;
148 }
149
150 /* disable signalling in kill_child_handler */
151 child_pid = 0;
152
153 if (p1 != p)
154 {
155 yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
156 break;
157 }
158 if (WIFSIGNALED(status))
159 {
160 /* keep the child alive in case of errors, but _log_ */
161 switch (WTERMSIG(status))
162 {
163 case SIGILL:
164 yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
165 cont = 1;
166 no_sigill++;
167 break;
168 case SIGABRT:
169 yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
170 cont = 1;
171 no_sigabrt++;
172 break ;
173 case SIGSEGV:
174 yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
175 cont = 1;
176 ++no_sigsegv;
177 break;
178 case SIGBUS:
179 yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
180 cont = 1;
181 no_sigbus++;
182 break;
183 case SIGTERM:
184 yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
185 (long) p);
186 cont = 0;
187 break;
188 default:
189 yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
190 WTERMSIG(status), (long) p);
191 cont = 0;
192 }
193 }
194 else if (WIFEXITED(status))
195 {
196 cont = 0;
197 if (WEXITSTATUS(status) != 0)
198 { /* child exited with error */
199 yaz_log(YLOG_LOG, "Exit %d from child %ld",
200 WEXITSTATUS(status), (long) p);
201 }
202 }
203 if (cont) /* respawn slower as we get more errors */
204 sleep(1 + run/5);
205 run++;
206 }
207 if (no_sigill)
208 yaz_log(YLOG_WARN, "keepalive stop. %d SIGILL signal(s)", no_sigill);
209 if (no_sigabrt)
210 yaz_log(YLOG_WARN, "keepalive stop. %d SIGABRT signal(s)", no_sigabrt);
211 if (no_sigsegv)
212 yaz_log(YLOG_WARN, "keepalive stop. %d SIGSEGV signal(s)", no_sigsegv);
213 if (no_sigbus)
214 yaz_log(YLOG_WARN, "keepalive stop. %d SIGBUS signal(s)", no_sigbus);
215 yaz_log(YLOG_LOG, "keepalive stop");
216}
217#endif
218
220{
221#if HAVE_PWD_H
222 if (keepalive_pid)
223 kill(keepalive_pid, SIGUSR2); /* invoke immediate_exit_handler */
224#endif
225}
226
227
228int yaz_daemon(const char *progname,
229 unsigned int flags,
230 void (*work)(void *data), void *data,
231 const char *pidfile, const char *uid)
232{
233#if HAVE_PWD_H
234 int pid_fd = -1;
235
236 /* open pidfile .. defer write until in child and after setuid */
237 if (pidfile)
238 {
239 pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666);
240 if (pid_fd == -1)
241 {
242 yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile);
243 exit(1);
244 }
245 }
246
247 if (flags & YAZ_DAEMON_DEBUG)
248 {
249 /* in debug mode.. it's quite simple */
250 write_pidfile(pid_fd);
251 work(data);
252 exit(0);
253 }
254
255 /* running in production mode. */
256 if (uid)
257 {
258 /* OK to use the non-thread version here */
259 struct passwd *pw = getpwnam(uid);
260 if (!pw)
261 {
262 yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
263 exit(1);
264 }
265 if (flags & YAZ_DAEMON_LOG_REOPEN)
266 {
267 FILE *f = yaz_log_file();
268 if (f)
269 {
270 if (fchown(fileno(f), pw->pw_uid, -1))
271 yaz_log(YLOG_WARN|YLOG_ERRNO, "fchown logfile");
272 }
273 }
274 if (setuid(pw->pw_uid) < 0)
275 {
276 yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
277 exit(1);
278 }
279 /* Linux don't produce core dumps evern if the limit is right and
280 files are writable.. This fixes this. See prctl(2) */
281#if HAVE_SYS_PRCTL_H
282#ifdef PR_SET_DUMPABLE
283 prctl(PR_SET_DUMPABLE, 1, 0, 0);
284#endif
285#endif
286 }
287
288 if (flags & YAZ_DAEMON_FORK)
289 {
290 /* create pipe so that parent waits until child has created
291 PID (or failed) */
292 static int hand[2]; /* hand shake for child */
293 if (pipe(hand) < 0)
294 {
296 return 1;
297 }
298 switch (fork())
299 {
300 case 0:
301 break;
302 case -1:
303 return 1;
304 default:
305 close(hand[1]);
306 while(1)
307 {
308 char dummy[1];
309 int res = read(hand[0], dummy, 1);
310 if (res < 0 && errno != EINTR)
311 {
312 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
313 break;
314 }
315 else if (res >= 0)
316 break;
317 }
318 close(hand[0]);
319 _exit(0);
320 }
321 /* child */
322 close(hand[0]);
323 if (setsid() < 0)
324 return 1;
325
326 close(0);
327 close(1);
328 close(2);
329 open("/dev/null", O_RDWR);
330 if (dup(0) == -1)
331 return 1;
332 if (dup(0) == -1)
333 return 1;
334 close(hand[1]);
335 }
336
337 write_pidfile(pid_fd);
338
339 if (flags & YAZ_DAEMON_LOG_REOPEN)
340 {
341 signal(SIGHUP, log_reopen_handler);
342 }
343 if (flags & YAZ_DAEMON_KEEPALIVE)
344 {
345 keepalive(work, data);
346 }
347 else
348 {
349 work(data);
350 }
351 return 0;
352#else
353 work(data);
354 return 0;
355#endif
356}
357
358/*
359 * Local variables:
360 * c-basic-offset: 4
361 * c-file-style: "Stroustrup"
362 * indent-tabs-mode: nil
363 * End:
364 * vim: shiftwidth=4 tabstop=8 expandtab
365 */
366
void yaz_daemon_stop(void)
stop daemon - stop parent process
Definition daemon.c:219
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:228
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:1260
const char * progname
Definition stemwords.c:12
Header with fundamental macros.