Coverage Report

Created: 2026-08-13 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/pt.c
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
}