Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/net/net_tcp_proc.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2013-2014 OpenSIPS Solutions
3
 * Copyright (C) 2001-2003 FhG Fokus
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
 * History:
22
 * --------
23
 * 2015-02-05 imported from former tpc_read.c (bogdan)
24
 */
25
26
27
#include <stdint.h>
28
#include <sys/socket.h>
29
#include "../pt_load.h"
30
#include "../ipc.h"
31
#include "../timer.h"
32
#include "../reactor.h"
33
#include "../async.h"
34
#include "../cfg_reload.h"
35
#include "../mem/shm_mem.h"
36
#include "../receive.h"
37
#include "../profiling.h"
38
39
#include "tcp_conn.h"
40
#include "net_tcp.h"
41
#include "trans.h"
42
#include "net_tcp_dbg.h"
43
44
/*!< the FD currently used by the process to communicate with TCP MAIN*/
45
static int _my_fd_to_tcp_main = -1;
46
47
static int tcpmain_sock=-1;
48
49
/* wrapper around internal tcpconn_release() - to be called by functions which
50
 * used tcp_conn_get(), in order to release the connection;
51
 * It does the unref and pushes back (if needed) some update to TCP main;
52
 * right now, it used only from the xxx_send() functions
53
 */
54
void tcp_conn_release(struct tcp_connection* c, int pending_data)
55
0
{
56
0
  if (c->state==S_CONN_BAD) {
57
    /* do more or less nothing, let the TCP READER owning the conn
58
     * to trash it based on the S_CONN_BAD marker */
59
0
    c->lifetime=0;
60
0
    c->timeout=0;
61
    /* but be sure we unref the conn */
62
0
    tcpconn_put(c);
63
0
    return;
64
0
  }
65
0
  if (!pending_data && c->async && c->async->pending &&
66
0
      ((c->state == S_CONN_OK &&
67
0
        (c->main_flags & F_TCP_MAIN_REMOVED_WRITE)) ||
68
0
       c->fd == -1))
69
0
    pending_data = 1;
70
0
  if (pending_data) {
71
0
    if (tcp_async_write_job(c) < 0) {
72
0
      LM_ERR("failed to queue direct TCP write job for conn %p / %u\n",
73
0
        c, c->id);
74
0
      tcpconn_put(c);
75
0
    }
76
0
    return;
77
0
  }
78
0
  tcpconn_put(c);
79
0
}
80
81
82
struct tcp_ipc_payload {
83
  struct receive_info rcv;
84
  struct tcp_connection *conn;
85
  int msg_len;
86
  int data_len;
87
  char msg_buf[0];
88
};
89
90
91
/*! \brief
92
 *  handle io routine, based on the fd_map type
93
 * (it will be called from reactor_main_loop )
94
 * params:  fm  - pointer to a fd hash entry
95
 *          idx - index in the fd_array (or -1 if not known)
96
 * return: -1 on error, or when we are not interested any more on reads
97
 *            from this fd (e.g.: we are closing it )
98
 *          0 on EAGAIN or when by some other way it is known that no more
99
 *            io events are queued on the fd (the receive buffer is empty).
100
 *            Usefull to detect when there are no more io events queued for
101
 *            sigio_rt, epoll_et, kqueue.
102
 *         >0 on successful read from the fd (when there might be more io
103
 *            queued -- the receive buffer might still be non-empty)
104
 */
105
inline static int handle_io(struct fd_map* fm, int idx,int event_type)
106
0
{
107
0
  int ret=0;
108
0
  int n=0;
109
110
0
  (void)idx;
111
0
  pt_become_active();
112
113
0
  pre_run_handle_script_reload(fm->app_flags);
114
115
0
  profiling_proc_start( LEVEL_SIP, 1);
116
117
0
  switch(fm->type){
118
0
    case F_TIMER_JOB:
119
0
      profiling_proc_enter( LEVEL_FULL, "timer_job", 1 );
120
0
      handle_timer_job();
121
0
      profiling_proc_exit( LEVEL_FULL, "timer_job", n);
122
0
      break;
123
0
    case F_SCRIPT_ASYNC:
124
0
      profiling_proc_enter( LEVEL_SIP, "async_script", 0 );
125
0
      n = async_script_resume_f( fm->fd, fm->data,
126
0
        (event_type==IO_WATCH_TIMEOUT)?1:0 );
127
0
      profiling_proc_exit( LEVEL_SIP, "async_script", n);
128
0
      break;
129
0
    case F_FD_ASYNC:
130
0
      profiling_proc_enter( LEVEL_SIP, "async_fd", 0 );
131
0
      n = async_fd_resume( fm->fd, fm->data);
132
0
      profiling_proc_exit( LEVEL_SIP, "async_fd", n);
133
0
      break;
134
0
    case F_LAUNCH_ASYNC:
135
0
      profiling_proc_enter( LEVEL_SIP, "async_launch", 0 );
136
0
      n = async_launch_resume( fm->fd, fm->data);
137
0
      profiling_proc_exit( LEVEL_SIP, "async_launch", n);
138
0
      break;
139
0
    case F_IPC:
140
0
      profiling_proc_enter( LEVEL_SIP, "ipc_job", 1 );
141
0
      ipc_handle_job(fm->fd);
142
0
      profiling_proc_exit( LEVEL_SIP, "ipc_job", n );
143
0
      break;
144
0
    case F_TCPMAIN:
145
0
    {
146
0
      struct tcp_ipc_payload *payload;
147
0
      uintptr_t payload_ptr;
148
0
again_payload:
149
0
      ret = n = recv(fm->fd, &payload_ptr, sizeof(payload_ptr),
150
0
        MSG_DONTWAIT);
151
0
      if (n < 0) {
152
0
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
153
0
          ret = 0;
154
0
          break;
155
0
        } else if (errno == EINTR) {
156
0
          goto again_payload;
157
0
        }
158
0
        LM_CRIT("read from tcp main dispatch socket failed: %s\n",
159
0
          strerror(errno));
160
0
        goto error;
161
0
      }
162
0
      if (n == 0) {
163
0
        LM_WARN("EOF received from tcp main dispatch socket\n");
164
0
        goto error;
165
0
      }
166
0
      if (n != (int)sizeof(payload_ptr)) {
167
0
        LM_CRIT("short read on tcp main dispatch socket: %d bytes\n", n);
168
0
        goto error;
169
0
      }
170
171
0
      payload = (struct tcp_ipc_payload *)(uintptr_t)payload_ptr;
172
0
      if (!payload) {
173
0
        LM_BUG("null payload pointer from tcp main\n");
174
0
        break;
175
0
      }
176
177
0
      profiling_proc_enter( LEVEL_SIP,
178
0
        ss_merge256( protos[payload->rcv.proto].name,
179
0
        " proto handling"), 0);
180
181
0
      bind_address = payload->rcv.bind_address;
182
0
      if (payload->rcv.proto > PROTO_NONE &&
183
0
          payload->rcv.proto < PROTO_LAST &&
184
0
          is_tcp_based_proto(payload->rcv.proto) &&
185
0
          protos[payload->rcv.proto].net.stream.handle) {
186
0
        if ( (n=protos[payload->rcv.proto].net.stream.handle(
187
0
            payload->msg_buf, payload->msg_len, &payload->rcv,
188
0
            payload->data_len ?
189
0
            payload->msg_buf + payload->msg_len + 1 : NULL,
190
0
            payload->data_len)) < 0)
191
0
          LM_ERR("TCP dispatch handler failed\n");
192
0
      } else if ( (n=receive_msg(payload->msg_buf, payload->msg_len,
193
0
          &payload->rcv, NULL, 0)) < 0) {
194
0
        LM_ERR("receive_msg() failed for dispatched TCP message\n");
195
0
      }
196
197
0
      profiling_proc_exit( LEVEL_SIP, "handling done", n);
198
199
0
      if (payload->conn)
200
0
        tcpconn_put(payload->conn);
201
0
      shm_free(payload);
202
0
      break;
203
0
    }
204
0
    case F_NONE:
205
0
      LM_CRIT("empty fd map %p: "
206
0
            "{%d, %d, %p}\n", fm,
207
0
            fm->fd, fm->type, fm->data);
208
0
      goto error;
209
0
    default:
210
0
      LM_CRIT("unknown fd type %d\n", fm->type);
211
0
      goto error;
212
0
  }
213
214
0
  if (_termination_in_progress==1) {
215
    /* check if anything is still left */
216
0
    if (reactor_is_empty()) {
217
0
      LM_WARN("reactor got empty while termination in progress\n");
218
0
      ipc_handle_all_pending_jobs(IPC_FD_READ_SELF);
219
0
      if (reactor_is_empty())
220
0
        dynamic_process_final_exit();
221
0
    }
222
0
  }
223
224
0
  profiling_proc_end( LEVEL_SIP, 0 );
225
226
0
  post_run_handle_script_reload();
227
228
0
  pt_become_idle();
229
0
  return ret;
230
0
error:
231
0
  profiling_proc_end( LEVEL_SIP, -1 );
232
0
  pt_become_idle();
233
0
  return -1;
234
0
}
235
236
237
int tcp_worker_proc_reactor_init( int unix_sock)
238
0
{
239
  /* init reactor for TCP worker */
240
0
  tcpmain_sock=unix_sock; /* init com. socket */
241
0
  if ( init_worker_reactor( "TCP_worker", RCT_PRIO_MAX)<0 ) {
242
0
    goto error;
243
0
  }
244
245
  /* start watching for the timer jobs */
246
0
  if (reactor_add_reader( timer_fd_out, F_TIMER_JOB, RCT_PRIO_TIMER,NULL)<0){
247
0
    LM_CRIT("failed to add timer pipe_out to reactor\n");
248
0
    goto error;
249
0
  }
250
251
  /* init: start watching for the IPC jobs */
252
0
  if (reactor_add_reader(IPC_FD_READ_SELF, F_IPC, RCT_PRIO_ASYNC, NULL)<0){
253
0
    LM_CRIT("failed to add IPC pipe to reactor\n");
254
0
    goto error;
255
0
  }
256
257
  /* init: start watching for IPC "dispatched" jobs */
258
0
  if (reactor_add_reader(IPC_FD_READ_SHARED, F_IPC, RCT_PRIO_ASYNC, NULL)<0){
259
0
    LM_CRIT("failed to add IPC shared pipe to reactor\n");
260
0
    return -1;
261
0
  }
262
263
  /* add the unix socket */
264
0
  if (reactor_add_reader( tcpmain_sock, F_TCPMAIN, RCT_PRIO_PROC, NULL)<0) {
265
0
    LM_CRIT("failed to add socket to the fd list\n");
266
0
    goto error;
267
0
  }
268
0
  _my_fd_to_tcp_main = tcpmain_sock;
269
270
0
  return 0;
271
0
error:
272
0
  destroy_worker_reactor();
273
0
  return -1;
274
0
}
275
276
void tcp_worker_proc_loop(void)
277
0
{
278
  /* main loop */
279
0
  reactor_main_loop( worker_reactor_timeout, error,);
280
0
  LM_CRIT("exiting...");
281
0
  exit(-1);
282
0
error:
283
0
  destroy_worker_reactor();
284
0
}
285
286
287
void tcp_terminate_worker(void)
288
0
{
289
  /*remove from reactor all the shared fds, so we stop reading from them */
290
291
  /*remove timer jobs pipe */
292
0
  reactor_del_reader( timer_fd_out, -1, 0);
293
294
  /*remove IPC dispatcher pipe */
295
0
  reactor_del_reader( IPC_FD_READ_SHARED, -1, 0);
296
297
  /*remove private IPC pipe */
298
0
  reactor_del_reader( IPC_FD_READ_SELF, -1, 0);
299
300
  /*remove unix sock to TCP main */
301
0
  reactor_del_reader( _my_fd_to_tcp_main, -1, 0);
302
303
0
  _termination_in_progress = 1;
304
305
  /* let's drain the private IPC */
306
0
  ipc_handle_all_pending_jobs(IPC_FD_READ_SELF);
307
308
  /* what is left now is the reactor are async fd's, so we need to 
309
   * wait to complete all of them */
310
0
  if (reactor_is_empty())
311
0
    dynamic_process_final_exit();
312
313
  /* the exit will be triggered by the reactor, when empty */
314
0
  LM_INFO("reactor not empty, waiting for pending async/conns\n");
315
0
}