Coverage Report

Created: 2026-08-13 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/shutdown.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 * Copyright (C) 2005-2006 Voice Sistem S.R.L
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
23
#include <sys/types.h>
24
#include <sys/wait.h>
25
#include <stdlib.h>
26
#include <signal.h>
27
#include <unistd.h>
28
29
#include "lib/dbg/profiling.h"
30
#include "config.h"
31
#include "dprint.h"
32
#include "daemonize.h"
33
#include "globals.h"
34
#include "db/pi_framework.h"
35
#include "pt.h"
36
#include "route.h"
37
#include "script_cb.h"
38
#include "blacklists.h"
39
#include "status_report.h"
40
#include "mem/shm_mem.h"
41
#include "db/db_insertq.h"
42
#include "net/net_udp.h"
43
#include "net/net_tcp.h"
44
#include "shutdown.h"
45
46
/**
47
 * Clean up on exit. This should be called before exiting.
48
 * \param show_status set to one to display the mem status
49
 */
50
void cleanup(int show_status)
51
0
{
52
0
  LM_INFO("cleanup\n");
53
54
  /*clean-up*/
55
56
#ifdef DBG_MALLOC
57
  if (shm_memlog_size && mem_dbg_lock)
58
    shm_dbg_unlock();
59
#endif
60
61
0
  handle_ql_shutdown();
62
0
  pi_framework_destroy_db();
63
0
  destroy_modules();
64
0
  udp_destroy();
65
0
  tcp_destroy();
66
0
  destroy_timer();
67
0
  if (shm_memlog_size)
68
0
    shm_mem_disable_dbg();
69
0
  destroy_stats_collector();
70
0
  destroy_script_cb();
71
0
  pv_free_extra_list();
72
0
  tr_free_extra_list();
73
0
  destroy_argv_list();
74
0
  destroy_black_lists();
75
0
  free_route_lists(sroutes); // this is just for testing purposes
76
#ifdef PKG_MALLOC
77
  if (show_status){
78
    LM_GEN1(memdump, "Memory status (pkg):\n");
79
    pkg_status();
80
  }
81
#endif
82
0
  cleanup_log_level();
83
84
0
  if (pt && (0
85
0
#if defined F_MALLOC || defined Q_MALLOC
86
0
    || mem_lock
87
0
#endif
88
#ifdef HP_MALLOC
89
    || mem_locks
90
#endif
91
0
    ))
92
0
    shm_free(pt);
93
0
  pt=0;
94
0
  if (show_status){
95
0
      LM_GEN1(memdump, "Memory status (shm):\n");
96
0
      shm_status();
97
0
  }
98
99
0
  cleanup_log_cons_shm_table();
100
101
  /* zero all shmem alloc vars that we still use */
102
0
  shm_mem_destroy();
103
0
  if (pid_file) unlink(pid_file);
104
0
  if (pgid_file) unlink(pgid_file);
105
0
}
106
107
108
/**
109
 * Send a signal to all child processes
110
 * \param signum signal for killing the children
111
 */
112
void kill_all_children(int signum)
113
0
{
114
0
  int r;
115
116
0
  if (!pt)
117
0
    return;
118
119
0
  for (r = 1; r < counted_max_processes; r++) {
120
0
    if (pt[r].pid == -1 || (pt[r].flags & OSS_PROC_DOING_DUMP))
121
0
      continue;
122
123
    /* as the PIDs are filled in by child processes, a 0 PID means
124
     * an un-initalized procees; killing an uninitialized proc is
125
     * very dangerous, so better wait for it to finish its init
126
     * sequence by blocking until the pid is populated */
127
0
    while (pt[r].pid == 0)
128
0
      usleep(1000);
129
130
0
    kill(pt[r].pid, signum);
131
0
  }
132
0
}
133
134
135
/**
136
 * SIGALRM "timeout" handler during the attendant's final cleanup,
137
 * try to leave a core for future diagnostics.
138
 */
139
static void sig_alarm_abort(int signo)
140
0
{
141
  /* LOG is not signal safe, but who cares, we are abort-ing anyway :-) */
142
0
  LM_CRIT("BUG - shutdown timeout triggered, dying...\n");
143
0
  abort();
144
0
}
145
146
147
/* RPC function send by main process to all worker processes supporting
148
 * IPC in order to force a gracefull termination
149
 */
150
static void rpc_process_terminate(int sender_id, void *code)
151
0
{
152
  #ifdef PKG_MALLOC
153
  LM_GEN1(memdump, "Memory status (pkg):\n");
154
  pkg_status();
155
  #endif
156
157
  /* simply terminate the process */
158
0
  LM_DBG("Process %d exiting with code %d...\n",
159
0
    process_no, (int)(long)code);
160
161
0
  _ProfilerStop();
162
0
  exit( (int)(long)code );
163
0
}
164
165
166
/* Implements full shutdown sequence (terminate processes and cleanup)
167
 * To be called ONLY from MAIN process, not from workers !!!
168
 */
169
void shutdown_opensips( int status )
170
0
{
171
0
  pid_t  proc;
172
0
  int i, n, p;
173
0
  int chld_status;
174
175
0
  sr_set_core_status_terminating();
176
177
0
  distroy_log_event_cons();
178
179
  /* terminate all processes */
180
181
  /* first we try to terminate the processes via the IPC channel */
182
0
  for( i=1,n=0 ; i<counted_max_processes; i++) {
183
    /* Depending on the processes status, its PID may be:
184
     *   -1 - process not forked yet
185
     *    0 - process forked but not fully configured by core
186
     *   >0 - process fully running
187
     */
188
0
    if (pt[i].pid!=-1) {
189
      /* use IPC (if avaiable) for a graceful termination */
190
0
      if ( IPC_FD_WRITE(i)>0 ) {
191
0
        LM_DBG("Asking process %d [%s] to terminate\n", i, pt[i].desc);
192
0
        if (ipc_send_rpc( i, rpc_process_terminate, (void*)0)<0) {
193
0
          LM_ERR("failed to trigger RPC termination for "
194
0
            "process %d\n", i );
195
0
        }
196
0
      } else {
197
0
        while (pt[i].pid==0) usleep(1000);
198
0
        kill(pt[i].pid, SIGTERM);
199
0
      }
200
0
      n++;
201
0
    }
202
0
  }
203
204
  /* now wait for the processes to finish */
205
0
  i = GRACEFUL_SHUTDOWN_TIMEOUT * 100;
206
0
  while( i && n ) {
207
0
    proc = waitpid( -1, &chld_status, WNOHANG);
208
0
    if (proc<=0) {
209
      /* no process exited so far, do a small sleep before retry */
210
0
      usleep(10000);
211
0
      i--;
212
0
    } else {
213
0
      if ( (p=get_process_ID_by_PID(proc)) == -1 ) {
214
0
        LM_DBG("unknown child process %d ended. Ignoring\n",proc);
215
0
      } else {
216
0
        LM_INFO("process %d(%d) [%s] terminated, "
217
0
          "still waiting for %d more\n", p, proc, pt[p].desc, n-1);
218
        /* mark the child process as terminated / not running */
219
0
        pt[p].pid = -1;
220
0
        status |= chld_status;
221
0
        n--;
222
0
      }
223
0
    }
224
0
  }
225
226
0
  if (i==0 && n!=0) {
227
0
    LM_DBG("force termination for all processes\n");
228
0
    kill_all_children(SIGKILL);
229
0
  }
230
231
0
  _ProfilerStop();
232
233
  /* Only one process is running now. Clean up and return overall status */
234
235
  /* hack: force-unlock the shared memory lock(s) in case
236
     some process crashed and let it locked; this will
237
     allow an almost gracious shutdown */
238
0
  shm_force_unlock();
239
240
0
  signal(SIGALRM, sig_alarm_abort);
241
0
  alarm(SHUTDOWN_TIMEOUT - i / 100);
242
0
  cleanup(1);
243
0
  alarm(0);
244
0
  signal(SIGALRM, SIG_IGN);
245
246
0
  stderr_dprint_tmp("Thank you for running " NAME "\n");
247
0
  exit( status );
248
0
}