Coverage Report

Created: 2026-07-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/ipc.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2017 OpenSIPS Project
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * opensips is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
22
#include <string.h>
23
#include <errno.h>
24
#include <sys/types.h>
25
#include <sys/socket.h>
26
27
#include "ipc.h"
28
#include "dprint.h"
29
#include "profiling.h"
30
#include "mem/mem.h"
31
32
#include <fcntl.h>
33
34
0
#define IPC_HANDLER_NAME_MAX  32
35
typedef struct _ipc_handler {
36
  /* handler function */
37
  ipc_handler_f *func;
38
  /* same name/description, null terminated */
39
  char name[IPC_HANDLER_NAME_MAX+1];
40
} ipc_handler;
41
42
typedef struct _ipc_job {
43
  /* the ID (internal) of the process sending the job */
44
  unsigned short snd_proc;
45
  /* the job's handler type */
46
  ipc_handler_type handler_type;
47
  /* the payload of the job, just pointers */
48
  void *payload1;
49
  void *payload2;
50
} ipc_job;
51
52
static ipc_handler *ipc_handlers = NULL;
53
static unsigned int ipc_handlers_no = 0;
54
55
/* shared IPC support: dispatching a job to a random OpenSIPS worker */
56
static int ipc_shared_pipe[2];
57
58
/* IPC type used for RPC - a self registered type */
59
static ipc_handler_type ipc_rpc_type = 0;
60
61
/* FD (pipe) used for dispatching IPC jobs between all processes (1 to any) */
62
int ipc_shared_fd_read;
63
64
int init_ipc(void)
65
0
{
66
0
  int optval;
67
68
  /* create the pipe for dispatching the timer jobs */
69
0
  if (pipe(ipc_shared_pipe) != 0) {
70
0
    LM_ERR("failed to create ipc pipe (%s)!\n", strerror(errno));
71
0
    return -1;
72
0
  }
73
74
  /* make reading fd non-blocking */
75
0
  optval = fcntl(ipc_shared_pipe[0], F_GETFL);
76
0
  if (optval == -1) {
77
0
    LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
78
0
    return -1;
79
0
  }
80
81
0
  if (fcntl(ipc_shared_pipe[0], F_SETFL, optval|O_NONBLOCK) == -1) {
82
0
    LM_ERR("set non-blocking failed: (%d) %s\n", errno, strerror(errno));
83
0
    return -1;
84
0
  }
85
86
0
  ipc_shared_fd_read = ipc_shared_pipe[0];
87
88
  /* self-register the IPC type for RPC */
89
0
  ipc_rpc_type = ipc_register_handler( NULL, "RPC");
90
0
  if (ipc_bad_handler_type(ipc_rpc_type)) {
91
0
    LM_ERR("failed to self register RPC type\n");
92
0
    return -1;
93
0
  }
94
95
  /* we are all set */
96
0
  return 0;
97
0
}
98
99
100
int create_ipc_pipes( int proc_no )
101
0
{
102
0
  int optval, i;
103
104
0
  for( i=0 ; i<proc_no ; i++ ) {
105
0
    if (pipe(pt[i].ipc_pipe_holder)<0) {
106
0
      LM_ERR("failed to create IPC pipe for process %d, err %d/%s\n",
107
0
        i, errno, strerror(errno));
108
0
      return -1;
109
0
    }
110
111
    /* make writing fd non-blocking */
112
0
    optval = fcntl( pt[i].ipc_pipe_holder[1], F_GETFL);
113
0
    if (optval == -1) {
114
0
      LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
115
0
      return -1;
116
0
    }
117
118
0
    if (fcntl(pt[i].ipc_pipe_holder[1], F_SETFL, optval|O_NONBLOCK) == -1){
119
0
      LM_ERR("set non-blocking write failed: (%d) %s\n",
120
0
        errno, strerror(errno));
121
0
      return -1;
122
0
    }
123
124
125
0
    if (pipe(pt[i].ipc_sync_pipe_holder)<0) {
126
0
      LM_ERR("failed to create IPC sync pipe for process %d, "
127
0
        "err %d/%s\n", i, errno, strerror(errno));
128
0
      return -1;
129
0
    }
130
131
    /* make writing fd non-blocking */
132
0
    optval = fcntl( pt[i].ipc_sync_pipe_holder[1], F_GETFL);
133
0
    if (optval == -1) {
134
0
      LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
135
0
      return -1;
136
0
    }
137
138
0
    if (fcntl(pt[i].ipc_sync_pipe_holder[1], F_SETFL, optval|O_NONBLOCK) == -1){
139
0
      LM_ERR("set non-blocking write failed: (%d) %s\n",
140
0
        errno, strerror(errno));
141
0
      return -1;
142
0
    }
143
144
0
  }
145
0
  return 0;
146
0
}
147
148
149
ipc_handler_type ipc_register_handler( ipc_handler_f *hdl, char *name)
150
0
{
151
0
  ipc_handler *new;
152
153
  /* allocate an n+1 new buffer to accomodate the new handler */
154
0
  new = (ipc_handler*)
155
0
    pkg_malloc( (ipc_handlers_no+1)*sizeof(ipc_handler) );
156
0
  if (new==NULL) {
157
0
    LM_ERR("failed to alloctes IPC handler array for size %d\n",
158
0
      ipc_handlers_no+1);
159
0
    return -1;
160
0
  }
161
162
  /* copy previous records, if any */
163
0
  if (ipc_handlers) {
164
0
    memcpy( new, ipc_handlers, ipc_handlers_no*sizeof(ipc_handler) );
165
0
    pkg_free( ipc_handlers );
166
0
  }
167
168
  /* copy handler function */
169
0
  new[ipc_handlers_no].func = hdl;
170
171
  /* copy the name, trunkate it needed, but keep it null terminated */
172
0
  strncpy( new[ipc_handlers_no].name , name, IPC_HANDLER_NAME_MAX);
173
0
  new[ipc_handlers_no].name[IPC_HANDLER_NAME_MAX] = 0;
174
175
0
  ipc_handlers = new;
176
177
0
  LM_DBG("IPC type %d [%s] registered with handler %p\n",
178
0
    ipc_handlers_no, ipc_handlers[ipc_handlers_no].name, hdl );
179
180
0
  return ipc_handlers_no++;
181
0
}
182
183
184
static inline int __ipc_send_job(int fd, int dst_proc, ipc_handler_type type,
185
                        void *payload1, void *payload2)
186
0
{
187
0
  ipc_job job;
188
0
  int n;
189
190
  // FIXME - we should check if the destination process really listens
191
  // for read, otherwise we may end up filling in the pipe and block
192
0
  memset(&job, 0, sizeof job);
193
194
0
  job.snd_proc = (short)process_no;
195
0
  job.handler_type = type;
196
0
  job.payload1 = payload1;
197
0
  job.payload2 = payload2;
198
199
0
again:
200
  /* The per-proc IPC write fds are sent to non-blocking (to be sure we
201
   * do not escalate into a global blocking if a single process got stuck.
202
   * In such care the EAGAIN or EWOULDBLOCK will be thrown and we will
203
   * handle as generic error, nothing special to do.
204
   */
205
0
  n = write(fd, &job, sizeof(job) );
206
0
  if (n<0) {
207
0
    if (errno==EAGAIN || errno==EWOULDBLOCK)
208
0
      LM_CRIT("blocking detected while sending job type %d[%s] on %d "
209
0
        " to proc id %d/%d [%s]\n", type, ipc_handlers[type].name, fd,
210
0
        dst_proc, (dst_proc==-1)?-1:pt[dst_proc].pid ,
211
0
        (dst_proc==-1)?"n/a":pt[dst_proc].desc);
212
0
    else if (errno==EINTR)
213
0
      goto again;
214
0
    else
215
0
      LM_ERR("sending job type %d[%s] on %d failed: %s\n",
216
0
        type, ipc_handlers[type].name, fd, strerror(errno));
217
0
    return -1;
218
0
  }
219
0
  return 0;
220
0
}
221
222
int ipc_send_job(int dst_proc, ipc_handler_type type, void *payload)
223
0
{
224
0
  return __ipc_send_job(IPC_FD_WRITE(dst_proc), dst_proc,
225
0
    type, payload, NULL);
226
0
}
227
228
int ipc_dispatch_job(ipc_handler_type type, void *payload)
229
0
{
230
0
  return __ipc_send_job(ipc_shared_pipe[1], -1, type, payload, NULL);
231
0
}
232
233
int ipc_send_rpc(int dst_proc, ipc_rpc_f *rpc, void *param)
234
0
{
235
  /* wait for the write IPC FD to be available, for a maximum 200ms */
236
0
  busy_wait_for(IPC_FD_WRITE(dst_proc) >= 0, 200000, 10);
237
0
  return __ipc_send_job(IPC_FD_WRITE(dst_proc), dst_proc,
238
0
    ipc_rpc_type, rpc, param);
239
0
}
240
241
int ipc_send_rpc_all(ipc_rpc_f *rpc, void *param)
242
0
{
243
0
  int p, count = 0;
244
245
0
  for (p = 1; p < counted_max_processes; p++) {
246
0
    if (pt[p].flags & OSS_PROC_NO_IPC)
247
0
      continue;
248
0
    if (p == process_no) {
249
      /* run line the cmd for the proc itself */
250
0
      rpc(process_no, param);
251
0
      count++;
252
0
    } else {
253
0
      if (ipc_send_rpc(p, rpc, param) >= 0)
254
0
        count++;
255
0
    }
256
0
  }
257
0
  return count;
258
0
}
259
260
int ipc_dispatch_rpc( ipc_rpc_f *rpc, void *param)
261
0
{
262
0
  return __ipc_send_job(ipc_shared_pipe[1], -1, ipc_rpc_type, rpc, param);
263
0
}
264
265
int ipc_send_sync_reply(int dst_proc, void *param)
266
0
{
267
0
  int n;
268
269
0
again:
270
0
  n = write(IPC_FD_SYNC_WRITE(dst_proc), &param, sizeof(param));
271
0
  if (n<0) {
272
0
    if (errno==EINTR)
273
0
      goto again;
274
0
    LM_ERR("sending sync rpc %d[%s]\n", errno, strerror(errno));
275
0
    return -1;
276
0
  }
277
0
  return 0;
278
0
}
279
280
int ipc_recv_sync_reply(void **param)
281
0
{
282
0
  void *ret;
283
0
  int n;
284
285
0
again:
286
0
  n = read(IPC_FD_SYNC_READ_SELF, &ret, sizeof(ret));
287
0
  if (n < sizeof(*ret)) {
288
0
    if (errno == EINTR)
289
0
      goto again;
290
    /* if we got here, it's definitely an error, because the socket is
291
     * blocking, so we can't read partial messages */
292
0
    LM_ERR("read failed:[%d] %s\n", errno, strerror(errno));
293
0
    return -1;
294
0
  }
295
0
  *param = ret;
296
0
  return 0;
297
0
}
298
299
int ipc_running_rpc_job;
300
void ipc_handle_job(int fd)
301
0
{
302
0
  ipc_job job;
303
0
  int n;
304
305
  /* read one IPC job from the pipe; even if the read is blocking,
306
   * we are here triggered from the reactor, on a READ event, so 
307
   * we shouldn;t ever block */
308
0
  n = read(fd, &job, sizeof(job) );
309
0
  if (n==-1) {
310
0
    if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK )
311
0
      return;
312
0
    LM_ERR("read failed:[%d] %s\n", errno, strerror(errno));
313
0
    return;
314
0
  }
315
316
  /* suppress the E_CORE_LOG event for the below log while handling
317
   * the event itself */
318
0
  suppress_proc_log_event();
319
320
0
  LM_DBG("received job type %d[%s] from process %d\n",
321
0
    job.handler_type, ipc_handlers[job.handler_type].name, job.snd_proc);
322
323
0
  reset_proc_log_event();
324
325
0
  profiling_proc_enter( LEVEL_SIP, ipc_handlers[job.handler_type].name, 0 );
326
  /* custom handling for RPC type */
327
0
  if (job.handler_type==ipc_rpc_type) {
328
0
    ipc_running_rpc_job = 1;
329
0
    ((ipc_rpc_f*)job.payload1)( job.snd_proc, job.payload2);
330
0
    ipc_running_rpc_job = 0;
331
0
  } else {
332
    /* generic registered type */
333
0
    ipc_handlers[job.handler_type].func( job.snd_proc, job.payload1);
334
0
  }
335
0
  profiling_proc_exit( LEVEL_SIP, ipc_handlers[job.handler_type].name, 0 );
336
337
0
  return;
338
0
}
339
340
341
void ipc_handle_all_pending_jobs(int fd)
342
0
{
343
0
  char buf;
344
345
0
  while ( recv(fd, &buf, 1, MSG_DONTWAIT|MSG_PEEK)==1 )
346
0
    ipc_handle_job(fd);
347
0
}
348