Coverage Report

Created: 2025-07-11 06:28

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