- added ser profiling patch from Hendrik Scholz <hendrik.scholz@freenet-ag.de>,
[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  */
37
38
39 #include "pt.h"
40 #include "tcp_init.h"
41 #include "sr_module.h"
42
43 #include <stdio.h>
44 #ifdef PROFILING
45 #include <sys/gmon.h>
46
47         extern void _start(void);
48         extern void etext(void);
49 #endif
50
51
52 static int estimated_proc_no=0;
53
54 /* returns 0 on success, -1 on error */
55 int init_pt(int proc_no)
56 {
57         estimated_proc_no+=proc_no;
58         /*alloc pids*/
59 #ifdef SHM_MEM
60         pt=shm_malloc(sizeof(struct process_table)*estimated_proc_no);
61         process_count = shm_malloc(sizeof(int));
62 #else
63         pt=pkg_malloc(sizeof(struct process_table)*estimated_proc_no);
64         process_count = pkg_malloc(sizeof(int));
65 #endif
66         process_lock = lock_alloc();
67         process_lock = lock_init(process_lock);
68         if (pt==0||process_count==0||process_lock==0){
69                 LOG(L_ERR, "ERROR: out  of memory\n");
70                 return -1;
71         }
72         memset(pt, 0, sizeof(struct process_table)*estimated_proc_no);
73
74         process_no=0; /*main process number*/
75         pt[process_no].pid=getpid();
76         memcpy(pt[*process_count].desc,"main",5);
77         *process_count=1;
78         return 0;
79 }
80
81
82 /* register no processes, used from mod_init when processes will be forked
83  *  from mod_child 
84  *  returns 0 on success, -1 on error
85  */
86 int register_procs(int no)
87 {
88         if (pt){
89                 LOG(L_CRIT, "BUG: register_procs(%d) called at runtime\n", no);
90                 return -1;
91         }
92         estimated_proc_no+=no;
93         return 0;
94 }
95
96
97
98 /* returns the maximum number of processes */
99 int get_max_procs()
100 {
101         return estimated_proc_no;
102 }
103
104
105 /* return processes pid */
106 int my_pid()
107 {
108         return pt ? pt[process_no].pid : getpid();
109 }
110
111 /**
112  * Forks a new process.
113  * @param child_id - rank, if equal to PROC_NOCHLDINIT init_child will not be
114  *                   called for the new forked process (see sr_module.h)
115  * @param desc - text description for the process table
116  * @param make_sock - if to create a unix socket pair for it
117  * @returns the pid of the new process
118  */
119 int fork_process(int child_id, char *desc, int make_sock)
120 {
121         int pid,old_process_no;
122 #ifdef USE_TCP
123         int sockfd[2];
124 #endif
125
126         lock_get(process_lock); 
127         if (*process_count>=estimated_proc_no) {
128                 LOG(L_CRIT, "ERROR: fork_process(): Process limit of %d exceeded."
129                                         " Will simulate fork fail.\n", estimated_proc_no);
130                 lock_release(process_lock);
131                 return -1;
132         }       
133         
134         #ifdef USE_TCP
135                 if(make_sock && !tcp_disable){
136                          if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
137                                 LOG(L_ERR, "ERROR: fork_process(): socketpair failed: %s\n",
138                                         strerror(errno));
139                                 return -1;
140                         }
141                 }
142         #endif
143         
144         old_process_no = process_no;
145         process_no = *process_count;
146         pid = fork();
147         if (pid<0) {
148                 lock_release(process_lock);
149                 return pid;
150         }
151         if (pid==0){
152                 /* child */
153 #ifdef PROFILING
154                 monstartup((u_long) &_start, (u_long) &etext);
155 #endif
156                 /* wait for parent to get out of critical zone.
157                  * this is actually relevant as the parent updates
158                  * the pt & process_count. */
159                 lock_get(process_lock);
160                 #ifdef USE_TCP
161                         if (make_sock && !tcp_disable){
162                                 close(sockfd[0]);
163                                 unix_tcp_sock=sockfd[1];
164                         }
165                 #endif          
166                 lock_release(process_lock);     
167                 if ((child_id!=PROC_NOCHLDINIT) && (init_child(child_id) < 0)) {
168                         LOG(L_ERR, "ERROR: fork_process(): init_child failed for %s\n",
169                                                 pt[process_no].desc);
170                         return -1;
171                 }
172                 return pid;
173         } else {
174                 /* parent */
175                 process_no = old_process_no;
176                 /* add the process to the list in shm */
177                 pt[*process_count].pid=pid;
178                 if (desc){
179                         strncpy(pt[*process_count].desc, desc, MAX_PT_DESC);
180                 }
181                 #ifdef USE_TCP
182                         if (make_sock && !tcp_disable){
183                                 close(sockfd[1]);
184                                 pt[*process_count].unix_sock=sockfd[0];
185                                 pt[*process_count].idx=-1; /* this is not "tcp" process*/
186                         }
187                 #endif          
188                 *process_count = (*process_count) +1;
189                 lock_release(process_lock);
190                 return pid;
191         }
192 }
193
194 /**
195  * Forks a new TCP process.
196  * @param desc - text description for the process table
197  * @param r - index in the tcp_children array
198  * @param *reader_fd_1 - pointer to return the reader_fd[1]
199  * @returns the pid of the new process
200  */
201 #ifdef USE_TCP
202 int fork_tcp_process(int child_id,char *desc,int r,int *reader_fd_1)
203 {
204         int pid,old_process_no;
205         int sockfd[2];
206         int reader_fd[2]; /* for comm. with the tcp children read  */
207
208
209         
210         lock_get(process_lock);
211         /* set the local process_no */
212         if (*process_count>=estimated_proc_no) {
213                 LOG(L_CRIT, "ERROR: fork_tcp_process(): Process limit of %d exceeded."
214                                         " Simulating fork fail\n", estimated_proc_no);
215                 return -1;
216         }       
217         
218         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
219                 LOG(L_ERR, "ERROR: fork_tcp_process(): socketpair failed: %s\n",
220                                         strerror(errno));
221                 return -1;
222         }
223         if (socketpair(AF_UNIX, SOCK_STREAM, 0, reader_fd)<0){
224                 LOG(L_ERR, "ERROR: fork_tcp_process(): socketpair failed: %s\n",
225                                         strerror(errno));
226                 return -1;
227         }
228         if (tcp_fix_child_sockets(reader_fd)<0){
229                 LOG(L_ERR, "ERROR: fork_tcp_process(): failed to set non blocking"
230                                         "on child sockets\n");
231                 /* continue, it's not critical (it will go slower under
232                  * very high connection rates) */
233         }
234         
235         old_process_no = process_no;
236         process_no = *process_count;
237         pid = fork();
238         if (pid<0) {
239                 lock_release(process_lock);
240                 return pid;
241         }
242         if (pid==0){
243 #ifdef PROFILING
244                 monstartup((u_long) &_start, (u_long) &etext);
245 #endif
246                 /* wait for parent to get out of critical zone */
247                 lock_get(process_lock);
248                         close(sockfd[0]);
249                         unix_tcp_sock=sockfd[1];
250                         if (reader_fd_1) *reader_fd_1=reader_fd[1];
251                 lock_release(process_lock);
252                 if (init_child(child_id) < 0) {
253                         LOG(L_ERR, "ERROR: fork_tcp_process(): init_child failed for "
254                                         "%s\n", pt[process_no].desc);
255                         return -1;
256                 }
257                 return pid;
258         } else {
259                 /* parent */            
260                 process_no = old_process_no;
261                 /* add the process to the list in shm */
262                 pt[*process_count].pid=pid;
263                 pt[*process_count].unix_sock=sockfd[0];
264                 pt[*process_count].idx=r;       
265                 if (desc){
266                         snprintf(pt[*process_count].desc, MAX_PT_DESC, "%s child=%d", 
267                                                 desc, r);
268                 }
269                 
270                 close(sockfd[1]);
271                 close(reader_fd[1]);
272                 
273                 tcp_children[r].pid=pid;
274                 tcp_children[r].proc_no=process_no;
275                 tcp_children[r].busy=0;
276                 tcp_children[r].n_reqs=0;
277                 tcp_children[r].unix_sock=reader_fd[0];
278                 
279                 *process_count = (*process_count) +1;
280                 lock_release(process_lock);
281                 return pid;
282         }
283 }
284 #endif