78b6efa4668164ea2ac4598ef68d74033e0ae202
[sip-router] / pt.c
1 /*
2  * $Id$
3  *
4  * Process Table
5  *
6  *
7  *
8  * Copyright (C) 2001-2003 FhG Fokus
9  *
10  * This file is part of ser, a free SIP server.
11  *
12  * ser is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version
16  *
17  * For a license to use the ser software under conditions
18  * other than those described here, or to purchase support for this
19  * software, please contact iptel.org by e-mail at the following addresses:
20  *    info@iptel.org
21  *
22  * ser is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License 
28  * along with this program; if not, write to the Free Software 
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30  */
31 /*
32  * History:
33  * --------
34  *  2006-06-14  added process table in shared mem (dragos)
35  *  2006-09-20  added profile support (-DPROFILING) (hscholz)
36  *  2006-10-25  sanity check before allowing forking w/ tcp support (is_main
37  *               & tcp not started yet); set is_main=0 in childs (andrei)
38  */
39
40
41 #include "pt.h"
42 #include "tcp_init.h"
43 #include "sr_module.h"
44 #include "rand/fastrand.h"
45
46 #include <stdio.h>
47 #include <time.h> /* time(), used to initialize random numbers */
48
49 #define FORK_DONT_WAIT  /* child doesn't wait for parent before starting 
50                                                    => faster startup, but the child should not assume
51                                                    the parent fixed the pt[] entry for it */
52
53
54 #ifdef PROFILING
55 #include <sys/gmon.h>
56
57         extern void _start(void);
58         extern void etext(void);
59 #endif
60
61
62 static int estimated_proc_no=0;
63
64 /* returns 0 on success, -1 on error */
65 int init_pt(int proc_no)
66 {
67 #ifdef USE_TCP
68         int r;
69 #endif
70         
71         estimated_proc_no+=proc_no;
72         /*alloc pids*/
73 #ifdef SHM_MEM
74         pt=shm_malloc(sizeof(struct process_table)*estimated_proc_no);
75         process_count = shm_malloc(sizeof(int));
76 #else
77         pt=pkg_malloc(sizeof(struct process_table)*estimated_proc_no);
78         process_count = pkg_malloc(sizeof(int));
79 #endif
80         process_lock = lock_alloc();
81         process_lock = lock_init(process_lock);
82         if (pt==0||process_count==0||process_lock==0){
83                 LOG(L_ERR, "ERROR: out  of memory\n");
84                 return -1;
85         }
86         memset(pt, 0, sizeof(struct process_table)*estimated_proc_no);
87 #ifdef USE_TCP
88         for (r=0; r<estimated_proc_no; r++){
89                 pt[r].unix_sock=-1;
90                 pt[r].idx=-1;
91         }
92 #endif
93         process_no=0; /*main process number*/
94         pt[process_no].pid=getpid();
95         memcpy(pt[process_no].desc,"main",5);
96         *process_count=1;
97         return 0;
98 }
99
100
101 /* register no processes, used from mod_init when processes will be forked
102  *  from mod_child 
103  *  returns 0 on success, -1 on error
104  */
105 int register_procs(int no)
106 {
107         if (pt){
108                 LOG(L_CRIT, "BUG: register_procs(%d) called at runtime\n", no);
109                 return -1;
110         }
111         estimated_proc_no+=no;
112         return 0;
113 }
114
115
116
117 /* returns the maximum number of processes */
118 int get_max_procs()
119 {
120         if (pt==0){
121                 LOG(L_CRIT, "BUG: get_max_procs() called too early "
122                                 "(it must _not_ be called from mod_init())\n");
123                 abort(); /* crash to quickly catch offenders */
124         }
125         return estimated_proc_no;
126 }
127
128
129 /* return processes pid */
130 int my_pid()
131 {
132         return pt ? pt[process_no].pid : getpid();
133 }
134
135
136
137 /**
138  * Forks a new process.
139  * @param child_id - rank, if equal to PROC_NOCHLDINIT init_child will not be
140  *                   called for the new forked process (see sr_module.h)
141  * @param desc - text description for the process table
142  * @param make_sock - if to create a unix socket pair for it
143  * @returns the pid of the new process
144  */
145 int fork_process(int child_id, char *desc, int make_sock)
146 {
147         int pid, child_process_no;
148         int ret;
149         unsigned int new_seed1;
150         unsigned int new_seed2;
151 #ifdef USE_TCP
152         int sockfd[2];
153 #endif
154
155         ret=-1;
156         #ifdef USE_TCP
157                 sockfd[0]=sockfd[1]=-1;
158                 if(make_sock && !tcp_disable){
159                          if (!is_main){
160                                  LOG(L_CRIT, "BUG: fork_process(..., 1) called from a non "
161                                                  "\"main\" process! If forking from a module's "
162                                                  "child_init() fork only if rank==PROC_MAIN or"
163                                                  " give up tcp send support (use 0 for make_sock)\n");
164                                  goto error;
165                          }
166                          if (tcp_main_pid){
167                                  LOG(L_CRIT, "BUG: fork_process(..., 1) called, but tcp main "
168                                                  " is already started\n");
169                                  goto error;
170                          }
171                          if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
172                                 LOG(L_ERR, "ERROR: fork_process(): socketpair failed: %s\n",
173                                                         strerror(errno));
174                                 goto error;
175                         }
176                 }
177         #endif
178         lock_get(process_lock);
179         if (*process_count>=estimated_proc_no) {
180                 LOG(L_CRIT, "ERROR: fork_process(): Process limit of %d exceeded."
181                                         " Will simulate fork fail.\n", estimated_proc_no);
182                 lock_release(process_lock);
183                 goto error;
184         }       
185         
186         
187         child_process_no = *process_count;
188         new_seed1=rand();
189         new_seed2=random();
190         pid = fork();
191         if (pid<0) {
192                 lock_release(process_lock);
193                 ret=pid;
194                 goto error;
195         }else if (pid==0){
196                 /* child */
197                 is_main=0; /* a forked process cannot be the "main" one */
198                 process_no=child_process_no;
199                 srand(new_seed1);
200                 fastrand_seed(rand());
201                 srandom(new_seed2+time(0));
202                 shm_malloc_on_fork();
203 #ifdef PROFILING
204                 monstartup((u_long) &_start, (u_long) &etext);
205 #endif
206 #ifdef FORK_DONT_WAIT
207                 /* record pid twice to avoid the child using it, before
208                  * parent gets a chance to set it*/
209                 pt[process_no].pid=getpid();
210 #else
211                 /* wait for parent to get out of critical zone.
212                  * this is actually relevant as the parent updates
213                  * the pt & process_count. */
214                 lock_get(process_lock);
215                 lock_release(process_lock);
216 #endif
217                 #ifdef USE_TCP
218                         if (make_sock && !tcp_disable){
219                                 close(sockfd[0]);
220                                 unix_tcp_sock=sockfd[1];
221                         }
222                 #endif          
223                 if ((child_id!=PROC_NOCHLDINIT) && (init_child(child_id) < 0)) {
224                         LOG(L_ERR, "ERROR: fork_process(): init_child failed for "
225                                         " process %d, pid %d, \"%s\"\n", process_no,
226                                         pt[process_no].pid, pt[process_no].desc);
227                         return -1;
228                 }
229                 return pid;
230         } else {
231                 /* parent */
232                 (*process_count)++;
233 #ifdef FORK_DONT_WAIT
234                 lock_release(process_lock);
235 #endif
236                 /* add the process to the list in shm */
237                 pt[child_process_no].pid=pid;
238                 if (desc){
239                         strncpy(pt[child_process_no].desc, desc, MAX_PT_DESC);
240                 }
241                 #ifdef USE_TCP
242                         if (make_sock && !tcp_disable){
243                                 close(sockfd[1]);
244                                 pt[child_process_no].unix_sock=sockfd[0];
245                                 pt[child_process_no].idx=-1; /* this is not a "tcp" process*/
246                         }
247                 #endif
248 #ifdef FORK_DONT_WAIT
249 #else
250                 lock_release(process_lock);
251 #endif
252                 ret=pid;
253                 goto end;
254         }
255 error:
256 #ifdef USE_TCP
257         if (sockfd[0]!=-1) close(sockfd[0]);
258         if (sockfd[1]!=-1) close(sockfd[1]);
259 #endif
260 end:
261         return ret;
262 }
263
264 /**
265  * Forks a new TCP process.
266  * @param desc - text description for the process table
267  * @param r - index in the tcp_children array
268  * @param *reader_fd_1 - pointer to return the reader_fd[1]
269  * @returns the pid of the new process
270  */
271 #ifdef USE_TCP
272 int fork_tcp_process(int child_id, char *desc, int r, int *reader_fd_1)
273 {
274         int pid, child_process_no;
275         int sockfd[2];
276         int reader_fd[2]; /* for comm. with the tcp children read  */
277         int ret;
278         unsigned int new_seed1;
279         unsigned int new_seed2;
280         
281         /* init */
282         sockfd[0]=sockfd[1]=-1;
283         reader_fd[0]=reader_fd[1]=-1;
284         ret=-1;
285         
286         if (!is_main){
287                  LOG(L_CRIT, "BUG: fork_tcp_process() called from a non \"main\" "
288                                         "process\n");
289                  goto error;
290          }
291          if (tcp_main_pid){
292                  LOG(L_CRIT, "BUG: fork_tcp_process(..., 1) called _after_ starting"
293                                         " tcp main\n");
294                  goto error;
295          }
296         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
297                 LOG(L_ERR, "ERROR: fork_tcp_process(): socketpair failed: %s\n",
298                                         strerror(errno));
299                 goto error;
300         }
301         if (socketpair(AF_UNIX, SOCK_STREAM, 0, reader_fd)<0){
302                 LOG(L_ERR, "ERROR: fork_tcp_process(): socketpair failed: %s\n",
303                                         strerror(errno));
304                 goto error;
305         }
306         if (tcp_fix_child_sockets(reader_fd)<0){
307                 LOG(L_ERR, "ERROR: fork_tcp_process(): failed to set non blocking"
308                                         "on child sockets\n");
309                 /* continue, it's not critical (it will go slower under
310                  * very high connection rates) */
311         }
312         lock_get(process_lock);
313         /* set the local process_no */
314         if (*process_count>=estimated_proc_no) {
315                 LOG(L_CRIT, "ERROR: fork_tcp_process(): Process limit of %d exceeded."
316                                         " Simulating fork fail\n", estimated_proc_no);
317                 lock_release(process_lock);
318                 goto error;
319         }
320         
321         
322         child_process_no = *process_count;
323         new_seed1=rand();
324         new_seed2=random();
325         pid = fork();
326         if (pid<0) {
327                 lock_release(process_lock);
328                 ret=pid;
329                 goto end;
330         }
331         if (pid==0){
332                 is_main=0; /* a forked process cannot be the "main" one */
333                 process_no=child_process_no;
334                 srand(new_seed1);
335                 fastrand_seed(rand());
336                 srandom(new_seed2+time(0));
337                 shm_malloc_on_fork();
338 #ifdef PROFILING
339                 monstartup((u_long) &_start, (u_long) &etext);
340 #endif
341 #ifdef FORK_DONT_WAIT
342                 /* record pid twice to avoid the child using it, before
343 -                * parent gets a chance to set it*/
344                 pt[process_no].pid=getpid();
345 #else
346                 /* wait for parent to get out of critical zone */
347                 lock_get(process_lock);
348                 lock_release(process_lock);
349 #endif
350                 close(sockfd[0]);
351                 unix_tcp_sock=sockfd[1];
352                 close(reader_fd[0]);
353                 if (reader_fd_1) *reader_fd_1=reader_fd[1];
354                 if ((child_id!=PROC_NOCHLDINIT) && (init_child(child_id) < 0)) {
355                         LOG(L_ERR, "ERROR: fork_tcp_process(): init_child failed for "
356                                         "process %d, pid %d, \"%s\"\n", process_no, 
357                                         pt[process_no].pid, pt[process_no].desc);
358                         return -1;
359                 }
360                 return pid;
361         } else {
362                 /* parent */
363                 (*process_count)++;
364 #ifdef FORK_DONT_WAIT
365                 lock_release(process_lock);
366 #endif
367                 /* add the process to the list in shm */
368                 pt[child_process_no].pid=pid;
369                 pt[child_process_no].unix_sock=sockfd[0];
370                 pt[child_process_no].idx=r;
371                 if (desc){
372                         snprintf(pt[child_process_no].desc, MAX_PT_DESC, "%s child=%d", 
373                                                 desc, r);
374                 }
375 #ifdef FORK_DONT_WAIT
376 #else
377                 lock_release(process_lock);
378 #endif
379                 
380                 close(sockfd[1]);
381                 close(reader_fd[1]);
382                 
383                 tcp_children[r].pid=pid;
384                 tcp_children[r].proc_no=child_process_no;
385                 tcp_children[r].busy=0;
386                 tcp_children[r].n_reqs=0;
387                 tcp_children[r].unix_sock=reader_fd[0];
388                 
389                 ret=pid;
390                 goto end;
391         }
392 error:
393         if (sockfd[0]!=-1) close(sockfd[0]);
394         if (sockfd[1]!=-1) close(sockfd[1]);
395         if (reader_fd[0]!=-1) close(reader_fd[0]);
396         if (reader_fd[1]!=-1) close(reader_fd[1]);
397 end:
398         return ret;
399 }
400 #endif