- added missing register_fds & get_max_open_fds() declarations
[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  *  2007-07-04  added register_fds() and get_max_open_fds(() (andrei)
39  */
40
41
42 #include "pt.h"
43 #include "tcp_init.h"
44 #include "sr_module.h"
45 #include "rand/fastrand.h"
46
47 #include <stdio.h>
48 #include <time.h> /* time(), used to initialize random numbers */
49
50 #define FORK_DONT_WAIT  /* child doesn't wait for parent before starting 
51                                                    => faster startup, but the child should not assume
52                                                    the parent fixed the pt[] entry for it */
53
54
55 #ifdef PROFILING
56 #include <sys/gmon.h>
57
58         extern void _start(void);
59         extern void etext(void);
60 #endif
61
62
63 static int estimated_proc_no=0;
64 static int estimated_fds_no=0;
65
66
67 /* number of known "common" used fds */
68 static int calc_common_open_fds_no()
69 {
70         int max_fds_no;
71         
72         /* 1 tcp send unix socket/all_proc, 
73          *  + 1 udp sock/udp proc + 1 possible dns comm. socket + 
74          *  + 1 temporary tcp send sock.
75          */
76         max_fds_no=estimated_proc_no*4 /* udp + tcp unix sock + tmp. tcp send +
77                                                                           tmp dns.*/ -1 /* timer (no udp)*/ + 
78                                 3 /* stdin/out/err */;
79         return max_fds_no;
80 }
81
82
83
84 /* returns 0 on success, -1 on error */
85 int init_pt(int proc_no)
86 {
87 #ifdef USE_TCP
88         int r;
89 #endif
90         
91         estimated_proc_no+=proc_no;
92         estimated_fds_no+=calc_common_open_fds_no();
93         /*alloc pids*/
94 #ifdef SHM_MEM
95         pt=shm_malloc(sizeof(struct process_table)*estimated_proc_no);
96         process_count = shm_malloc(sizeof(int));
97 #else
98         pt=pkg_malloc(sizeof(struct process_table)*estimated_proc_no);
99         process_count = pkg_malloc(sizeof(int));
100 #endif
101         process_lock = lock_alloc();
102         process_lock = lock_init(process_lock);
103         if (pt==0||process_count==0||process_lock==0){
104                 LOG(L_ERR, "ERROR: out  of memory\n");
105                 return -1;
106         }
107         memset(pt, 0, sizeof(struct process_table)*estimated_proc_no);
108 #ifdef USE_TCP
109         for (r=0; r<estimated_proc_no; r++){
110                 pt[r].unix_sock=-1;
111                 pt[r].idx=-1;
112         }
113 #endif
114         process_no=0; /*main process number*/
115         pt[process_no].pid=getpid();
116         memcpy(pt[process_no].desc,"main",5);
117         *process_count=1;
118         return 0;
119 }
120
121
122 /* register no processes, used from mod_init when processes will be forked
123  *  from mod_child 
124  *  returns 0 on success, -1 on error
125  */
126 int register_procs(int no)
127 {
128         if (pt){
129                 LOG(L_CRIT, "BUG: register_procs(%d) called at runtime\n", no);
130                 return -1;
131         }
132         estimated_proc_no+=no;
133         return 0;
134 }
135
136
137
138 /* returns the maximum number of processes */
139 int get_max_procs()
140 {
141         if (pt==0){
142                 LOG(L_CRIT, "BUG: get_max_procs() called too early "
143                                 "(it must _not_ be called from mod_init())\n");
144                 abort(); /* crash to quickly catch offenders */
145         }
146         return estimated_proc_no;
147 }
148
149
150 /* register no fds, used from mod_init when modules will open more global
151  *  fds (from mod_init or child_init(PROC_INIT)
152  *  or from child_init(rank) when the module will open fds local to the
153  *   process "rank".
154  *   (this is needed because some other parts of ser code rely on knowing
155  *    the maximum open fd number in a process)
156  *  returns 0 on success, -1 on error
157  */
158 int register_fds(int no)
159 {
160         /* can be called at runtime, but should be called from child_init() */
161         estimated_fds_no+=no;
162         return 0;
163 }
164
165
166
167 /* returns the maximum open fd number */
168 int get_max_open_fds()
169 {
170         if (pt==0){
171                 LOG(L_CRIT, "BUG: get_max_open_fds() called too early "
172                                 "(it must _not_ be called from mod_init())\n");
173                 abort(); /* crash to quickly catch offenders */
174         }
175         return estimated_fds_no;
176 }
177
178
179 /* return processes pid */
180 int my_pid()
181 {
182         return pt ? pt[process_no].pid : getpid();
183 }
184
185
186
187 /**
188  * Forks a new process.
189  * @param child_id - rank, if equal to PROC_NOCHLDINIT init_child will not be
190  *                   called for the new forked process (see sr_module.h)
191  * @param desc - text description for the process table
192  * @param make_sock - if to create a unix socket pair for it
193  * @returns the pid of the new process
194  */
195 int fork_process(int child_id, char *desc, int make_sock)
196 {
197         int pid, child_process_no;
198         int ret;
199         unsigned int new_seed1;
200         unsigned int new_seed2;
201 #ifdef USE_TCP
202         int sockfd[2];
203 #endif
204
205         ret=-1;
206         #ifdef USE_TCP
207                 sockfd[0]=sockfd[1]=-1;
208                 if(make_sock && !tcp_disable){
209                          if (!is_main){
210                                  LOG(L_CRIT, "BUG: fork_process(..., 1) called from a non "
211                                                  "\"main\" process! If forking from a module's "
212                                                  "child_init() fork only if rank==PROC_MAIN or"
213                                                  " give up tcp send support (use 0 for make_sock)\n");
214                                  goto error;
215                          }
216                          if (tcp_main_pid){
217                                  LOG(L_CRIT, "BUG: fork_process(..., 1) called, but tcp main "
218                                                  " is already started\n");
219                                  goto error;
220                          }
221                          if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
222                                 LOG(L_ERR, "ERROR: fork_process(): socketpair failed: %s\n",
223                                                         strerror(errno));
224                                 goto error;
225                         }
226                 }
227         #endif
228         lock_get(process_lock);
229         if (*process_count>=estimated_proc_no) {
230                 LOG(L_CRIT, "ERROR: fork_process(): Process limit of %d exceeded."
231                                         " Will simulate fork fail.\n", estimated_proc_no);
232                 lock_release(process_lock);
233                 goto error;
234         }       
235         
236         
237         child_process_no = *process_count;
238         new_seed1=rand();
239         new_seed2=random();
240         pid = fork();
241         if (pid<0) {
242                 lock_release(process_lock);
243                 ret=pid;
244                 goto error;
245         }else if (pid==0){
246                 /* child */
247                 is_main=0; /* a forked process cannot be the "main" one */
248                 process_no=child_process_no;
249                 srand(new_seed1);
250                 fastrand_seed(rand());
251                 srandom(new_seed2+time(0));
252                 shm_malloc_on_fork();
253 #ifdef PROFILING
254                 monstartup((u_long) &_start, (u_long) &etext);
255 #endif
256 #ifdef FORK_DONT_WAIT
257                 /* record pid twice to avoid the child using it, before
258                  * parent gets a chance to set it*/
259                 pt[process_no].pid=getpid();
260 #else
261                 /* wait for parent to get out of critical zone.
262                  * this is actually relevant as the parent updates
263                  * the pt & process_count. */
264                 lock_get(process_lock);
265                 lock_release(process_lock);
266 #endif
267                 #ifdef USE_TCP
268                         if (make_sock && !tcp_disable){
269                                 close(sockfd[0]);
270                                 unix_tcp_sock=sockfd[1];
271                         }
272                 #endif          
273                 if ((child_id!=PROC_NOCHLDINIT) && (init_child(child_id) < 0)) {
274                         LOG(L_ERR, "ERROR: fork_process(): init_child failed for "
275                                         " process %d, pid %d, \"%s\"\n", process_no,
276                                         pt[process_no].pid, pt[process_no].desc);
277                         return -1;
278                 }
279                 return pid;
280         } else {
281                 /* parent */
282                 (*process_count)++;
283 #ifdef FORK_DONT_WAIT
284                 lock_release(process_lock);
285 #endif
286                 /* add the process to the list in shm */
287                 pt[child_process_no].pid=pid;
288                 if (desc){
289                         strncpy(pt[child_process_no].desc, desc, MAX_PT_DESC);
290                 }
291                 #ifdef USE_TCP
292                         if (make_sock && !tcp_disable){
293                                 close(sockfd[1]);
294                                 pt[child_process_no].unix_sock=sockfd[0];
295                                 pt[child_process_no].idx=-1; /* this is not a "tcp" process*/
296                         }
297                 #endif
298 #ifdef FORK_DONT_WAIT
299 #else
300                 lock_release(process_lock);
301 #endif
302                 ret=pid;
303                 goto end;
304         }
305 error:
306 #ifdef USE_TCP
307         if (sockfd[0]!=-1) close(sockfd[0]);
308         if (sockfd[1]!=-1) close(sockfd[1]);
309 #endif
310 end:
311         return ret;
312 }
313
314 /**
315  * Forks a new TCP process.
316  * @param desc - text description for the process table
317  * @param r - index in the tcp_children array
318  * @param *reader_fd_1 - pointer to return the reader_fd[1]
319  * @returns the pid of the new process
320  */
321 #ifdef USE_TCP
322 int fork_tcp_process(int child_id, char *desc, int r, int *reader_fd_1)
323 {
324         int pid, child_process_no;
325         int sockfd[2];
326         int reader_fd[2]; /* for comm. with the tcp children read  */
327         int ret;
328         unsigned int new_seed1;
329         unsigned int new_seed2;
330         
331         /* init */
332         sockfd[0]=sockfd[1]=-1;
333         reader_fd[0]=reader_fd[1]=-1;
334         ret=-1;
335         
336         if (!is_main){
337                  LOG(L_CRIT, "BUG: fork_tcp_process() called from a non \"main\" "
338                                         "process\n");
339                  goto error;
340          }
341          if (tcp_main_pid){
342                  LOG(L_CRIT, "BUG: fork_tcp_process(..., 1) called _after_ starting"
343                                         " tcp main\n");
344                  goto error;
345          }
346         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
347                 LOG(L_ERR, "ERROR: fork_tcp_process(): socketpair failed: %s\n",
348                                         strerror(errno));
349                 goto error;
350         }
351         if (socketpair(AF_UNIX, SOCK_STREAM, 0, reader_fd)<0){
352                 LOG(L_ERR, "ERROR: fork_tcp_process(): socketpair failed: %s\n",
353                                         strerror(errno));
354                 goto error;
355         }
356         if (tcp_fix_child_sockets(reader_fd)<0){
357                 LOG(L_ERR, "ERROR: fork_tcp_process(): failed to set non blocking"
358                                         "on child sockets\n");
359                 /* continue, it's not critical (it will go slower under
360                  * very high connection rates) */
361         }
362         lock_get(process_lock);
363         /* set the local process_no */
364         if (*process_count>=estimated_proc_no) {
365                 LOG(L_CRIT, "ERROR: fork_tcp_process(): Process limit of %d exceeded."
366                                         " Simulating fork fail\n", estimated_proc_no);
367                 lock_release(process_lock);
368                 goto error;
369         }
370         
371         
372         child_process_no = *process_count;
373         new_seed1=rand();
374         new_seed2=random();
375         pid = fork();
376         if (pid<0) {
377                 lock_release(process_lock);
378                 ret=pid;
379                 goto end;
380         }
381         if (pid==0){
382                 is_main=0; /* a forked process cannot be the "main" one */
383                 process_no=child_process_no;
384                 srand(new_seed1);
385                 fastrand_seed(rand());
386                 srandom(new_seed2+time(0));
387                 shm_malloc_on_fork();
388 #ifdef PROFILING
389                 monstartup((u_long) &_start, (u_long) &etext);
390 #endif
391 #ifdef FORK_DONT_WAIT
392                 /* record pid twice to avoid the child using it, before
393 -                * parent gets a chance to set it*/
394                 pt[process_no].pid=getpid();
395 #else
396                 /* wait for parent to get out of critical zone */
397                 lock_get(process_lock);
398                 lock_release(process_lock);
399 #endif
400                 close(sockfd[0]);
401                 unix_tcp_sock=sockfd[1];
402                 close(reader_fd[0]);
403                 if (reader_fd_1) *reader_fd_1=reader_fd[1];
404                 if ((child_id!=PROC_NOCHLDINIT) && (init_child(child_id) < 0)) {
405                         LOG(L_ERR, "ERROR: fork_tcp_process(): init_child failed for "
406                                         "process %d, pid %d, \"%s\"\n", process_no, 
407                                         pt[process_no].pid, pt[process_no].desc);
408                         return -1;
409                 }
410                 return pid;
411         } else {
412                 /* parent */
413                 (*process_count)++;
414 #ifdef FORK_DONT_WAIT
415                 lock_release(process_lock);
416 #endif
417                 /* add the process to the list in shm */
418                 pt[child_process_no].pid=pid;
419                 pt[child_process_no].unix_sock=sockfd[0];
420                 pt[child_process_no].idx=r;
421                 if (desc){
422                         snprintf(pt[child_process_no].desc, MAX_PT_DESC, "%s child=%d", 
423                                                 desc, r);
424                 }
425 #ifdef FORK_DONT_WAIT
426 #else
427                 lock_release(process_lock);
428 #endif
429                 
430                 close(sockfd[1]);
431                 close(reader_fd[1]);
432                 
433                 tcp_children[r].pid=pid;
434                 tcp_children[r].proc_no=child_process_no;
435                 tcp_children[r].busy=0;
436                 tcp_children[r].n_reqs=0;
437                 tcp_children[r].unix_sock=reader_fd[0];
438                 
439                 ret=pid;
440                 goto end;
441         }
442 error:
443         if (sockfd[0]!=-1) close(sockfd[0]);
444         if (sockfd[1]!=-1) close(sockfd[1]);
445         if (reader_fd[0]!=-1) close(reader_fd[0]);
446         if (reader_fd[1]!=-1) close(reader_fd[1]);
447 end:
448         return ret;
449 }
450 #endif