9a97da7877c63442618888511873274be2e9bf0f
[pazpar2-moved-to-github.git] / src / process.c
1 /* $Id: process.c,v 1.1 2007-06-08 13:57:19 adam Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4 This file is part of Pazpar2.
5
6 Pazpar2 is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Pazpar2; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #if HAVE_CONFIG_H
23 #include "cconfig.h"
24 #endif
25
26 #include <signal.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <sys/wait.h>
32
33 #include <yaz/log.h>
34
35 #include "pazpar2.h"
36
37 static void write_pidfile(const char *pidfile)
38 {
39     if (pidfile)
40     {
41         FILE *f = fopen(pidfile, "w");
42         if (!f)
43         {
44             yaz_log(YLOG_ERRNO|YLOG_FATAL, "Couldn't create %s", pidfile);
45             exit(0);
46         }
47         fprintf(f, "%ld", (long) getpid());
48         fclose(f);
49     }
50 }
51
52 pid_t child_pid = 0;
53 void kill_child_handler(int num)
54 {
55     if (child_pid)
56         kill(child_pid, num);
57 }
58
59 int pazpar2_process(int debug, int flags,
60                     void (*work)(void *data), void *data,
61                     const char *pidfile, const char *uid /* not yet used */)
62 {
63     int run = 1;
64     int cont = 1;
65     void (*old_sighup)(int);
66     void (*old_sigterm)(int);
67
68
69     if (debug)
70     {
71         /* in debug mode.. it's quite simple */
72         write_pidfile(pidfile);
73         work(data);
74         exit(0);
75     }
76     /* running in production mode. */
77
78     /* keep signals in their original state and make sure that some signals
79        to parent process also gets sent to the child.. Normally this
80        should not happen. We want the _child_ process to be terminated
81        normally. However, if the parent process is terminated, we
82        kill the child too */
83     old_sighup = signal(SIGHUP, kill_child_handler);
84     old_sigterm = signal(SIGTERM, kill_child_handler);
85     while (cont)
86     {
87         pid_t p = fork();
88         pid_t p1;
89         int status;
90         if (p == (pid_t) (-1))
91         {
92             
93             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
94             exit(1);
95         }
96         else if (p == 0)
97         {
98             /* child */
99             signal(SIGHUP, old_sighup);  /* restore */
100             signal(SIGTERM, old_sigterm);/* restore */
101
102             write_pidfile(pidfile);
103             work(data);
104             exit(0);
105         }
106
107         /* enable signalling in kill_child_handler */
108         child_pid = p;
109         
110         p1 = wait(&status);
111         yaz_log_reopen();
112
113         /* disable signalling in kill_child_handler */
114         child_pid = 0;
115
116         if (p1 != p)
117         {
118             yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
119             exit(1);
120         }
121         
122         if (WIFSIGNALED(status))
123         {
124             /*  keep the child alive in case of errors, but _log_ */
125             switch(WTERMSIG(status)) {
126             case SIGILL:
127                 yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
128                 cont = 1;
129                 break;
130             case SIGABRT:
131                 yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
132                 cont = 1;
133                 break ;
134             case SIGSEGV:
135                 yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
136                 cont = 1;
137                 break;
138             case SIGBUS:        
139                 yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
140                 cont = 1;
141                 break;
142             case SIGTERM:
143                 yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
144                         (long) p);
145                 cont = 0;
146                 break;
147             default:
148                 yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
149                         WTERMSIG(status), (long) p);
150                 cont = 0;
151             }
152         }
153         else if (status == 0)
154             cont = 0; /* child exited normally */
155         else
156         {   /* child exited in an abnormal way */
157             yaz_log(YLOG_LOG, "Exit %d from child %ld", status, (long) p);
158             cont = 1;
159         }
160         if (cont) /* respawn slower as we get more errors */
161             sleep(1 + run/5);
162         run++;
163     }
164     return 0;
165 }
166
167 /*
168  * Local variables:
169  * c-basic-offset: 4
170  * indent-tabs-mode: nil
171  * End:
172  * vim: shiftwidth=4 tabstop=8 expandtab
173  */