Post request possible with pzHttpRequest class.
[pazpar2-moved-to-github.git] / src / process.c
1 /* $Id: process.c,v 1.2 2007-06-12 13:02:38 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 #include <sys/types.h>
33 #include <pwd.h>
34
35 #include <yaz/log.h>
36
37 #include "pazpar2.h"
38
39 static void write_pidfile(const char *pidfile)
40 {
41     if (pidfile)
42     {
43         FILE *f = fopen(pidfile, "w");
44         if (!f)
45         {
46             yaz_log(YLOG_ERRNO|YLOG_FATAL, "Couldn't create %s", pidfile);
47             exit(0);
48         }
49         fprintf(f, "%ld", (long) getpid());
50         fclose(f);
51     }
52 }
53
54 pid_t child_pid = 0;
55 void kill_child_handler(int num)
56 {
57     if (child_pid)
58         kill(child_pid, num);
59 }
60
61 int pazpar2_process(int debug, 
62                     void (*work)(void *data), void *data,
63                     const char *pidfile, const char *uid /* not yet used */)
64 {
65     struct passwd *pw = 0;
66     int run = 1;
67     int cont = 1;
68     void (*old_sighup)(int);
69     void (*old_sigterm)(int);
70
71
72     if (debug)
73     {
74         /* in debug mode.. it's quite simple */
75         write_pidfile(pidfile);
76         work(data);
77         exit(0);
78     }
79     
80     /* running in production mode. */
81     if (uid)
82     {
83         yaz_log(YLOG_LOG, "getpwnam");
84         // OK to use the non-thread version here
85         if (!(pw = getpwnam(uid)))
86         {
87             yaz_log(YLOG_FATAL, "%s: Unknown user", uid);
88             exit(1);
89         }
90     }
91     
92
93     /* keep signals in their original state and make sure that some signals
94        to parent process also gets sent to the child.. Normally this
95        should not happen. We want the _child_ process to be terminated
96        normally. However, if the parent process is terminated, we
97        kill the child too */
98     old_sighup = signal(SIGHUP, kill_child_handler);
99     old_sigterm = signal(SIGTERM, kill_child_handler);
100     while (cont)
101     {
102         pid_t p = fork();
103         pid_t p1;
104         int status;
105         if (p == (pid_t) (-1))
106         {
107             
108             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
109             exit(1);
110         }
111         else if (p == 0)
112         {
113             /* child */
114             signal(SIGHUP, old_sighup);  /* restore */
115             signal(SIGTERM, old_sigterm);/* restore */
116
117             write_pidfile(pidfile);
118
119             if (pw)
120             {
121                 if (setuid(pw->pw_uid) < 0)
122                 {
123                     yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
124                     exit(1);
125                 }
126             }
127
128             work(data);
129             exit(0);
130         }
131
132         /* enable signalling in kill_child_handler */
133         child_pid = p;
134         
135         p1 = wait(&status);
136         yaz_log_reopen();
137
138         /* disable signalling in kill_child_handler */
139         child_pid = 0;
140
141         if (p1 != p)
142         {
143             yaz_log(YLOG_FATAL, "p1=%d != p=%d", p1, p);
144             exit(1);
145         }
146         
147         if (WIFSIGNALED(status))
148         {
149             /*  keep the child alive in case of errors, but _log_ */
150             switch(WTERMSIG(status)) {
151             case SIGILL:
152                 yaz_log(YLOG_WARN, "Received SIGILL from child %ld", (long) p);
153                 cont = 1;
154                 break;
155             case SIGABRT:
156                 yaz_log(YLOG_WARN, "Received SIGABRT from child %ld", (long) p);
157                 cont = 1;
158                 break ;
159             case SIGSEGV:
160                 yaz_log(YLOG_WARN, "Received SIGSEGV from child %ld", (long) p);
161                 cont = 1;
162                 break;
163             case SIGBUS:        
164                 yaz_log(YLOG_WARN, "Received SIGBUS from child %ld", (long) p);
165                 cont = 1;
166                 break;
167             case SIGTERM:
168                 yaz_log(YLOG_LOG, "Received SIGTERM from child %ld",
169                         (long) p);
170                 cont = 0;
171                 break;
172             default:
173                 yaz_log(YLOG_WARN, "Received SIG %d from child %ld",
174                         WTERMSIG(status), (long) p);
175                 cont = 0;
176             }
177         }
178         else if (status == 0)
179             cont = 0; /* child exited normally */
180         else
181         {   /* child exited with error */
182             yaz_log(YLOG_LOG, "Exit %d from child %ld", status, (long) p);
183             cont = 0;
184         }
185         if (cont) /* respawn slower as we get more errors */
186             sleep(1 + run/5);
187         run++;
188     }
189     return 0;
190 }
191
192 /*
193  * Local variables:
194  * c-basic-offset: 4
195  * indent-tabs-mode: nil
196  * End:
197  * vim: shiftwidth=4 tabstop=8 expandtab
198  */