YAZ  5.34.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 
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
47 static 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  {
60  yaz_log(YLOG_FATAL|YLOG_ERRNO, "write");
61  exit(1);
62  }
63  close(pid_fd);
64  }
65 }
66 
67 int child_got_signal_from_us = 0;
68 pid_t child_pid = 0;
69 static 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 
78 static void log_reopen_handler(int num)
79 {
81  if (child_pid)
82  kill(child_pid, num);
83 }
84 
85 static void sigusr2_handler(int num)
86 {
87  child_got_signal_from_us = 1;
88 }
89 
90 static pid_t keepalive_pid = 0;
91 
92 static 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  {
123  yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
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  p1 = waitpid(p, &status, 0);
141 
142  /* disable signalling in kill_child_handler */
143  child_pid = 0;
144 
145  if (p1 == (pid_t)(-1))
146  {
147  if (errno != EINTR)
148  {
149  yaz_log(YLOG_FATAL|YLOG_ERRNO, "waitpid");
150  break;
151  }
152  continue;
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  cont = 1;
167  no_sigill++;
168  break;
169  case SIGABRT:
170  yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
171  cont = 1;
172  no_sigabrt++;
173  break ;
174  case SIGSEGV:
175  yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
176  cont = 1;
177  ++no_sigsegv;
178  break;
179  case SIGBUS:
180  yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
181  cont = 1;
182  no_sigbus++;
183  break;
184  case SIGTERM:
185  yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
186  (long) p);
187  cont = 0;
188  break;
189  default:
190  yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
191  WTERMSIG(status), (long) p);
192  cont = 0;
193  }
194  }
195  else if (WIFEXITED(status))
196  {
197  cont = 0;
198  if (WEXITSTATUS(status) != 0)
199  { /* child exited with error */
200  yaz_log(YLOG_LOG, "Exit %d from child %ld",
201  WEXITSTATUS(status), (long) p);
202  }
203  }
204  if (cont) /* respawn slower as we get more errors */
205  sleep(1 + run/5);
206  run++;
207  }
208  if (no_sigill)
209  yaz_log(YLOG_WARN, "keepalive stop. %d SIGILL signal(s)", no_sigill);
210  if (no_sigabrt)
211  yaz_log(YLOG_WARN, "keepalive stop. %d SIGABRT signal(s)", no_sigabrt);
212  if (no_sigsegv)
213  yaz_log(YLOG_WARN, "keepalive stop. %d SIGSEGV signal(s)", no_sigsegv);
214  if (no_sigbus)
215  yaz_log(YLOG_WARN, "keepalive stop. %d SIGBUS signal(s)", no_sigbus);
216  yaz_log(YLOG_LOG, "keepalive stop");
217 }
218 #endif
219 
220 void yaz_daemon_stop(void)
221 {
222 #if HAVE_PWD_H
223  if (keepalive_pid)
224  kill(keepalive_pid, SIGUSR2); /* invoke immediate_exit_handler */
225 #endif
226 }
227 
228 
229 int yaz_daemon(const char *progname,
230  unsigned int flags,
231  void (*work)(void *data), void *data,
232  const char *pidfile, const char *uid)
233 {
234 #if HAVE_PWD_H
235  int pid_fd = -1;
236 
237  /* open pidfile .. defer write until in child and after setuid */
238  if (pidfile)
239  {
240  pid_fd = open(pidfile, O_CREAT|O_RDWR, 0666);
241  if (pid_fd == -1)
242  {
243  yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s", pidfile);
244  exit(1);
245  }
246  }
247 
248  if (flags & YAZ_DAEMON_DEBUG)
249  {
250  /* in debug mode.. it's quite simple */
251  write_pidfile(pid_fd);
252  work(data);
253  exit(0);
254  }
255 
256  /* running in production mode. */
257  if (uid)
258  {
259  /* OK to use the non-thread version here */
260  struct passwd *pw = getpwnam(uid);
261  if (!pw)
262  {
263  yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
264  exit(1);
265  }
266  if (flags & YAZ_DAEMON_LOG_REOPEN)
267  {
268  FILE *f = yaz_log_file();
269  if (f)
270  {
271  if (fchown(fileno(f), pw->pw_uid, -1))
272  yaz_log(YLOG_WARN|YLOG_ERRNO, "fchown logfile");
273  }
274  }
275  if (setuid(pw->pw_uid) < 0)
276  {
277  yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
278  exit(1);
279  }
280  /* Linux don't produce core dumps evern if the limit is right and
281  files are writable.. This fixes this. See prctl(2) */
282 #if HAVE_SYS_PRCTL_H
283 #ifdef PR_SET_DUMPABLE
284  prctl(PR_SET_DUMPABLE, 1, 0, 0);
285 #endif
286 #endif
287  }
288 
289  if (flags & YAZ_DAEMON_FORK)
290  {
291  /* create pipe so that parent waits until child has created
292  PID (or failed) */
293  static int hand[2]; /* hand shake for child */
294  if (pipe(hand) < 0)
295  {
296  yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
297  return 1;
298  }
299  switch (fork())
300  {
301  case 0:
302  break;
303  case -1:
304  return 1;
305  default:
306  close(hand[1]);
307  while(1)
308  {
309  char dummy[1];
310  int res = read(hand[0], dummy, 1);
311  if (res < 0 && errno != EINTR)
312  {
313  yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
314  break;
315  }
316  else if (res >= 0)
317  break;
318  }
319  close(hand[0]);
320  _exit(0);
321  }
322  /* child */
323  close(hand[0]);
324  if (setsid() < 0)
325  return 1;
326 
327  close(0);
328  close(1);
329  close(2);
330  open("/dev/null", O_RDWR);
331  if (dup(0) == -1)
332  return 1;
333  if (dup(0) == -1)
334  return 1;
335  close(hand[1]);
336  }
337 
338  write_pidfile(pid_fd);
339 
340  if (flags & YAZ_DAEMON_LOG_REOPEN)
341  {
342  signal(SIGHUP, log_reopen_handler);
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:220
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:229
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.
FILE * yaz_log_file(void)
returns FILE handle for log or NULL if no file is in use
Definition: log.c:138
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
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.