Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2007 Voice Sistem SRL |
3 | | * Copyright (C) 2008-2019 OpenSIPS Project |
4 | | * |
5 | | * This file is part of opensips, a free SIP server. |
6 | | * |
7 | | * opensips is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version |
11 | | * |
12 | | * opensips is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | #include <sys/types.h> |
23 | | #include <sys/wait.h> |
24 | | #include <unistd.h> |
25 | | #include <sched.h> |
26 | | #include <stdio.h> |
27 | | |
28 | | #include "lib/dbg/profiling.h" |
29 | | #include "mem/shm_mem.h" |
30 | | #include "net/net_tcp.h" |
31 | | #include "net/net_udp.h" |
32 | | #include "db/db_insertq.h" |
33 | | #include "sr_module.h" |
34 | | #include "dprint.h" |
35 | | #include "pt.h" |
36 | | #include "bin_interface.h" |
37 | | #include "core_stats.h" |
38 | | |
39 | | |
40 | | /* array with children pids, 0= main proc, |
41 | | * alloc'ed in shared mem if possible */ |
42 | | struct process_table *pt = NULL; |
43 | | |
44 | | /* The maximum number of processes that will ever exist in OpenSIPS. This is |
45 | | * actually the size of the process table |
46 | | * This is READONLY!! */ |
47 | | unsigned int counted_max_processes = 0; |
48 | | |
49 | | /* flag per process to control the termination stages */ |
50 | | int _termination_in_progress = 0; |
51 | | |
52 | | static int internal_fork_child_setup(const struct internal_fork_params *); |
53 | | |
54 | | static struct internal_fork_handler default_fh = { |
55 | | .desc = "internal_fork_child_setup()", |
56 | | .post_fork.in_child = internal_fork_child_setup, |
57 | | }; |
58 | | |
59 | | static struct internal_fork_handler *_fork_handlers = &default_fh; |
60 | | |
61 | | /* Register handlers to be invoked after internal_fork() |
62 | | * to do various per-subsystem setup / cleanup tasks. |
63 | | * Takes a reference to a "stable" structure (i.e. static or |
64 | | * malloc'ed) which has to be alive until the last internal_fork() |
65 | | * is called. */ |
66 | | void register_fork_handler(struct internal_fork_handler *h) |
67 | 0 | { |
68 | 0 | struct internal_fork_handler *hp; |
69 | |
|
70 | 0 | if (is_main == 0) { |
71 | 0 | LM_BUG("buggy call from non-main process!!!\n"); |
72 | 0 | abort(); |
73 | 0 | } |
74 | 0 | if (h->_next != NULL) { |
75 | 0 | LM_BUG("buggy call h->_next != NULL!!!\n"); |
76 | 0 | abort(); |
77 | 0 | } |
78 | | |
79 | 0 | for (hp = _fork_handlers; hp->_next != NULL; hp = hp->_next) |
80 | 0 | continue; |
81 | 0 | hp->_next = h; |
82 | 0 | }; |
83 | | |
84 | | static unsigned long count_running_processes(void *x) |
85 | 0 | { |
86 | 0 | int i,cnt=0; |
87 | |
|
88 | 0 | if (pt) |
89 | 0 | for ( i=0 ; i<counted_max_processes ; i++ ) |
90 | 0 | if (is_process_running(i)) |
91 | 0 | cnt++; |
92 | |
|
93 | 0 | return cnt; |
94 | 0 | } |
95 | | |
96 | | |
97 | | int init_multi_proc_support(void) |
98 | 0 | { |
99 | 0 | int i; |
100 | | /* at this point we know exactly the possible number of processes, since |
101 | | * all the other modules already adjusted their extra numbers */ |
102 | 0 | counted_max_processes = count_child_processes(); |
103 | |
|
104 | | #ifdef UNIT_TESTS |
105 | | #include "mem/test/test_malloc.h" |
106 | | counted_max_processes += TEST_MALLOC_PROCS - 1; |
107 | | #endif |
108 | | |
109 | | /* allocate the PID table to accomodate the maximum possible number of |
110 | | * process we may have during runtime (covering extra procs created |
111 | | * due auto-scaling) */ |
112 | 0 | pt = shm_malloc(sizeof(struct process_table)*counted_max_processes); |
113 | 0 | if (pt==0){ |
114 | 0 | LM_ERR("out of memory\n"); |
115 | 0 | return -1; |
116 | 0 | } |
117 | 0 | memset(pt, 0, sizeof(struct process_table)*counted_max_processes); |
118 | |
|
119 | 0 | for( i=0 ; i<counted_max_processes ; i++ ) { |
120 | | /* reset fds to prevent bogus ops */ |
121 | 0 | pt[i].pid = -1; |
122 | 0 | pt[i].ipc_pipe[0] = pt[i].ipc_pipe[1] = -1; |
123 | 0 | pt[i].ipc_sync_pipe[0] = pt[i].ipc_sync_pipe[1] = -1; |
124 | 0 | } |
125 | | |
126 | | /* create the load-related stats (initially marked as hidden */ |
127 | | /* until the proc starts) */ |
128 | 0 | if (register_processes_load_stats( counted_max_processes ) != 0) { |
129 | 0 | LM_ERR("failed to create load stats\n"); |
130 | 0 | return -1; |
131 | 0 | } |
132 | | |
133 | | /* create the IPC pipes for all possible procs */ |
134 | 0 | if (create_ipc_pipes( counted_max_processes )<0) { |
135 | 0 | LM_ERR("failed to create IPC pipes, aborting\n"); |
136 | 0 | return -1; |
137 | 0 | } |
138 | | |
139 | | /* create the pkg_mem stats */ |
140 | | #ifdef PKG_MALLOC |
141 | | if (init_pkg_stats(counted_max_processes)!=0) { |
142 | | LM_ERR("failed to init stats for pkg\n"); |
143 | | return -1; |
144 | | } |
145 | | #endif |
146 | | |
147 | | /* set the pid for the starter process */ |
148 | 0 | set_proc_attrs("starter"); |
149 | | |
150 | | /* register the stats for the global load */ |
151 | 0 | if ( register_stat2( "load", "load", (stat_var**)pt_get_rt_load, |
152 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
153 | 0 | LM_ERR("failed to add RT global load stat\n"); |
154 | 0 | return -1; |
155 | 0 | } |
156 | | |
157 | 0 | if ( register_stat2( "load", "load1m", (stat_var**)pt_get_1m_load, |
158 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
159 | 0 | LM_ERR("failed to add RT global load stat\n"); |
160 | 0 | return -1; |
161 | 0 | } |
162 | | |
163 | 0 | if ( register_stat2( "load", "load10m", (stat_var**)pt_get_10m_load, |
164 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
165 | 0 | LM_ERR("failed to add RT global load stat\n"); |
166 | 0 | return -1; |
167 | 0 | } |
168 | | |
169 | | /* register the stats for the extended global load */ |
170 | 0 | if ( register_stat2( "load", "load-all", (stat_var**)pt_get_rt_loadall, |
171 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
172 | 0 | LM_ERR("failed to add RT global load stat\n"); |
173 | 0 | return -1; |
174 | 0 | } |
175 | | |
176 | 0 | if ( register_stat2( "load", "load1m-all", (stat_var**)pt_get_1m_loadall, |
177 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
178 | 0 | LM_ERR("failed to add RT global load stat\n"); |
179 | 0 | return -1; |
180 | 0 | } |
181 | | |
182 | 0 | if ( register_stat2( "load", "load10m-all", (stat_var**)pt_get_10m_loadall, |
183 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
184 | 0 | LM_ERR("failed to add RT global load stat\n"); |
185 | 0 | return -1; |
186 | 0 | } |
187 | | |
188 | 0 | if ( register_stat2( "load", "processes_number", |
189 | 0 | (stat_var**)count_running_processes, |
190 | 0 | STAT_IS_FUNC, NULL, 0) != 0) { |
191 | 0 | LM_ERR("failed to add processes_number stat\n"); |
192 | 0 | return -1; |
193 | 0 | } |
194 | | |
195 | 0 | return 0; |
196 | 0 | } |
197 | | |
198 | | |
199 | | void set_proc_attrs(const char *fmt, ...) |
200 | 0 | { |
201 | 0 | va_list ap; |
202 | | |
203 | | /* description */ |
204 | 0 | va_start(ap, fmt); |
205 | 0 | vsnprintf( pt[process_no].desc, MAX_PT_DESC, fmt, ap); |
206 | 0 | va_end(ap); |
207 | | |
208 | | /* pid */ |
209 | 0 | pt[process_no].pid=getpid(); |
210 | 0 | } |
211 | | |
212 | | |
213 | | /* Resets all the values in the process table for a given id (a slot) so that |
214 | | * it can be reused later |
215 | | * WARNING: this should be called only by main process and when it is 100% |
216 | | * that the process mapped on this slot is not running anymore */ |
217 | | void reset_process_slot( int p_id ) |
218 | 0 | { |
219 | 0 | if (is_main==0) { |
220 | 0 | LM_BUG("buggy call from non-main process!!!"); |
221 | 0 | return; |
222 | 0 | } |
223 | | |
224 | | /* we cannot simply do a memset here, as we need to preserve the holders |
225 | | * with the inter-process communication fds */ |
226 | 0 | pt[p_id].pid = -1; |
227 | 0 | pt[p_id].type = TYPE_NONE; |
228 | 0 | pt[p_id].pg_filter = NULL; |
229 | 0 | pt[p_id].desc[0] = 0; |
230 | 0 | pt[p_id].flags = 0; |
231 | |
|
232 | 0 | pt[p_id].ipc_pipe[0] = pt[p_id].ipc_pipe[1] = -1; |
233 | 0 | pt[p_id].ipc_sync_pipe[0] = pt[p_id].ipc_sync_pipe[1] = -1; |
234 | |
|
235 | 0 | pt[p_id].log_level = pt[p_id].default_log_level = 0; /*not really needed*/ |
236 | 0 | pt[p_id].profiling_proc_level = LEVEL_OFF; |
237 | | |
238 | | /* purge all load-related data */ |
239 | 0 | memset( &pt[p_id].load, 0, sizeof(struct proc_load_info)); |
240 | | /* hide the load stats */ |
241 | 0 | pt[p_id].load_rt->flags |= STAT_HIDDEN; |
242 | 0 | pt[p_id].load_1m->flags |= STAT_HIDDEN; |
243 | 0 | pt[p_id].load_10m->flags |= STAT_HIDDEN; |
244 | | #ifdef PKG_MALLOC |
245 | | pt[p_id].pkg_total->flags |= STAT_HIDDEN; |
246 | | pt[p_id].pkg_used->flags |= STAT_HIDDEN; |
247 | | pt[p_id].pkg_rused->flags |= STAT_HIDDEN; |
248 | | pt[p_id].pkg_mused->flags |= STAT_HIDDEN; |
249 | | pt[p_id].pkg_free->flags |= STAT_HIDDEN; |
250 | | pt[p_id].pkg_frags->flags |= STAT_HIDDEN; |
251 | | #endif |
252 | 0 | } |
253 | | |
254 | | |
255 | | enum {CHLD_STARTING, CHLD_OK, CHLD_FAILED}; |
256 | | |
257 | | static __attribute__((__noreturn__)) void child_startup_failed(void) |
258 | 0 | { |
259 | 0 | atomic_store(&pt[process_no].startup_result, CHLD_FAILED); |
260 | 0 | exit(1); |
261 | 0 | } |
262 | | |
263 | | static int internal_fork_child_setup(const struct internal_fork_params *ifpp) |
264 | 0 | { |
265 | 0 | init_log_level(); |
266 | | |
267 | | /* free the script if not needed */ |
268 | 0 | if (!(ifpp->flags & OSS_PROC_NEEDS_SCRIPT) && sroutes) { |
269 | 0 | free_route_lists(sroutes); |
270 | 0 | sroutes = NULL; |
271 | 0 | } |
272 | 0 | return 0; |
273 | 0 | } |
274 | | |
275 | | /* This function is to be called only by the main process! |
276 | | * Returns, on success, the ID (non zero) in the process table of the |
277 | | * newly forked procees. |
278 | | * */ |
279 | | int internal_fork(const struct internal_fork_params *ifpp) |
280 | 0 | { |
281 | 0 | int new_idx; |
282 | 0 | pid_t pid; |
283 | 0 | unsigned int seed; |
284 | |
|
285 | 0 | if (is_main==0) { |
286 | 0 | LM_BUG("buggy call from non-main process!!!"); |
287 | 0 | return -1; |
288 | 0 | } |
289 | | |
290 | 0 | new_idx = 1; /* start from 1 as 0 (attendent) is always running */ |
291 | 0 | for( ; new_idx<counted_max_processes ; new_idx++) |
292 | 0 | if ( (pt[new_idx].flags&OSS_PROC_IS_RUNNING)==0 ) break; |
293 | 0 | if (new_idx==counted_max_processes) { |
294 | 0 | LM_BUG("no free process slot found while trying to fork again\n"); |
295 | 0 | return -1; |
296 | 0 | } |
297 | | |
298 | 0 | seed = rand(); |
299 | |
|
300 | 0 | LM_DBG("forking new process \"%s\" on slot %d\n", ifpp->proc_desc, new_idx); |
301 | | |
302 | | /* set the IPC pipes */ |
303 | 0 | if ( (ifpp->flags & OSS_PROC_NO_IPC) ) { |
304 | | /* advertise no IPC to the rest of the procs */ |
305 | 0 | pt[new_idx].ipc_pipe[0] = -1; |
306 | 0 | pt[new_idx].ipc_pipe[1] = -1; |
307 | 0 | pt[new_idx].ipc_sync_pipe[0] = -1; |
308 | 0 | pt[new_idx].ipc_sync_pipe[1] = -1; |
309 | | /* NOTE: the IPC fds will remain open in the other processes, |
310 | | * but they will not be known */ |
311 | 0 | } else { |
312 | | /* activate the IPC pipes */ |
313 | 0 | pt[new_idx].ipc_pipe[0]=pt[new_idx].ipc_pipe_holder[0]; |
314 | 0 | pt[new_idx].ipc_pipe[1]=pt[new_idx].ipc_pipe_holder[1]; |
315 | 0 | pt[new_idx].ipc_sync_pipe[0]=pt[new_idx].ipc_sync_pipe_holder[0]; |
316 | 0 | pt[new_idx].ipc_sync_pipe[1]=pt[new_idx].ipc_sync_pipe_holder[1]; |
317 | 0 | } |
318 | |
|
319 | 0 | pt[new_idx].pid = 0; |
320 | |
|
321 | 0 | atomic_init(&pt[new_idx].startup_result, CHLD_STARTING); |
322 | |
|
323 | 0 | if ( (pid=fork())<0 ){ |
324 | 0 | LM_CRIT("cannot fork \"%s\" process (%d: %s)\n",ifpp->proc_desc, |
325 | 0 | errno, strerror(errno)); |
326 | 0 | reset_process_slot( new_idx ); |
327 | 0 | return -1; |
328 | 0 | } |
329 | | |
330 | 0 | if (pid==0){ |
331 | 0 | const struct internal_fork_handler *cfhp; |
332 | | /* child process */ |
333 | 0 | is_main = 0; /* a child is not main process */ |
334 | | /* set uid */ |
335 | 0 | process_no = new_idx; |
336 | | /* set attributes, pid etc */ |
337 | 0 | set_proc_attrs(ifpp->proc_desc); |
338 | |
|
339 | 0 | pt[process_no].flags |= ifpp->flags; |
340 | 0 | pt[process_no].type = ifpp->type; |
341 | | /* activate its load & pkg statistics, but only if IPC present */ |
342 | 0 | if ( (ifpp->flags & OSS_PROC_NO_IPC)==0 ) { |
343 | 0 | pt[process_no].load_rt->flags &= (~STAT_HIDDEN); |
344 | 0 | pt[process_no].load_1m->flags &= (~STAT_HIDDEN); |
345 | 0 | pt[process_no].load_10m->flags &= (~STAT_HIDDEN); |
346 | | #ifdef PKG_MALLOC |
347 | | pt[process_no].pkg_total->flags &= (~STAT_HIDDEN); |
348 | | pt[process_no].pkg_used->flags &= (~STAT_HIDDEN); |
349 | | pt[process_no].pkg_rused->flags &= (~STAT_HIDDEN); |
350 | | pt[process_no].pkg_mused->flags &= (~STAT_HIDDEN); |
351 | | pt[process_no].pkg_free->flags &= (~STAT_HIDDEN); |
352 | | pt[process_no].pkg_frags->flags &= (~STAT_HIDDEN); |
353 | | #endif |
354 | 0 | } |
355 | | /* each children need a unique seed */ |
356 | 0 | seed_child(seed); |
357 | |
|
358 | 0 | for (cfhp = _fork_handlers; cfhp != NULL; cfhp = cfhp->_next) { |
359 | 0 | if (cfhp->post_fork.in_child == NULL) |
360 | 0 | continue; |
361 | 0 | if (cfhp->post_fork.in_child(ifpp) != 0) { |
362 | 0 | LM_CRIT("failed to run %s for process %d\n", cfhp->desc, |
363 | 0 | process_no); |
364 | 0 | child_startup_failed(); |
365 | 0 | } |
366 | 0 | } |
367 | 0 | atomic_store(&pt[process_no].startup_result, CHLD_OK); |
368 | 0 | return 0; |
369 | 0 | }else{ |
370 | | /* parent process */ |
371 | | /* wait for the child to complete the critical sectoin of the |
372 | | * start-up */ |
373 | 0 | while (atomic_load(&pt[new_idx].startup_result) == CHLD_STARTING) { |
374 | 0 | int status; |
375 | 0 | sched_yield(); |
376 | 0 | pid_t result = waitpid(pid, &status, WNOHANG); |
377 | 0 | if (result < 0) { |
378 | 0 | if (errno == EINTR) |
379 | 0 | continue; |
380 | 0 | goto child_is_down; |
381 | 0 | } |
382 | 0 | if (result == 0) { |
383 | | // Child has not exited yet |
384 | 0 | continue; |
385 | 0 | } |
386 | | // Child has exited, oops |
387 | 0 | goto child_is_down; |
388 | 0 | } |
389 | 0 | if (atomic_load(&pt[new_idx].startup_result) != CHLD_OK) { |
390 | 0 | goto child_is_down; |
391 | 0 | } |
392 | 0 | pt[new_idx].flags |= OSS_PROC_IS_RUNNING; |
393 | 0 | return new_idx; |
394 | 0 | child_is_down: |
395 | 0 | LM_CRIT("failed to initialize child process %d\n", new_idx); |
396 | 0 | reset_process_slot( new_idx ); |
397 | 0 | return -1; |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | |
402 | | /* counts the number of processes created by OpenSIPS at startup. processes |
403 | | * that also do child_init() (the per-process module init) |
404 | | * |
405 | | * used for proper status return code |
406 | | */ |
407 | | int count_init_child_processes(void) |
408 | 0 | { |
409 | 0 | int ret=0; |
410 | | |
411 | | /* listening children to be create at startup */ |
412 | 0 | ret += udp_count_processes(NULL); |
413 | 0 | ret += tcp_count_processes(NULL); |
414 | 0 | ret += timer_count_processes(NULL) - 2/*for keeper & trigger*/; |
415 | | |
416 | | /* attendent */ |
417 | 0 | ret++; |
418 | | |
419 | | /* count number of module procs going to be initialised */ |
420 | 0 | ret += count_module_procs(PROC_FLAG_INITCHILD); |
421 | |
|
422 | 0 | LM_DBG("%d children are going to be inited\n",ret); |
423 | 0 | return ret; |
424 | 0 | } |
425 | | |
426 | | /* counts the number of processes known by OpenSIPS at startup. |
427 | | * Note that the number of processes might change during init, if one of the |
428 | | * module decides that it will no longer use a process (ex; rtpproxy timeout |
429 | | * process) |
430 | | */ |
431 | | int count_child_processes(void) |
432 | 0 | { |
433 | 0 | unsigned int proc_no; |
434 | 0 | unsigned int proc_extra_no; |
435 | 0 | unsigned int extra; |
436 | |
|
437 | 0 | proc_no = 0; |
438 | 0 | proc_extra_no = 0; |
439 | | |
440 | | /* UDP based listeners */ |
441 | 0 | proc_no += udp_count_processes( &extra ); |
442 | 0 | proc_extra_no += extra; |
443 | | |
444 | | /* TCP based listeners */ |
445 | 0 | proc_no += tcp_count_processes( &extra ); |
446 | 0 | proc_extra_no += extra; |
447 | | |
448 | | /* Timer related processes */ |
449 | 0 | proc_no += timer_count_processes( &extra ); |
450 | 0 | proc_extra_no += extra; |
451 | | |
452 | | /* attendent */ |
453 | 0 | proc_no++; |
454 | | |
455 | | /* count the processes requested by modules */ |
456 | 0 | proc_no += count_module_procs(0); |
457 | |
|
458 | 0 | return proc_no + proc_extra_no; |
459 | 0 | } |
460 | | |
461 | | |
462 | | void dynamic_process_final_exit(void) |
463 | 0 | { |
464 | | /* prevent any more IPC */ |
465 | 0 | pt[process_no].ipc_pipe[0] = -1; |
466 | 0 | pt[process_no].ipc_pipe[1] = -1; |
467 | 0 | pt[process_no].ipc_sync_pipe[0] = -1; |
468 | 0 | pt[process_no].ipc_sync_pipe[1] = -1; |
469 | | |
470 | | /* clear the per-process connection from the DB queues */ |
471 | 0 | ql_force_process_disconnect(process_no); |
472 | | |
473 | | /* if a TCP proc by chance, reset the tcp-related data */ |
474 | 0 | tcp_reset_worker_slot(); |
475 | |
|
476 | 0 | pt_become_idle(); |
477 | | |
478 | | /* mark myself as DYNAMIC (just in case) to have an err-less termination */ |
479 | 0 | pt[process_no].flags |= OSS_PROC_SELFEXIT; |
480 | 0 | LM_INFO("doing self termination\n"); |
481 | | |
482 | | /* the process slot in the proc table will be purge on SIGCHLD by main */ |
483 | 0 | exit(0); |
484 | 0 | } |
485 | | |
486 | | int run_post_fork_handlers(void) |
487 | 0 | { |
488 | 0 | const struct internal_fork_handler *cfhp; |
489 | |
|
490 | 0 | for (cfhp = _fork_handlers; cfhp != NULL; cfhp = cfhp->_next) { |
491 | 0 | if (cfhp->post_fork.in_parent == NULL) |
492 | 0 | continue; |
493 | 0 | if (cfhp->post_fork.in_parent() != 0) { |
494 | 0 | LM_CRIT("failed to run %s for process %d\n", cfhp->desc, |
495 | 0 | process_no); |
496 | 0 | return (-1); |
497 | 0 | } |
498 | 0 | } |
499 | 0 | return (0); |
500 | 0 | } |