Coverage Report

Created: 2024-11-08 06:09

/src/opensips/status_report.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2022 OpenSIPS Solutions
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
#include "dprint.h"
22
#include "locking.h"
23
#include "rw_locking.h"
24
#include "str.h"
25
#include "ut.h"
26
#include "evi/evi.h"
27
#include "mi/fmt.h"
28
#include "status_report.h"
29
30
struct report_rec {
31
  str log;
32
  time_t ts;
33
};
34
35
/* Implementation details
36
 * The list of group is static, groups are to be registered only at startup and
37
 * it does not change at runtime; so it is safe to grap and hold referances
38
 * to groups (as they will not change or be deleted).
39
 * The list of identifiers under a group may change; identifiers may be added
40
 * or deleted at runtime; so, the framework does not expose any reference to
41
 * the identifiers. The identifiers will be looked up each time.
42
 * Both types of lists are protected by a RW lock. WRITE is used only when the
43
 * lists are to changed as elements (adding / removing), otherwise READ is
44
 * used.
45
 * For changes within an identity (like changing status or reports), the
46
 * per-identity lock is used.
47
 */
48
49
typedef struct _sr_identifier {
50
  /* name of the indentifier, allocated in the same mem chunk */
51
  str name;
52
  /* some text detailing / explaining the reason (usually for "KO" reason)
53
   * allocated as a separated mem chunk, to be freed
54
   * it is optional*/
55
  str status_txt;
56
  /* value of the status "> 0" ok; "< 0" not ok ; 0 not allowed */
57
  enum sr_core_states status;
58
  /* size of the "reports" array, pre-allocated */
59
  short max_reports;
60
  /* indexes of first and last used reports
61
   * starts as -1/-1 meaning no report at all */
62
  short first_report;
63
  short last_report;
64
  /* lock for protexting access to "status" and "reports" */
65
  gen_lock_t lock;
66
  /* array of max_reports reports, allocated in the same mem chunk */
67
  struct report_rec *reports;
68
  /* next identifier in the group */
69
  struct _sr_identifier *next;
70
} sr_identifier;
71
72
73
typedef struct _sr_group {
74
  /* name of the group, the buffer is allocated in the same mem chunk */
75
  str name;
76
  /* if public, no script or external function (like MI) will be allowed
77
   * to change the status or report here */
78
  int is_public;
79
  /* identifiers as a simple linked list (the list SHOULD not be empty) */
80
  sr_identifier *identifiers;
81
  /* link to the next group (simple linked list) */
82
  struct _sr_group *next;
83
} sr_group;
84
85
86
/* lock to protect the list of sr_groups and changes on their field */
87
static rw_lock_t *sr_lock = NULL;
88
89
/* global list of 'status_report' groups */
90
static sr_group *sr_groups = NULL;
91
92
/* name to be used as default (or nameless) identifier */
93
static str main_identifier = str_init("main");
94
95
/* event to be raised when the status of an identifier changed */
96
static str sr_event = str_init("E_CORE_SR_STATUS_CHANGED");
97
static event_id_t sr_evi_id;
98
99
/* the SR group of the core */
100
static sr_group *srg_core = NULL;
101
static sr_identifier *sri_core = NULL;
102
103
104
/****************** Internal functions  **********************/
105
106
/* Allocates, initializes and links a new group.
107
 * NOTE 1 - is expects the sr_lock to be already taken !!
108
 */
109
static sr_group* _create_new_group(str *name)
110
0
{
111
0
  sr_group *srg;
112
113
0
  LM_DBG("adding new group [%.*s]\n", name->len, name->s);
114
115
0
  srg=(sr_group*)shm_malloc( sizeof(sr_group) + name->len );
116
0
  if ( srg==NULL ) {
117
0
    LM_ERR("shm allocing for a new group failed\n");
118
0
    return NULL;
119
0
  }
120
121
0
  memset( srg, 0,  sizeof(sr_group));
122
123
  /* set the name*/
124
0
  srg->name.s = (char*)(srg+1);
125
0
  srg->name.len = name->len;
126
0
  memcpy( srg->name.s, name->s, name->len );
127
128
  /* link it first */
129
0
  srg->next = sr_groups;
130
0
  sr_groups = srg;
131
132
0
  return srg;
133
0
}
134
135
136
/* Searches a SR group by its name and returns the pointer to it
137
 * NOTE 1: the function assumes that the sr lock is already taken !!
138
 */
139
static sr_group* _get_group_by_name(str *name)
140
0
{
141
0
  sr_group *srg;
142
143
  /* locate the group */
144
0
  for ( srg=sr_groups ; srg ; srg=srg->next ) {
145
0
    if ( (srg->name.len == name->len) &&
146
0
    strncasecmp( srg->name.s, name->s, name->len)==0 )
147
0
      return srg;
148
0
  }
149
150
0
  return NULL;
151
0
}
152
153
154
/* Searches a SR identifier by its group and name and returns the pointer to it
155
 * NOTE 1: the function assumes that the sr lock is already taken !!
156
 */
157
static sr_identifier* _get_identifier_by_name(sr_group *srg, str *id_name)
158
0
{
159
0
  sr_identifier *sri;
160
161
0
  for( sri=srg->identifiers ; sri ; sri=sri->next )
162
0
    if (sri->name.len==id_name->len && 
163
0
    strncasecmp( sri->name.s, id_name->s, id_name->len)==0)
164
0
      return sri;
165
166
0
  return NULL;
167
0
}
168
169
170
171
/****************** Module (to be used) functions  **********************/
172
173
/* Registers a new SR group by its name
174
 * To be done at startup only
175
 * The modules should typically use their names as groups
176
 */
177
void *sr_register_group( char* name_s, int name_len, int is_public)
178
0
{
179
0
  sr_group *srg = NULL;
180
0
  str name = {name_s, name_len};
181
182
0
  lock_start_write( sr_lock );
183
184
  /* locate the group, for duplicates */
185
0
  srg = _get_group_by_name(&name);
186
0
  if (srg) {
187
0
    LM_ERR("duplicated group already existing [%.*s]\n",
188
0
      name.len, name.s);
189
0
    srg = NULL;
190
0
  } else {
191
0
    srg = _create_new_group( &name );
192
0
    if ( srg==NULL ) {
193
0
      LM_ERR("failed to register group [%.*s]\n",
194
0
        name.len, name.s);
195
0
    }
196
0
    srg->is_public = is_public;
197
0
  }
198
199
0
  lock_stop_write( sr_lock );
200
201
0
  return srg;
202
0
}
203
204
205
/* Search a SR group by name and and returns its pointer
206
 */
207
void *sr_get_group_by_name(  char *name_s, int name_len)
208
0
{
209
0
  sr_group *srg = NULL;
210
0
  str name = {name_s, name_len};
211
212
0
  lock_start_read( sr_lock );
213
214
  /* locate the group, for duplicates */
215
0
  srg = _get_group_by_name( &name );
216
217
0
  lock_stop_read( sr_lock );
218
219
0
  return srg;
220
0
}
221
222
223
/* Registers a new SR identifier into a existing group.
224
 * - The group is mandatory
225
 * - The identifier name is optional; if missing, the default one (per entire
226
 *   group) will be used.
227
 * - The initial status of the identity; 0 is not accepted, it will be
228
 *   converted to 1
229
 * - The status text / details - optional string to detail the status code
230
 * - The max_reports defines how many logs should be kept (last logs) for the
231
 *   identifier; if 0, no logs/reports will be stored.
232
 */
233
int sr_register_identifier( void *group, 
234
    char *identifier_s, int identifier_len,
235
    int init_status, char *status_txt_s, int status_txt_len,
236
    int max_reports)
237
0
{
238
0
  sr_group *srg = (sr_group*)group;
239
0
  str identifier = {identifier_s, identifier_len};
240
0
  char *err_txt = "";
241
0
  sr_identifier *sri = NULL;
242
243
244
0
  if (identifier.s==NULL)
245
0
    identifier = main_identifier;
246
247
0
  LM_DBG("adding new identifier [%.*s] to group [%.*s]\n",
248
0
    identifier.len, identifier.s, srg->name.len, srg->name.s);
249
250
0
  lock_start_write( sr_lock );
251
252
  /* double check if we already have such identifier, just to be sure */
253
0
  sri = _get_identifier_by_name( srg, &identifier);
254
0
  if (sri) {
255
0
    err_txt = "add identifier (already existing)";
256
0
    sri = NULL; /* just to avoid its freeing on error handling */
257
0
    goto error;
258
0
  }
259
260
  /* allocate a new identifier */
261
0
  sri = (sr_identifier*)shm_malloc( sizeof(sr_identifier) + identifier.len
262
0
    + max_reports*sizeof(struct report_rec) );
263
0
  if (sri==NULL) {
264
0
    err_txt = "shm allocate new identifier";
265
0
    goto error;
266
0
  }
267
0
  memset(sri, 0, sizeof(sr_identifier) + identifier.len
268
0
    + max_reports*sizeof(struct report_rec) );
269
270
  /* initialize the new identifier */
271
0
  sri->reports = (struct report_rec*)(sri+1);
272
0
  sri->name.s = (char*)(sri->reports + max_reports);
273
0
  sri->name.len = identifier.len;
274
0
  memcpy( sri->name.s, identifier.s, identifier.len);
275
276
0
  sri->status = (init_status==0) ? 1: init_status;
277
0
  if (status_txt_s) {
278
0
    sri->status_txt.s = shm_malloc(status_txt_len);
279
0
    if (sri->status_txt.s) {
280
0
      memcpy( sri->status_txt.s, status_txt_s, status_txt_len);
281
0
      sri->status_txt.len = status_txt_len;
282
0
    } else
283
0
      sri->status_txt.len = 0;
284
0
  }
285
286
0
  if (max_reports==0)
287
0
    sri->reports = NULL;
288
0
  sri->max_reports = max_reports;
289
0
  sri->first_report = sri->last_report = -1;
290
291
0
  if (lock_init(&sri->lock)==NULL) {
292
0
    err_txt = "init identifier's lock";
293
0
    goto error;
294
0
  }
295
296
  /* add the new identifier to the group */
297
0
  sri->next = srg->identifiers;
298
0
  srg->identifiers = sri;
299
300
0
  lock_stop_write( sr_lock );
301
302
0
  return 0;
303
304
0
error:
305
0
  if (sri)
306
0
    shm_free(sri);
307
0
  lock_stop_write( sr_lock );
308
0
  LM_ERR("failed to %s when registering the [%.*s] identifier "
309
0
    "in [%.*s] group\n", err_txt, identifier.len, identifier.s,
310
0
    srg->name.len, srg->name.s);
311
0
  return -1;
312
0
}
313
314
315
/* Just a simple bundle of register_group and register_identifier for
316
 * cases where you need to register both in a single shot
317
 */
318
void* sr_register_group_with_identifier( char *group_s, int group_len,
319
    int grp_is_public,
320
    char *identifier_s, int identifier_len,
321
    int init_status, char *status_txt_s, int status_txt_len,
322
    int max_reports)
323
0
{
324
0
  void *srg;
325
326
0
  if ( (srg=sr_register_group( group_s, group_len, grp_is_public))<0 ) {
327
0
    LM_ERR("failed to register 'status_report' group [%.*s]\n",
328
0
      group_len, group_s);
329
0
    return NULL;
330
0
  }
331
0
  if (sr_register_identifier( srg, identifier_s, identifier_len,
332
0
  init_status, status_txt_s, status_txt_len, max_reports)<0 ) {
333
0
    LM_ERR("Failed to create 'core' group and identifier\n'\n");
334
0
    return NULL;
335
0
  }
336
337
0
  return srg;
338
0
}
339
340
341
/* Unregisters / removes an identifier from its group
342
 */
343
int sr_unregister_identifier( void *group,
344
    char *identifier_s, int identifier_len)
345
0
{
346
0
  sr_group *srg = (sr_group*)group;
347
0
  str identifier = {identifier_s, identifier_len};
348
0
  sr_identifier *sri, *prev_sri;
349
0
  int i;
350
351
0
  if (identifier.s==NULL)
352
0
    identifier = main_identifier;
353
354
  /* search for the identifier, but remember
355
   * the prev as group and identifier as we will have to unlink it */
356
0
  lock_start_write( sr_lock );
357
358
  /* locate the identifier */
359
0
  for (sri=srg->identifiers, prev_sri=NULL ; sri ;
360
0
  prev_sri=sri,sri=sri->next ) {
361
0
    if ( sri->name.len==identifier.len &&
362
0
    strncasecmp( sri->name.s, identifier.s, identifier.len)==0 ) {
363
0
      LM_DBG("identity [%.*s] found in group [%.*s] , removing...\n",
364
0
        sri->name.len, sri->name.s,
365
0
        srg->name.len, srg->name.s);
366
      /* we found the identifier, first remove it from the 
367
       * list of identifiers within the group */
368
0
      if (prev_sri)
369
0
        prev_sri->next = sri->next;
370
0
      else
371
0
        srg->identifiers = sri->next;
372
373
      /* purge the identity - it is safe to remove it as we have
374
       * the WRITE access to the lists, so no one else may have 
375
       * a referece to the identity */
376
0
      for( i=0 ; i<sri->max_reports ; i++ )
377
0
        if (sri->reports[i].log.s)
378
0
          shm_free(sri->reports[i].log.s);
379
0
      if (sri->status_txt.s)
380
0
        shm_free(sri->status_txt.s);
381
0
      shm_free( sri );
382
383
      /* done */
384
0
      lock_stop_write( sr_lock );
385
0
      return 0;
386
0
    }
387
0
  }
388
389
  /* not found */
390
0
  LM_BUG("asking to remove an identity which was not found\n");
391
0
  lock_stop_write( sr_lock );
392
0
  return -1;
393
0
}
394
395
/* event parameters */
396
static str evi_group_str = str_init("group");
397
static str evi_identifier_str = str_init("identifier");
398
static str evi_status_str = str_init("status");
399
static str evi_details_str = str_init("details");
400
static str evi_old_status_str = str_init("old_status");
401
402
403
/* Sets a new status for an existing identifier (within a group).
404
 * - The "group" must a pointer to a registered SR group, mandatory
405
 * - The "identifier" name is optional if this is a default (per entire group
406
 *    identifier).
407
 * - Optionally a status text/detail may be provided (set status_txt_s NULL
408
 *    to avoid using it)
409
 * - Set "is_public" if this operation was triggered from script or external
410
      sources (like MI). If the identity does not accept public ops, this
411
 *    operation will be forbidden.
412
 */
413
int sr_set_status( void *group,
414
    char *identifier_s, int identifier_len,
415
    int status, char *status_txt_s, int status_txt_len,
416
    int is_public)
417
0
{
418
0
  sr_group *srg = (sr_group*)group;
419
0
  str identifier = {identifier_s, identifier_len};
420
0
  sr_identifier *sri;
421
0
  evi_params_p list = NULL;
422
0
  int old_status;
423
0
  str s;
424
425
0
  if (identifier.s==NULL)
426
0
    identifier = main_identifier;
427
428
0
  if ( (is_public) ^ (srg->is_public) ) {
429
0
    LM_ERR("forbidden setting status for identifier [%.*s] group [%.*s]\n",
430
0
      identifier.len, identifier.s, srg->name.len, srg->name.s);
431
0
    return -1;
432
0
  }
433
434
0
  lock_start_read( sr_lock );
435
436
0
  sri = _get_identifier_by_name( srg, &identifier);
437
0
  if (sri==NULL) {
438
0
    lock_stop_read( sr_lock );
439
0
    LM_ERR("setting status for unknow identifier [%.*s] group [%.*s]\n",
440
0
      identifier.len, identifier.s, srg->name.len, srg->name.s);
441
0
    return -1;
442
0
  }
443
444
  /* we do no accept 0 status values, as we cannot return 0 to script */
445
0
  if (status==0)
446
0
    status = 1;
447
448
0
  lock_get( &sri->lock );
449
0
  old_status = sri->status;
450
0
  sri->status = status;
451
0
  if (sri->status_txt.s) shm_free(sri->status_txt.s);
452
0
  if (status_txt_s) {
453
0
    sri->status_txt.s = shm_malloc(status_txt_len);
454
0
    if (sri->status_txt.s) {
455
0
      memcpy( sri->status_txt.s, status_txt_s, status_txt_len);
456
0
      sri->status_txt.len = status_txt_len;
457
0
    } else
458
0
      sri->status_txt.len = 0;
459
0
  } else {
460
0
    sri->status_txt.s = NULL;
461
0
    sri->status_txt.len = 0;
462
0
  }
463
0
  lock_release( &sri->lock );
464
465
  /* raise event if status changed */
466
0
  if (old_status != status && evi_probe_event(sr_evi_id)) {
467
0
    if (!(list = evi_get_params()))
468
0
      goto done;
469
0
    if (evi_param_add_str( list, &evi_group_str, &srg->name)) {
470
0
      LM_ERR("unable to add group EVI parameter\n");
471
0
      evi_free_params(list);
472
0
      goto done;
473
0
    }
474
0
    if (evi_param_add_str( list, &evi_identifier_str, &sri->name)) {
475
0
      LM_ERR("unable to add identifier EVI parameter\n");
476
0
      evi_free_params(list);
477
0
      goto done;
478
0
    }
479
0
    if (evi_param_add_int( list, &evi_status_str, &status)) {
480
0
      LM_ERR("unable to add status EVI parameter\n");
481
0
      evi_free_params(list);
482
0
      goto done;
483
0
    }
484
0
    if (status_txt_s) {
485
0
      s.s = status_txt_s;
486
0
      s.len = status_txt_len;
487
0
      if (evi_param_add_str( list, &evi_details_str, &s)) {
488
0
        LM_ERR("unable to add identifier parameter\n");
489
0
        evi_free_params(list);
490
0
        goto done;
491
0
      }
492
0
    }
493
0
    if (evi_param_add_int( list, &evi_old_status_str, &old_status)) {
494
0
      LM_ERR("unable to add old_status EVI parameter\n");
495
0
      evi_free_params(list);
496
0
      goto done;
497
0
    }
498
499
0
    if (evi_raise_event( sr_evi_id, list)) {
500
0
      LM_ERR("unable to raise status_changed event\n");
501
0
    }
502
503
0
  }
504
505
0
  lock_stop_read( sr_lock );
506
507
0
done:
508
0
  return 0;
509
0
}
510
511
512
/* Adds a new report log to an identifier.
513
 * - The "group" must a pointer to a registered SR group, mandatory
514
 * - The "identifier" name is optional if this is the default, per entire group
515
 *    identifier.
516
 * - The "report" is mandatory too, it will be internally duplicated
517
 * - Set "is_public" if this operation was triggered from script or external
518
 *    sources (like MI). If the identity does not accept public ops, this
519
 *    operation will be forbidden.
520
 */
521
int sr_add_report(void *group,
522
    char *identifier_s, int identifier_len,
523
    char *report_s, int report_len,
524
    int is_public)
525
0
{
526
0
  sr_group *srg = (sr_group*)group;
527
0
  str identifier = {identifier_s, identifier_len};
528
0
  sr_identifier *sri;
529
0
  short idx;
530
0
  char *s;
531
532
0
  if (group==NULL || report_s==NULL) {
533
0
    LM_BUG("bogus call wtih group %p, report %p\n",
534
0
      group, report_s);
535
0
    return -1;
536
0
  }
537
538
0
  if (identifier.s==NULL)
539
0
    identifier = main_identifier;
540
541
0
  if ( (is_public) ^ (srg->is_public) ) {
542
0
    LM_ERR("forbidden adding report to identifier [%.*s] group [%.*s]\n",
543
0
      identifier.len, identifier.s, srg->name.len, srg->name.s);
544
0
    return -1;
545
0
  }
546
547
0
  lock_start_read( sr_lock );
548
549
0
  sri = _get_identifier_by_name( srg, &identifier);
550
0
  if (sri==NULL) {
551
0
    lock_stop_read( sr_lock );
552
0
    LM_ERR("adding report for unknow identifier [%.*s] group [%.*s]\n",
553
0
      identifier.len, identifier.s, srg->name.len, srg->name.s);
554
0
    return -1;
555
0
  }
556
557
0
  if (sri->max_reports==0)  {
558
0
    lock_stop_read( sr_lock );
559
0
    LM_ERR("identifier [%.*s] group [%.*s] does not accept reports\n",
560
0
      identifier.len, identifier.s, srg->name.len, srg->name.s);
561
0
    return -1;
562
0
  }
563
564
0
  s = shm_malloc( report_len );
565
0
  if (s==NULL) {
566
0
    LM_ERR("failed to sh malloc for cloning report\n");
567
0
    lock_stop_read( sr_lock );
568
0
    return -1;
569
0
  }
570
571
0
  lock_get( &sri->lock );
572
573
  /* compute the index where to add the new report */
574
0
  if (sri->first_report==-1 && sri->last_report==-1) {
575
0
    sri->first_report = 0;
576
0
    idx = 0;
577
0
  } else {
578
0
    idx = (sri->last_report+1) % sri->max_reports;
579
0
  }
580
581
0
  if (idx==sri->first_report && -1!=sri->last_report) {
582
    /* overflow, free the oldest report */
583
0
    shm_free( sri->reports[idx].log.s );
584
0
    sri->reports[idx].log.s = NULL;
585
0
    sri->reports[idx].log.len = 0;
586
0
    sri->first_report = (sri->first_report+1) % sri->max_reports;
587
0
  }
588
589
0
  sri->last_report = idx;
590
591
0
  LM_DBG("adding report to identifier [%.*s] group [%.*s] on idx %d "
592
0
    "[%d,%d]\n", identifier.len, identifier.s,
593
0
    srg->name.len, srg->name.s, idx, sri->first_report, sri->last_report);
594
595
  /* copy the report here */
596
0
  sri->reports[idx].log.s = s;
597
0
  memcpy( s, report_s, report_len);
598
0
  sri->reports[idx].log.len = report_len;
599
0
  sri->reports[idx].ts = time(NULL);
600
601
0
  lock_release( &sri->lock );
602
603
0
  lock_stop_read( sr_lock );
604
605
0
  return sri?0:-1;
606
0
}
607
608
609
int sr_add_report_fmt(void *group,
610
    char *identifier_s, int identifier_len,
611
    int is_public,
612
    char *fmt_val, ...)
613
0
{
614
0
  va_list ap;
615
0
  char *report_s;
616
0
  int report_len;
617
618
0
  va_start(ap, fmt_val);
619
0
  report_s = mi_print_fmt(fmt_val, ap, &report_len);
620
0
  va_end(ap);
621
0
  if (!report_s)
622
0
    return -1;
623
624
0
  return sr_add_report( group, identifier_s, identifier_len,
625
0
    report_s, report_len, is_public);
626
0
}
627
628
629
#define _add_mi_readiness( _mi_item, _status) \
630
0
  add_mi_bool(_mi_item, CHAR_INT("Readiness"), (_status<0)?0:1)
631
632
/* Checks the status of an identifier
633
 * NOTE1 : for internal usage only, it does its own locking
634
 */
635
static int _check_status(sr_group *srg, str *identifier, mi_item_t *id_item)
636
0
{
637
0
  sr_identifier *sri;
638
0
  int status;
639
640
0
  if (identifier==NULL) {
641
0
    identifier = &main_identifier;
642
0
  } else if (identifier->len==3 && strncasecmp(identifier->s, "all", 3)==0){
643
0
    identifier = (void*)-1;
644
0
  }
645
646
0
  lock_start_read( sr_lock );
647
648
0
  if ( identifier != (void*)-1 ) {
649
650
    /* only one identifier */
651
0
    sri = _get_identifier_by_name( srg, identifier);
652
0
    if (sri==NULL) {
653
0
      LM_DBG("identifier [%.*s] not found in group [%.*s]\n",
654
0
        identifier->len, identifier->s,
655
0
        srg->name.len, srg->name.s);
656
0
      status = SR_STATUS_NOT_FOUND;
657
0
    } else {
658
0
      lock_get( &sri->lock );
659
0
      status = sri->status ;
660
0
      if (id_item) {
661
0
        _add_mi_readiness( id_item, status);
662
0
        add_mi_number( id_item, CHAR_INT("Status"), status);
663
0
        if (sri->status_txt.s)
664
0
          add_mi_string(id_item, CHAR_INT("Details"),
665
0
            sri->status_txt.s, sri->status_txt.len);
666
0
      }
667
0
      lock_release( &sri->lock );
668
0
    }
669
670
0
  } else {
671
672
    /* aggregate the status of all indetifiers in the group */
673
0
    status = SR_STATUS_READY;
674
0
    for( sri=srg->identifiers ; sri ; sri=sri->next) {
675
0
      lock_get( &sri->lock );
676
0
      if (sri->status < 0)
677
0
        status = SR_STATUS_NOT_READY;
678
0
      lock_release( &sri->lock );
679
0
    }
680
681
0
    if (id_item) {
682
0
      _add_mi_readiness( id_item, status);
683
0
      add_mi_number( id_item, CHAR_INT("Status"), status);
684
0
      add_mi_string(id_item, CHAR_INT("Details"),
685
0
        CHAR_INT("aggregated"));
686
0
    }
687
688
0
  }
689
690
0
  lock_stop_read( sr_lock );
691
692
0
  return status;
693
0
}
694
695
696
/****************** SR status of the OpenSIPS core  **************/
697
698
int sr_set_core_status(enum sr_core_states status, char *txt_s, int txt_len)
699
0
{
700
0
  if (status == STATE_RUNNING) {
701
0
    ready_time = time(NULL);
702
0
    ready_delay = ready_time - startup_time;
703
0
  }
704
705
0
  return sr_set_status( srg_core, CHAR_INT_NULL /*main*/, status,
706
0
    txt_s, txt_len, 0);
707
0
}
708
709
710
/* light version of set_core_status, doing no shm, locking or event
711
 * operations - such ops are potentially dangerous during shutdonw */
712
void sr_set_core_status_terminating( void )
713
0
{
714
0
  sri_core->status = STATE_TERMINATING;
715
  /* note: the below assigment will produce a small mem leak in shm
716
   * (for the previously allocated status), but we do not really care
717
   * as we do this only once, at shutdown */
718
0
  sri_core->status_txt.s = "shutting down";
719
0
  sri_core->status_txt.len = 13;
720
0
}
721
722
723
enum sr_core_states sr_get_core_status(void)
724
0
{
725
0
  return sri_core->status;
726
0
}
727
728
729
int sr_add_core_report(char *report_s, int report_len)
730
0
{
731
0
  return sr_add_report( srg_core, CHAR_INT_NULL /*main*/,
732
0
    report_s, report_len, 0);
733
0
}
734
735
/****************** Core (to be used) functions  **********************/
736
737
int init_status_report(void)
738
0
{
739
0
  sr_lock = lock_init_rw();
740
0
  if (sr_lock==NULL) {
741
0
    LM_ERR("Failed to create the global RW lock, abording\n");
742
0
    return -1;
743
0
  }
744
745
0
  sr_evi_id = evi_publish_event(sr_event);
746
0
  if (sr_evi_id == EVI_ERROR) {
747
0
    LM_ERR("cannot register 'statis_changed' event\n");
748
0
    return -1;
749
0
  }
750
751
0
  srg_core = (sr_group*)sr_register_group_with_identifier(
752
0
    CHAR_INT("core"), 0/*not public*/, CHAR_INT_NULL /*main*/,
753
0
    STATE_NONE, CHAR_INT_NULL/*report*/, 10);
754
0
  if (srg_core==NULL) {
755
0
    LM_ERR("Failed to register 'status_report' group and identifier for "
756
0
      "'core'\n");
757
0
    return -1;
758
0
  }
759
760
  /* this is a bit hackish, but 100% safe as time as it is done right
761
   * after the above sr_register_group_with_identifier() - the "main"
762
   * identifier will be the only one in the group, so the first one too */
763
0
  sri_core = (srg_core)->identifiers;
764
  /* also it is safe to keep a reference here as we will never delete 
765
   * this identifer - but be CAREFULL on this !!! */
766
767
0
  return 0;
768
0
}
769
770
771
/****************** Core Scripting functions  **********************/
772
773
int fixup_sr_group(void **param)
774
0
{
775
0
  str *name = (str*)*param;
776
0
  sr_group *srg = NULL;
777
778
0
  if ( (srg=sr_get_group_by_name( name->s, name->len )) == NULL ) {
779
0
    LM_ERR("SR group [%.*s] not registered\n",
780
0
      name->len, name->s);
781
0
    return -1;
782
0
  }
783
0
  *param = (void*)srg;
784
785
0
  return 0;
786
0
}
787
788
789
/* Checks the status of an identifier
790
 * - The "msg" is useless, just for compatibility with script functions
791
 * - The "group" must a pointer to a registered SR group, mandatory
792
 * - The "identifier" may be: (1) name of an identifier, (2) NULL, to refer to
793
 *    the default (per group) identifier or (3) "all" to refer to all the
794
 *    identifiers in the group
795
 * Returns the status (as non zero value) of the requested identifier(s). If
796
 *  multiple indetifiers are to be checked - case (3) - the returned 
797
 *  status is an aggregated one as 1 or -1 (over all identifiers).
798
 */
799
int w_sr_check_status(struct sip_msg *msg, void *group, str *identifier)
800
0
{
801
0
  return _check_status((sr_group *)group, identifier, NULL);
802
0
}
803
804
805
/****************** Core MI  functions  **********************/
806
807
mi_response_t *mi_sr_get_status(const mi_params_t *params,
808
                      struct mi_handler *async_hdl)
809
0
{
810
0
  str group;
811
0
  str identifier;
812
0
  int status;
813
0
  mi_response_t *resp;
814
0
  mi_item_t *resp_obj;
815
0
  sr_group *srg = NULL;
816
817
0
  if (get_mi_string_param(params, "group", &group.s, &group.len) < 0)
818
0
    return init_mi_param_error();
819
820
0
  if ( (srg=sr_get_group_by_name( group.s, group.len )) == NULL ) {
821
0
    LM_DBG("SR group [%.*s] not found as registered\n",
822
0
      group.len, group.s);
823
0
    return init_mi_error(404, CHAR_INT("Group not found"));
824
0
  }
825
826
0
  resp = init_mi_result_object(&resp_obj);
827
0
  if (!resp)
828
0
    return 0;
829
830
0
  if (try_get_mi_string_param(params, "identifier",
831
0
  &identifier.s, &identifier.len)!=0) {
832
    /* no identifier passed */
833
0
    status = _check_status( srg, NULL, resp_obj);
834
0
  } else {
835
0
    status = _check_status( srg, &identifier, resp_obj);
836
0
  }
837
838
0
  if (status == SR_STATUS_NOT_FOUND) {
839
0
    free_mi_response(resp);
840
0
    return init_mi_error(404, CHAR_INT("Identity not found"));
841
0
  }
842
843
0
  return resp;
844
0
}
845
846
847
static int _mi_list_status_group(sr_group *srg, mi_item_t *id_arr)
848
0
{
849
0
  sr_identifier *sri;
850
0
  mi_item_t *id_item;
851
852
0
  for ( sri=srg->identifiers ; sri ; sri=sri->next ) {
853
854
0
    id_item = add_mi_object(id_arr, 0, 0);
855
0
    if (!id_item)
856
0
      return -1;
857
858
0
    lock_get( &sri->lock );
859
860
0
    if (add_mi_string(id_item, CHAR_INT("Name"),
861
0
    sri->name.s, sri->name.len ) < 0 ) {
862
0
      lock_release( &sri->lock );
863
0
      return -1;
864
0
    }
865
866
0
    if (_add_mi_readiness( id_item, sri->status)<0 ) {
867
0
      lock_release( &sri->lock );
868
0
      return -1;
869
0
    }
870
871
0
    if (add_mi_number( id_item, CHAR_INT("Status"), sri->status)<0) {
872
0
      lock_release( &sri->lock );
873
0
      return -1;
874
0
    }
875
876
0
    if (sri->status_txt.s && add_mi_string(id_item, CHAR_INT("Details"),
877
0
    sri->status_txt.s, sri->status_txt.len) < 0) {
878
0
      lock_release( &sri->lock );
879
0
      return -1;
880
0
    }
881
882
0
    lock_release( &sri->lock );
883
884
0
  }
885
886
0
  return 0;
887
0
}
888
889
890
mi_response_t *mi_sr_list_status(const mi_params_t *params,
891
                      struct mi_handler *async_hdl)
892
0
{
893
0
  str group;
894
0
  mi_response_t *resp;
895
0
  mi_item_t *grp_arr, *grp_item, *id_arr;
896
0
  sr_group *srg = NULL;
897
898
0
  if (try_get_mi_string_param(params, "group", &group.s, &group.len)!=0) {
899
    /* no group passed */
900
0
    srg = NULL;
901
0
  } else {
902
0
    if ( (srg=sr_get_group_by_name( group.s, group.len )) == NULL ) {
903
0
      LM_DBG("SR group [%.*s] not found as registered\n",
904
0
        group.len, group.s);
905
0
      return init_mi_error(404, CHAR_INT("Group not found"));
906
0
    }
907
0
  }
908
909
0
  lock_start_read( sr_lock );
910
911
  /* list the readiness of all identifiers from the group(s) */
912
0
  if (srg) {
913
914
0
    resp = init_mi_result_array( &id_arr);
915
0
    if (!resp)
916
0
      goto error;
917
918
0
    if (_mi_list_status_group( srg, id_arr)<0) {
919
0
      LM_ERR("failed to inser group, mem failure\n");
920
0
      goto error;
921
0
    }
922
923
0
  } else {
924
925
0
    resp = init_mi_result_array( &grp_arr);
926
0
    if (!resp)
927
0
      goto error;
928
929
0
    for ( srg=sr_groups ; srg ; srg=srg->next ) {
930
      
931
0
      grp_item = add_mi_object( grp_arr, 0, 0);
932
0
      if (!grp_item)
933
0
        goto error;
934
935
0
      if (add_mi_string( grp_item, CHAR_INT("Name"),
936
0
      srg->name.s, srg->name.len)<0)
937
0
        goto error;
938
939
0
      id_arr = add_mi_array( grp_item, CHAR_INT("Identifiers"));
940
0
      if (!id_arr)
941
0
        goto error;
942
943
0
      if (_mi_list_status_group( srg, id_arr)<0) {
944
0
        LM_ERR("failed to inser group, mem failure\n");
945
0
        goto error;
946
0
      }
947
0
    }
948
949
0
  }
950
951
0
  lock_stop_read( sr_lock );
952
953
0
  return resp;
954
0
error:
955
0
  lock_stop_read( sr_lock );
956
0
  free_mi_response(resp);
957
0
  return 0;
958
959
0
}
960
961
962
static int _mi_list_reports(sr_identifier *sri, mi_item_t *log_arr)
963
0
{
964
0
  mi_item_t *log_item;
965
0
  short i, cnt;
966
0
  str date;
967
968
0
  lock_get( &sri->lock );
969
970
0
  if (sri->first_report==-1 || sri->max_reports==0) {
971
    /* no reports at all */
972
0
    lock_release( &sri->lock );
973
0
    return 0;
974
0
  }
975
976
977
0
  cnt = sri->last_report - sri->first_report + 1;
978
0
  if (cnt<=0)
979
0
    cnt += sri->max_reports;
980
981
0
  LM_DBG("idxes: first=%d, last=%d, cnt=%d\n",
982
0
    sri->first_report,sri->last_report,cnt);
983
984
0
  for ( i=sri->first_report ; cnt ; i=(i+1)%sri->max_reports,cnt-- ) {
985
986
0
    log_item = add_mi_object( log_arr, 0, 0);
987
0
    if (log_item==NULL)
988
0
      goto error;
989
990
0
    if (add_mi_number( log_item, CHAR_INT("Timestamp"), sri->reports[i].ts) < 0)
991
0
      goto error;
992
993
0
    date.s = ctime( &sri->reports[i].ts );
994
0
    date.len = strlen(date.s) - 1 /* get rid of the trailing \n */;
995
0
    if (add_mi_string( log_item, CHAR_INT("Date"), date.s, date.len) < 0)
996
0
      goto error;
997
998
0
    if (add_mi_string( log_item, CHAR_INT("Log"),
999
0
    sri->reports[i].log.s, sri->reports[i].log.len ) < 0)
1000
0
      goto error;
1001
0
  }
1002
1003
0
  lock_release( &sri->lock );
1004
0
  return 0;
1005
1006
0
error:
1007
0
  lock_release( &sri->lock );
1008
0
  return -1;
1009
0
}
1010
1011
1012
static int _mi_list_reports_group(sr_group *srg, mi_item_t *id_arr)
1013
0
{
1014
0
  sr_identifier *sri;
1015
0
  mi_item_t *id_item, *log_arr;
1016
1017
0
  for ( sri=srg->identifiers ; sri ; sri=sri->next ) {
1018
1019
0
    id_item = add_mi_object( id_arr, 0, 0);
1020
0
    if (!id_item)
1021
0
      return -1;
1022
1023
0
    if (add_mi_string( id_item, CHAR_INT("Name"),
1024
0
    sri->name.s, sri->name.len ) < 0 )
1025
0
      return -1;
1026
1027
0
    log_arr = add_mi_array( id_item, CHAR_INT("Reports"));
1028
0
    if (log_arr==NULL)
1029
0
      return -1;
1030
1031
0
    if ( _mi_list_reports( sri, log_arr)!=0 )
1032
0
      return -1;
1033
1034
0
  }
1035
1036
0
  return 0;
1037
0
}
1038
1039
1040
mi_response_t *mi_sr_list_reports(const mi_params_t *params,
1041
                      struct mi_handler *async_hdl)
1042
0
{
1043
0
  str group, identifier;
1044
0
  mi_response_t *resp;
1045
0
  mi_item_t *log_arr, *id_arr, *grp_arr, *grp_item;
1046
0
  sr_group *srg = NULL;
1047
0
  sr_identifier *sri = NULL;
1048
1049
0
  if (try_get_mi_string_param(params, "group", &group.s, &group.len)==0) {
1050
1051
    /* group provide */
1052
0
    if ( (srg=sr_get_group_by_name( group.s, group.len )) == NULL ) {
1053
0
      LM_DBG("SR group [%.*s] not found as registered\n",
1054
0
        group.len, group.s);
1055
0
      return init_mi_error(404, CHAR_INT("Group not found"));
1056
0
    }
1057
1058
0
    lock_start_read( sr_lock );
1059
1060
0
    if (try_get_mi_string_param(params, "identifier",
1061
0
    &identifier.s, &identifier.len)==0) {
1062
0
      if ( (sri=_get_identifier_by_name( srg, &identifier )) == NULL ) {
1063
0
        lock_stop_read( sr_lock );
1064
0
        LM_DBG("SR identifier [%.*s] group [%.*s] not found as "
1065
0
          "registered\n", identifier.len, identifier.s,
1066
0
          group.len, group.s);
1067
0
        return init_mi_error(404, CHAR_INT("Identifier not found"));
1068
0
      }
1069
1070
0
    }
1071
1072
0
  } else {
1073
1074
0
    lock_start_read( sr_lock );
1075
1076
0
  }
1077
1078
0
  if (sri) {
1079
1080
0
    resp = init_mi_result_array( &log_arr);
1081
0
    if (!log_arr)
1082
0
      goto error;
1083
1084
0
    if (_mi_list_reports( sri, log_arr)<0) {
1085
0
      LM_ERR("failed to inser identity, mem failure\n");
1086
0
      goto error;
1087
0
    }
1088
1089
0
  } else if (srg) {
1090
1091
0
    resp = init_mi_result_array( &id_arr);
1092
0
    if (!id_arr)
1093
0
      goto error;
1094
1095
0
    if (_mi_list_reports_group( srg, id_arr)<0) {
1096
0
      LM_ERR("failed to inser group, mem failure\n");
1097
0
      goto error;
1098
0
    }
1099
1100
0
  } else {
1101
1102
0
    resp = init_mi_result_array( &grp_arr);
1103
0
    if (!grp_arr)
1104
0
      goto error;
1105
1106
0
    for ( srg=sr_groups ; srg ; srg=srg->next ) {
1107
1108
0
      grp_item = add_mi_object(grp_arr, 0, 0);
1109
0
      if (!grp_item)
1110
0
        goto error;
1111
1112
0
      if (add_mi_string( grp_item, CHAR_INT("Name"),
1113
0
      srg->name.s, srg->name.len)<0)
1114
0
        goto error;
1115
1116
0
      id_arr = add_mi_array(grp_item, CHAR_INT("Identifiers"));
1117
0
      if (!id_arr)
1118
0
        goto error;
1119
1120
0
      if (_mi_list_reports_group( srg, id_arr)<0) {
1121
0
        LM_ERR("failed to inser group, mem failure\n");
1122
0
        goto error;
1123
0
      }
1124
0
    }
1125
0
  }
1126
1127
0
  lock_stop_read( sr_lock );
1128
1129
0
  return resp;
1130
0
error:
1131
0
  lock_stop_read( sr_lock );
1132
0
  if (resp)
1133
0
    free_mi_response(resp);
1134
0
  return 0;
1135
0
}
1136
1137
1138
mi_response_t *mi_sr_list_identifiers(const mi_params_t *params,
1139
                      struct mi_handler *async_hdl)
1140
0
{
1141
0
  str group;
1142
0
  mi_response_t *resp;
1143
0
  mi_item_t *id_arr, *grp_arr, *grp_item;
1144
0
  sr_group *srg = NULL;
1145
0
  sr_identifier *sri = NULL;
1146
1147
0
  if (try_get_mi_string_param(params, "group", &group.s, &group.len)==0) {
1148
1149
    /* group provide */
1150
0
    if ( (srg=sr_get_group_by_name( group.s, group.len )) == NULL ) {
1151
0
      LM_DBG("SR group [%.*s] not found as registered\n",
1152
0
        group.len, group.s);
1153
0
      return init_mi_error(404, CHAR_INT("Group not found"));
1154
0
    }
1155
1156
0
  }
1157
1158
0
  lock_start_read( sr_lock );
1159
1160
0
  if (srg) {
1161
1162
0
    resp = init_mi_result_object( &grp_item);
1163
0
    if (!grp_item)
1164
0
      goto error;
1165
1166
0
    if (add_mi_string( grp_item, CHAR_INT("Group"),
1167
0
    srg->name.s, srg->name.len)<0)
1168
0
      goto error;
1169
1170
0
    id_arr = add_mi_array( grp_item,  CHAR_INT("Identifiers"));
1171
0
    if (!id_arr)
1172
0
      goto error;
1173
1174
0
    for ( sri=srg->identifiers ; sri ; sri=sri->next ) {
1175
0
      if (add_mi_string( id_arr, CHAR_INT("Name"),
1176
0
      sri->name.s, sri->name.len ) < 0 )
1177
0
        goto error;
1178
0
    }
1179
1180
0
  } else {
1181
1182
0
    resp = init_mi_result_array( &grp_arr);
1183
0
    if (!grp_arr)
1184
0
      goto error;
1185
1186
0
    for ( srg=sr_groups ; srg ; srg=srg->next ) {
1187
1188
0
      grp_item = add_mi_object(grp_arr, 0, 0);
1189
0
      if (!grp_item)
1190
0
        goto error;
1191
1192
0
      if (add_mi_string( grp_item, CHAR_INT("Group"),
1193
0
      srg->name.s, srg->name.len)<0)
1194
0
        goto error;
1195
1196
0
      id_arr = add_mi_array( grp_item,  CHAR_INT("Identifiers"));
1197
0
      if (!id_arr)
1198
0
        goto error;
1199
1200
0
      for ( sri=srg->identifiers ; sri ; sri=sri->next ) {
1201
0
        if (add_mi_string( id_arr, CHAR_INT("Name"),
1202
0
        sri->name.s, sri->name.len ) < 0 )
1203
0
          goto error;
1204
0
      }
1205
1206
0
    }
1207
0
  }
1208
1209
0
  lock_stop_read( sr_lock );
1210
1211
0
  return resp;
1212
0
error:
1213
0
  lock_stop_read( sr_lock );
1214
0
  if (resp)
1215
0
    free_mi_response(resp);
1216
0
  return 0;
1217
1218
0
}