Coverage Report

Created: 2026-10-28 06:08

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
#define _GNU_SOURCE /* for F_{GET,SET}PIPE_SZ */
22
23
#include <string.h>
24
#include <errno.h>
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
28
#include "ipc.h"
29
#include "dprint.h"
30
#include "profiling.h"
31
#include "mem/mem.h"
32
33
#include <fcntl.h>
34
35
0
#define IPC_HANDLER_NAME_MAX  32
36
0
#define IPC_SHARED_PIPE_SIZE  (1024 * 1024)
37
typedef struct _ipc_handler {
38
  /* handler function */
39
  ipc_handler_f *func;
40
  /* same name/description, null terminated */
41
  char name[IPC_HANDLER_NAME_MAX+1];
42
} ipc_handler;
43
44
typedef struct _ipc_job {
45
  /* the ID (internal) of the process sending the job */
46
  unsigned short snd_proc;
47
  /* the job's handler type */
48
  ipc_handler_type handler_type;
49
  /* the payload of the job, just pointers */
50
  void *payload1;
51
  void *payload2;
52
} ipc_job;
53
54
static ipc_handler *ipc_handlers = NULL;
55
static unsigned int ipc_handlers_no = 0;
56
57
/* shared IPC support: dispatching a job to a random OpenSIPS worker */
58
static int ipc_shared_pipe[2];
59
60
/* IPC type used for RPC - a self registered type */
61
static ipc_handler_type ipc_rpc_type = 0;
62
63
/* FD (pipe) used for dispatching IPC jobs between all processes (1 to any) */
64
int ipc_shared_fd_read;
65
66
67
int enlarge_pipe(int fd, int target_size)
68
0
{
69
#if !defined(F_GETPIPE_SZ) || !defined(F_SETPIPE_SZ)
70
  (void)fd;
71
  (void)target_size;
72
  errno = ENOTSUP;
73
  return -1;
74
#else
75
0
  int initial_size, requested_size, actual_size;
76
77
0
  initial_size = fcntl(fd, F_GETPIPE_SZ);
78
0
  if (initial_size <= 0)
79
0
    return -1;
80
0
  if (initial_size >= target_size)
81
0
    return initial_size;
82
83
0
  for (requested_size = target_size;
84
0
    requested_size > initial_size; requested_size /= 2) {
85
0
    actual_size = fcntl(fd, F_SETPIPE_SZ, requested_size);
86
0
    if (actual_size > 0) {
87
0
      LM_DBG("enlarged pipe %d from %d to %d bytes\n",
88
0
        fd, initial_size, actual_size);
89
0
      return actual_size;
90
0
    }
91
0
  }
92
93
0
  return initial_size;
94
0
#endif
95
0
}
96
97
int init_ipc(void)
98
0
{
99
0
  int optval, pipe_size;
100
101
  /* create the pipe for dispatching the timer jobs */
102
0
  if (pipe(ipc_shared_pipe) != 0) {
103
0
    LM_ERR("failed to create ipc pipe (%s)!\n", strerror(errno));
104
0
    return -1;
105
0
  }
106
107
  /* make reading fd non-blocking */
108
0
  optval = fcntl(ipc_shared_pipe[0], F_GETFL);
109
0
  if (optval == -1) {
110
0
    LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
111
0
    return -1;
112
0
  }
113
114
0
  if (fcntl(ipc_shared_pipe[0], F_SETFL, optval|O_NONBLOCK) == -1) {
115
0
    LM_ERR("set non-blocking failed: (%d) %s\n", errno, strerror(errno));
116
0
    return -1;
117
0
  }
118
119
  /* The shared IPC pipe may receive large bursts of jobs.  Increase its
120
   * capacity where supported, but keep startup working if the kernel or
121
   * the per-user pipe quota does not allow the requested size. */
122
0
  pipe_size = enlarge_pipe(ipc_shared_pipe[0], IPC_SHARED_PIPE_SIZE);
123
0
  if (pipe_size == -1) {
124
0
    if (errno != ENOTSUP)
125
0
      LM_WARN("failed to enlarge the shared IPC pipe: %s\n",
126
0
        strerror(errno));
127
0
  } else if (pipe_size < IPC_SHARED_PIPE_SIZE) {
128
0
    LM_WARN("shared IPC pipe size is %d instead of the requested %d bytes\n",
129
0
      pipe_size, IPC_SHARED_PIPE_SIZE);
130
0
  }
131
132
  /* Never let a full shared pipe block a producer process indefinitely. */
133
0
  optval = fcntl(ipc_shared_pipe[1], F_GETFL);
134
0
  if (optval == -1) {
135
0
    LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
136
0
    return -1;
137
0
  }
138
139
0
  if (fcntl(ipc_shared_pipe[1], F_SETFL, optval|O_NONBLOCK) == -1) {
140
0
    LM_ERR("set non-blocking write failed: (%d) %s\n",
141
0
      errno, strerror(errno));
142
0
    return -1;
143
0
  }
144
145
0
  ipc_shared_fd_read = ipc_shared_pipe[0];
146
147
  /* self-register the IPC type for RPC */
148
0
  ipc_rpc_type = ipc_register_handler( NULL, "RPC");
149
0
  if (ipc_bad_handler_type(ipc_rpc_type)) {
150
0
    LM_ERR("failed to self register RPC type\n");
151
0
    return -1;
152
0
  }
153
154
  /* we are all set */
155
0
  return 0;
156
0
}
157
158
159
int create_ipc_pipes( int proc_no )
160
0
{
161
0
  int optval, i;
162
163
0
  for( i=0 ; i<proc_no ; i++ ) {
164
0
    if (pipe(pt[i].ipc_pipe_holder)<0) {
165
0
      LM_ERR("failed to create IPC pipe for process %d, err %d/%s\n",
166
0
        i, errno, strerror(errno));
167
0
      return -1;
168
0
    }
169
170
    /* make writing fd non-blocking */
171
0
    optval = fcntl( pt[i].ipc_pipe_holder[1], F_GETFL);
172
0
    if (optval == -1) {
173
0
      LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
174
0
      return -1;
175
0
    }
176
177
0
    if (fcntl(pt[i].ipc_pipe_holder[1], F_SETFL, optval|O_NONBLOCK) == -1){
178
0
      LM_ERR("set non-blocking write failed: (%d) %s\n",
179
0
        errno, strerror(errno));
180
0
      return -1;
181
0
    }
182
183
184
0
    if (pipe(pt[i].ipc_sync_pipe_holder)<0) {
185
0
      LM_ERR("failed to create IPC sync pipe for process %d, "
186
0
        "err %d/%s\n", i, errno, strerror(errno));
187
0
      return -1;
188
0
    }
189
190
    /* make writing fd non-blocking */
191
0
    optval = fcntl( pt[i].ipc_sync_pipe_holder[1], F_GETFL);
192
0
    if (optval == -1) {
193
0
      LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno));
194
0
      return -1;
195
0
    }
196
197
0
    if (fcntl(pt[i].ipc_sync_pipe_holder[1], F_SETFL, optval|O_NONBLOCK) == -1){
198
0
      LM_ERR("set non-blocking write failed: (%d) %s\n",
199
0
        errno, strerror(errno));
200
0
      return -1;
201
0
    }
202
203
0
  }
204
0
  return 0;
205
0
}
206
207
208
ipc_handler_type ipc_register_handler( ipc_handler_f *hdl, char *name)
209
0
{
210
0
  ipc_handler *new;
211
212
  /* allocate an n+1 new buffer to accomodate the new handler */
213
0
  new = (ipc_handler*)
214
0
    pkg_malloc( (ipc_handlers_no+1)*sizeof(ipc_handler) );
215
0
  if (new==NULL) {
216
0
    LM_ERR("failed to alloctes IPC handler array for size %d\n",
217
0
      ipc_handlers_no+1);
218
0
    return -1;
219
0
  }
220
221
  /* copy previous records, if any */
222
0
  if (ipc_handlers) {
223
0
    memcpy( new, ipc_handlers, ipc_handlers_no*sizeof(ipc_handler) );
224
0
    pkg_free( ipc_handlers );
225
0
  }
226
227
  /* copy handler function */
228
0
  new[ipc_handlers_no].func = hdl;
229
230
  /* copy the name, trunkate it needed, but keep it null terminated */
231
0
  strncpy( new[ipc_handlers_no].name , name, IPC_HANDLER_NAME_MAX);
232
0
  new[ipc_handlers_no].name[IPC_HANDLER_NAME_MAX] = 0;
233
234
0
  ipc_handlers = new;
235
236
0
  LM_DBG("IPC type %d [%s] registered with handler %p\n",
237
0
    ipc_handlers_no, ipc_handlers[ipc_handlers_no].name, hdl );
238
239
0
  return ipc_handlers_no++;
240
0
}
241
242
243
static inline int __ipc_send_job(int fd, int dst_proc, ipc_handler_type type,
244
                        void *payload1, void *payload2)
245
0
{
246
0
  ipc_job job;
247
0
  int n;
248
249
  // FIXME - check whether the destination process still listens for reads,
250
  // otherwise its pipe may fill up and the job will be rejected
251
0
  memset(&job, 0, sizeof job);
252
253
0
  job.snd_proc = (short)process_no;
254
0
  job.handler_type = type;
255
0
  job.payload1 = payload1;
256
0
  job.payload2 = payload2;
257
258
0
again:
259
  /* The IPC write fds are set to non-blocking (to be sure we do not
260
   * escalate into a global blocking if the consumer processes got stuck).
261
   * In such case EAGAIN or EWOULDBLOCK will be thrown and we will
262
   * handle as generic error, nothing special to do.
263
   */
264
0
  n = write(fd, &job, sizeof(job) );
265
0
  if (n<0) {
266
0
    if (errno==EAGAIN || errno==EWOULDBLOCK)
267
0
      LM_CRIT("blocking detected while sending job type %d[%s] on %d "
268
0
        " to proc id %d/%d [%s]\n", type, ipc_handlers[type].name, fd,
269
0
        dst_proc, (dst_proc==-1)?-1:pt[dst_proc].pid ,
270
0
        (dst_proc==-1)?"n/a":pt[dst_proc].desc);
271
0
    else if (errno==EINTR)
272
0
      goto again;
273
0
    else
274
0
      LM_ERR("sending job type %d[%s] on %d failed: %s\n",
275
0
        type, ipc_handlers[type].name, fd, strerror(errno));
276
0
    return -1;
277
0
  }
278
0
  return 0;
279
0
}
280
281
int ipc_send_job(int dst_proc, ipc_handler_type type, void *payload)
282
0
{
283
0
  return __ipc_send_job(IPC_FD_WRITE(dst_proc), dst_proc,
284
0
    type, payload, NULL);
285
0
}
286
287
int ipc_dispatch_job(ipc_handler_type type, void *payload)
288
0
{
289
0
  return __ipc_send_job(ipc_shared_pipe[1], -1, type, payload, NULL);
290
0
}
291
292
int ipc_send_rpc(int dst_proc, ipc_rpc_f *rpc, void *param)
293
0
{
294
  /* wait for the write IPC FD to be available, for a maximum 200ms */
295
0
  busy_wait_for(IPC_FD_WRITE(dst_proc) >= 0, 200000, 10);
296
0
  return __ipc_send_job(IPC_FD_WRITE(dst_proc), dst_proc,
297
0
    ipc_rpc_type, rpc, param);
298
0
}
299
300
int ipc_send_rpc_all(ipc_rpc_f *rpc, void *param)
301
0
{
302
0
  int p, count = 0;
303
304
0
  for (p = 1; p < counted_max_processes; p++) {
305
0
    if (pt[p].flags & OSS_PROC_NO_IPC)
306
0
      continue;
307
0
    if (p == process_no) {
308
      /* run line the cmd for the proc itself */
309
0
      rpc(process_no, param);
310
0
      count++;
311
0
    } else {
312
0
      if (ipc_send_rpc(p, rpc, param) >= 0)
313
0
        count++;
314
0
    }
315
0
  }
316
0
  return count;
317
0
}
318
319
int ipc_dispatch_rpc( ipc_rpc_f *rpc, void *param)
320
0
{
321
0
  return __ipc_send_job(ipc_shared_pipe[1], -1, ipc_rpc_type, rpc, param);
322
0
}
323
324
int ipc_send_sync_reply(int dst_proc, void *param)
325
0
{
326
0
  int n;
327
328
0
again:
329
0
  n = write(IPC_FD_SYNC_WRITE(dst_proc), &param, sizeof(param));
330
0
  if (n<0) {
331
0
    if (errno==EINTR)
332
0
      goto again;
333
0
    LM_ERR("sending sync rpc %d[%s]\n", errno, strerror(errno));
334
0
    return -1;
335
0
  }
336
0
  return 0;
337
0
}
338
339
int ipc_recv_sync_reply(void **param)
340
0
{
341
0
  void *ret;
342
0
  int n;
343
344
0
again:
345
0
  n = read(IPC_FD_SYNC_READ_SELF, &ret, sizeof(ret));
346
0
  if (n < sizeof(*ret)) {
347
0
    if (errno == EINTR)
348
0
      goto again;
349
    /* if we got here, it's definitely an error, because the socket is
350
     * blocking, so we can't read partial messages */
351
0
    LM_ERR("read failed:[%d] %s\n", errno, strerror(errno));
352
0
    return -1;
353
0
  }
354
0
  *param = ret;
355
0
  return 0;
356
0
}
357
358
int ipc_running_rpc_job;
359
void ipc_handle_job(int fd)
360
0
{
361
0
  ipc_job job;
362
0
  int n;
363
364
  /* read one IPC job from the pipe; even if the read is blocking,
365
   * we are here triggered from the reactor, on a READ event, so 
366
   * we shouldn;t ever block */
367
0
  n = read(fd, &job, sizeof(job) );
368
0
  if (n==-1) {
369
0
    if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK )
370
0
      return;
371
0
    LM_ERR("read failed:[%d] %s\n", errno, strerror(errno));
372
0
    return;
373
0
  }
374
375
  /* suppress the E_CORE_LOG event for the below log while handling
376
   * the event itself */
377
0
  suppress_proc_log_event();
378
379
0
  LM_DBG("received job type %d[%s] from process %d\n",
380
0
    job.handler_type, ipc_handlers[job.handler_type].name, job.snd_proc);
381
382
0
  reset_proc_log_event();
383
384
0
  profiling_proc_enter( LEVEL_SIP, ipc_handlers[job.handler_type].name, 0 );
385
  /* custom handling for RPC type */
386
0
  if (job.handler_type==ipc_rpc_type) {
387
0
    ipc_running_rpc_job = 1;
388
0
    ((ipc_rpc_f*)job.payload1)( job.snd_proc, job.payload2);
389
0
    ipc_running_rpc_job = 0;
390
0
  } else {
391
    /* generic registered type */
392
0
    ipc_handlers[job.handler_type].func( job.snd_proc, job.payload1);
393
0
  }
394
0
  profiling_proc_exit( LEVEL_SIP, ipc_handlers[job.handler_type].name, 0 );
395
396
0
  return;
397
0
}
398
399
400
void ipc_handle_all_pending_jobs(int fd)
401
0
{
402
0
  char buf;
403
404
0
  while ( recv(fd, &buf, 1, MSG_DONTWAIT|MSG_PEEK)==1 )
405
0
    ipc_handle_job(fd);
406
0
}