Coverage Report

Created: 2026-07-16 06:51

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 && (c->flags & F_CONN_REMOVED_WRITE)) ||
67
0
       c->fd == -1))
68
0
    pending_data = 1;
69
0
  if (pending_data) {
70
0
    if (tcp_async_write_job(c) < 0) {
71
0
      LM_ERR("failed to queue direct TCP write job for conn %p / %u\n",
72
0
        c, c->id);
73
0
      tcpconn_put(c);
74
0
    }
75
0
    return;
76
0
  }
77
0
  tcpconn_put(c);
78
0
}
79
80
81
struct tcp_ipc_payload {
82
  struct receive_info rcv;
83
  struct tcp_connection *conn;
84
  int msg_len;
85
  int data_len;
86
  char msg_buf[0];
87
};
88
89
90
/*! \brief
91
 *  handle io routine, based on the fd_map type
92
 * (it will be called from reactor_main_loop )
93
 * params:  fm  - pointer to a fd hash entry
94
 *          idx - index in the fd_array (or -1 if not known)
95
 * return: -1 on error, or when we are not interested any more on reads
96
 *            from this fd (e.g.: we are closing it )
97
 *          0 on EAGAIN or when by some other way it is known that no more
98
 *            io events are queued on the fd (the receive buffer is empty).
99
 *            Usefull to detect when there are no more io events queued for
100
 *            sigio_rt, epoll_et, kqueue.
101
 *         >0 on successful read from the fd (when there might be more io
102
 *            queued -- the receive buffer might still be non-empty)
103
 */
104
inline static int handle_io(struct fd_map* fm, int idx,int event_type)
105
0
{
106
0
  int ret=0;
107
0
  int n=0;
108
109
0
  (void)idx;
110
0
  pt_become_active();
111
112
0
  pre_run_handle_script_reload(fm->app_flags);
113
114
0
  profiling_proc_start( LEVEL_SIP, 1);
115
116
0
  switch(fm->type){
117
0
    case F_TIMER_JOB:
118
0
      profiling_proc_enter( LEVEL_FULL, "timer_job", 1 );
119
0
      handle_timer_job();
120
0
      profiling_proc_exit( LEVEL_FULL, "timer_job", n);
121
0
      break;
122
0
    case F_SCRIPT_ASYNC:
123
0
      profiling_proc_enter( LEVEL_SIP, "async_script", 0 );
124
0
      n = async_script_resume_f( fm->fd, fm->data,
125
0
        (event_type==IO_WATCH_TIMEOUT)?1:0 );
126
0
      profiling_proc_exit( LEVEL_SIP, "async_script", n);
127
0
      break;
128
0
    case F_FD_ASYNC:
129
0
      profiling_proc_enter( LEVEL_SIP, "async_fd", 0 );
130
0
      n = async_fd_resume( fm->fd, fm->data);
131
0
      profiling_proc_exit( LEVEL_SIP, "async_fd", n);
132
0
      break;
133
0
    case F_LAUNCH_ASYNC:
134
0
      profiling_proc_enter( LEVEL_SIP, "async_launch", 0 );
135
0
      n = async_launch_resume( fm->fd, fm->data);
136
0
      profiling_proc_exit( LEVEL_SIP, "async_launch", n);
137
0
      break;
138
0
    case F_IPC:
139
0
      profiling_proc_enter( LEVEL_SIP, "ipc_job", 1 );
140
0
      ipc_handle_job(fm->fd);
141
0
      profiling_proc_exit( LEVEL_SIP, "ipc_job", n );
142
0
      break;
143
0
    case F_TCPMAIN:
144
0
    {
145
0
      struct tcp_ipc_payload *payload;
146
0
      uintptr_t payload_ptr;
147
0
again_payload:
148
0
      ret = n = recv(fm->fd, &payload_ptr, sizeof(payload_ptr),
149
0
        MSG_DONTWAIT);
150
0
      if (n < 0) {
151
0
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
152
0
          ret = 0;
153
0
          break;
154
0
        } else if (errno == EINTR) {
155
0
          goto again_payload;
156
0
        }
157
0
        LM_CRIT("read from tcp main dispatch socket failed: %s\n",
158
0
          strerror(errno));
159
0
        goto error;
160
0
      }
161
0
      if (n == 0) {
162
0
        LM_WARN("EOF received from tcp main dispatch socket\n");
163
0
        goto error;
164
0
      }
165
0
      if (n != (int)sizeof(payload_ptr)) {
166
0
        LM_CRIT("short read on tcp main dispatch socket: %d bytes\n", n);
167
0
        goto error;
168
0
      }
169
170
0
      payload = (struct tcp_ipc_payload *)(uintptr_t)payload_ptr;
171
0
      if (!payload) {
172
0
        LM_BUG("null payload pointer from tcp main\n");
173
0
        break;
174
0
      }
175
176
0
      profiling_proc_enter( LEVEL_SIP,
177
0
        ss_merge256( protos[payload->rcv.proto].name,
178
0
        " proto handling"), 0);
179
180
0
      bind_address = payload->rcv.bind_address;
181
0
      if (payload->rcv.proto > PROTO_NONE &&
182
0
          payload->rcv.proto < PROTO_LAST &&
183
0
          is_tcp_based_proto(payload->rcv.proto) &&
184
0
          protos[payload->rcv.proto].net.stream.handle) {
185
0
        if ( (n=protos[payload->rcv.proto].net.stream.handle(
186
0
            payload->msg_buf, payload->msg_len, &payload->rcv,
187
0
            payload->data_len ?
188
0
            payload->msg_buf + payload->msg_len + 1 : NULL,
189
0
            payload->data_len)) < 0)
190
0
          LM_ERR("TCP dispatch handler failed\n");
191
0
      } else if ( (n=receive_msg(payload->msg_buf, payload->msg_len,
192
0
          &payload->rcv, NULL, 0)) < 0) {
193
0
        LM_ERR("receive_msg() failed for dispatched TCP message\n");
194
0
      }
195
196
0
      profiling_proc_exit( LEVEL_SIP, "handling done", n);
197
198
0
      if (payload->conn)
199
0
        tcpconn_put(payload->conn);
200
0
      shm_free(payload);
201
0
      break;
202
0
    }
203
0
    case F_NONE:
204
0
      LM_CRIT("empty fd map %p: "
205
0
            "{%d, %d, %p}\n", fm,
206
0
            fm->fd, fm->type, fm->data);
207
0
      goto error;
208
0
    default:
209
0
      LM_CRIT("unknown fd type %d\n", fm->type);
210
0
      goto error;
211
0
  }
212
213
0
  if (_termination_in_progress==1) {
214
    /* check if anything is still left */
215
0
    if (reactor_is_empty()) {
216
0
      LM_WARN("reactor got empty while termination in progress\n");
217
0
      ipc_handle_all_pending_jobs(IPC_FD_READ_SELF);
218
0
      if (reactor_is_empty())
219
0
        dynamic_process_final_exit();
220
0
    }
221
0
  }
222
223
0
  profiling_proc_end( LEVEL_SIP, 0 );
224
225
0
  post_run_handle_script_reload();
226
227
0
  pt_become_idle();
228
0
  return ret;
229
0
error:
230
0
  profiling_proc_end( LEVEL_SIP, -1 );
231
0
  pt_become_idle();
232
0
  return -1;
233
0
}
234
235
236
int tcp_worker_proc_reactor_init( int unix_sock)
237
0
{
238
  /* init reactor for TCP worker */
239
0
  tcpmain_sock=unix_sock; /* init com. socket */
240
0
  if ( init_worker_reactor( "TCP_worker", RCT_PRIO_MAX)<0 ) {
241
0
    goto error;
242
0
  }
243
244
  /* start watching for the timer jobs */
245
0
  if (reactor_add_reader( timer_fd_out, F_TIMER_JOB, RCT_PRIO_TIMER,NULL)<0){
246
0
    LM_CRIT("failed to add timer pipe_out to reactor\n");
247
0
    goto error;
248
0
  }
249
250
  /* init: start watching for the IPC jobs */
251
0
  if (reactor_add_reader(IPC_FD_READ_SELF, F_IPC, RCT_PRIO_ASYNC, NULL)<0){
252
0
    LM_CRIT("failed to add IPC pipe to reactor\n");
253
0
    goto error;
254
0
  }
255
256
  /* init: start watching for IPC "dispatched" jobs */
257
0
  if (reactor_add_reader(IPC_FD_READ_SHARED, F_IPC, RCT_PRIO_ASYNC, NULL)<0){
258
0
    LM_CRIT("failed to add IPC shared pipe to reactor\n");
259
0
    return -1;
260
0
  }
261
262
  /* add the unix socket */
263
0
  if (reactor_add_reader( tcpmain_sock, F_TCPMAIN, RCT_PRIO_PROC, NULL)<0) {
264
0
    LM_CRIT("failed to add socket to the fd list\n");
265
0
    goto error;
266
0
  }
267
0
  _my_fd_to_tcp_main = tcpmain_sock;
268
269
0
  return 0;
270
0
error:
271
0
  destroy_worker_reactor();
272
0
  return -1;
273
0
}
274
275
void tcp_worker_proc_loop(void)
276
0
{
277
  /* main loop */
278
0
  reactor_main_loop(TCP_CHILD_SELECT_TIMEOUT, error,);
279
0
  LM_CRIT("exiting...");
280
0
  exit(-1);
281
0
error:
282
0
  destroy_worker_reactor();
283
0
}
284
285
286
void tcp_terminate_worker(void)
287
0
{
288
  /*remove from reactor all the shared fds, so we stop reading from them */
289
290
  /*remove timer jobs pipe */
291
0
  reactor_del_reader( timer_fd_out, -1, 0);
292
293
  /*remove IPC dispatcher pipe */
294
0
  reactor_del_reader( IPC_FD_READ_SHARED, -1, 0);
295
296
  /*remove private IPC pipe */
297
0
  reactor_del_reader( IPC_FD_READ_SELF, -1, 0);
298
299
  /*remove unix sock to TCP main */
300
0
  reactor_del_reader( _my_fd_to_tcp_main, -1, 0);
301
302
0
  _termination_in_progress = 1;
303
304
  /* let's drain the private IPC */
305
0
  ipc_handle_all_pending_jobs(IPC_FD_READ_SELF);
306
307
  /* what is left now is the reactor are async fd's, so we need to 
308
   * wait to complete all of them */
309
0
  if (reactor_is_empty())
310
0
    dynamic_process_final_exit();
311
312
  /* the exit will be triggered by the reactor, when empty */
313
0
  LM_INFO("reactor not empty, waiting for pending async/conns\n");
314
0
}