Asterisk - The Open Source Telephony Project GIT-master-590b490
Loading...
Searching...
No Matches
res_pjsip_maintenance.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2026, Aurora Innovation
5 *
6 * Daniel Donoghue
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*!
20 * \file
21 * \brief PJSIP Endpoint Maintenance Mode
22 *
23 * Provides a runtime toggle to place individual PJSIP endpoints into
24 * maintenance mode. While an endpoint is in maintenance mode:
25 *
26 * - New \b inbound out-of-dialog requests are rejected with
27 * "503 Service Unavailable" and a Retry-After: 300 header
28 * (except SUBSCRIBE/REGISTER with Expires: 0).
29 * - \b Outbound originations (Dial, ARI originate) are refused before
30 * any SIP session or Asterisk channel is created.
31 * - Active in-progress dialogs (BYE, re-INVITE, UPDATE, etc.) are
32 * completely unaffected.
33 * - Existing presence/BLF subscriptions are left to expire naturally.
34 *
35 * CLI:
36 * pjsip set maintenance <on|off> <endpoint|all>
37 * pjsip show maintenance [endpoint]
38 *
39 * AMI actions: PJSIPSetMaintenance, PJSIPShowMaintenance
40 *
41 * \ingroup res_pjsip
42 */
43
44/*** MODULEINFO
45 <depend>pjproject</depend>
46 <depend>res_pjsip</depend>
47 <depend>res_pjsip_session</depend>
48 <support_level>extended</support_level>
49 ***/
50
51/*** DOCUMENTATION
52 <manager name="PJSIPSetMaintenance" language="en_US">
53 <since>
54 <version>20.20.0</version>
55 <version>22.10.0</version>
56 <version>23.4.0</version>
57 </since>
58 <synopsis>
59 Enable or disable maintenance mode for a PJSIP endpoint.
60 </synopsis>
61 <syntax>
62 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
63 <parameter name="Endpoint" required="true">
64 <para>The PJSIP endpoint name, or <literal>all</literal> to
65 toggle maintenance mode for every configured endpoint.</para>
66 </parameter>
67 <parameter name="State" required="true">
68 <para>Desired maintenance state.</para>
69 <enumlist>
70 <enum name="on" />
71 <enum name="off" />
72 </enumlist>
73 </parameter>
74 </syntax>
75 <description>
76 <para>Enables or disables maintenance mode for the specified PJSIP
77 endpoint. While in maintenance mode, new inbound out-of-dialog
78 requests are rejected with 503 Service Unavailable (except
79 SUBSCRIBE/REGISTER with Expires: 0), and outbound originations via
80 Dial() or ARI are refused before any SIP session or channel is
81 created. In-progress dialogs are unaffected.</para>
82 <para>A <literal>PJSIPMaintenanceStatus</literal> event is emitted
83 when the state changes.</para>
84 </description>
85 </manager>
86 <manager name="PJSIPShowMaintenance" language="en_US">
87 <since>
88 <version>20.20.0</version>
89 <version>22.10.0</version>
90 <version>23.4.0</version>
91 </since>
92 <synopsis>
93 Show maintenance mode status for PJSIP endpoints.
94 </synopsis>
95 <syntax>
96 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
97 <parameter name="Endpoint" required="false">
98 <para>If specified, show the status for this endpoint only.
99 If omitted, list all endpoints currently in maintenance
100 mode.</para>
101 </parameter>
102 </syntax>
103 <description>
104 <para>Emits one <literal>PJSIPMaintenanceStatus</literal> event
105 per result, followed by a
106 <literal>PJSIPMaintenanceStatusComplete</literal> event.</para>
107 </description>
108 </manager>
109 <managerEvent language="en_US" name="PJSIPMaintenanceStatus">
110 <managerEventInstance class="EVENT_FLAG_SYSTEM">
111 <since>
112 <version>20.20.0</version>
113 <version>22.10.0</version>
114 <version>23.4.0</version>
115 </since>
116 <synopsis>
117 Reports the maintenance mode state of a PJSIP endpoint.
118 </synopsis>
119 <syntax>
120 <parameter name="Endpoint">
121 <para>The PJSIP endpoint name.</para>
122 </parameter>
123 <parameter name="Status">
124 <para>Current maintenance state.</para>
125 <enumlist>
126 <enum name="enabled" />
127 <enum name="disabled" />
128 </enumlist>
129 </parameter>
130 </syntax>
131 <description>
132 <para>Emitted when an endpoint enters or leaves maintenance
133 mode, and as a list entry in response to
134 <literal>PJSIPShowMaintenance</literal>.</para>
135 </description>
136 </managerEventInstance>
137 </managerEvent>
138 ***/
139
140#include "asterisk.h"
141
142#include <pjsip.h>
143
144#include "asterisk/res_pjsip.h"
146#include "asterisk/manager.h"
147#include "asterisk/module.h"
148#include "asterisk/logger.h"
149#include "asterisk/cli.h"
150#include "asterisk/sorcery.h"
151#include "asterisk/astobj2.h"
152#include "asterisk/strings.h"
153
154enum {
156};
157
158/*! Endpoints currently in maintenance mode.
159 * Protected by the container's own internal RWLOCK.
160 * No other locks are ever held simultaneously with this container.
161 */
163
164/*!
165 * \internal
166 * \brief Add an endpoint to the maintenance set.
167 * \retval 1 Added successfully.
168 * \retval 0 Already in maintenance (no-op).
169 * \retval -1 Allocation failure.
170 */
171static int maint_set_add(const char *endpoint_name)
172{
173 char *entry;
174
175 entry = ao2_find(maintenance_set, endpoint_name, OBJ_SEARCH_KEY);
176 if (entry) {
177 ao2_ref(entry, -1);
178 return 0; /* already in maintenance */
179 }
180 return ast_str_container_add(maintenance_set, endpoint_name) ? -1 : 1;
181}
182
183/*!
184 * \internal
185 * \brief Remove an endpoint from the maintenance set.
186 * \retval 1 Removed successfully.
187 * \retval 0 Was not in maintenance (no-op).
188 */
189static int maint_set_remove(const char *endpoint_name)
190{
191 char *entry;
192
193 entry = ao2_find(maintenance_set, endpoint_name, OBJ_SEARCH_KEY | OBJ_UNLINK);
194 if (!entry) {
195 return 0;
196 }
197 ao2_ref(entry, -1);
198 return 1;
199}
200
201/*!
202 * \internal
203 * \brief Apply a maintenance state change to the maintenance set.
204 *
205 * Does not validate endpoint existence; callers are responsible for that.
206 * Callers are also responsible for emitting log messages and AMI events.
207 *
208 * \retval 1 State changed.
209 * \retval 0 Already in requested state (no-op).
210 * \retval -1 Allocation failure (enable path only).
211 */
212static int apply_maintenance_state(const char *endpoint_name, int enable)
213{
214 return enable ? maint_set_add(endpoint_name) : maint_set_remove(endpoint_name);
215}
216
217/* Session supplement: block outgoing session creation when endpoint is in maintenance. */
218
219/*!
220 * \internal
221 * \brief Session supplement session_create callback: block outgoing sessions to
222 * endpoints currently in maintenance mode.
223 * \retval 1 Endpoint is in maintenance; session creation blocked.
224 * \retval 0 Endpoint is not in maintenance; session creation allowed.
225 */
226static int maint_session_create(struct ast_sip_endpoint *endpoint,
227 struct ast_sip_contact *contact, const char *location,
228 const char *request_user, struct ast_stream_topology *req_topology)
229{
230 const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
231 char *entry = ao2_find(maintenance_set, endpoint_name, OBJ_SEARCH_KEY);
232
233 if (entry) {
234 ao2_ref(entry, -1);
235 ast_log(LOG_NOTICE, "PJSIP: Refusing outbound call to endpoint '%s': maintenance mode active\n",
236 endpoint_name);
237 return 1;
238 }
239 return 0;
240}
241
246
247/* Inbound request hook for maintenance_pjsip_mod.
248 *
249 * For endpoints in maintenance mode, blocks new out-of-dialog requests
250 * with 503 + Retry-After: 300. Any in-dialog request is passed through
251 * unmodified. SUBSCRIBE and REGISTER with Expires: 0 are also passed
252 * through, allowing un-subscribe and de-registration. */
253
254static pj_bool_t maintenance_on_rx_request(pjsip_rx_data *rdata)
255{
256 pjsip_msg *msg = rdata->msg_info.msg;
257 const pjsip_method *method = &msg->line.req.method;
258 pjsip_to_hdr *to;
259 pjsip_expires_hdr *expires_hdr;
260 struct ast_sip_endpoint *endpoint;
261 char *entry;
262 pjsip_hdr hdr_list;
263 pjsip_generic_int_hdr *retry_after;
264 static const pj_str_t str_retry_after = { "Retry-After", 11 };
265 int is_subscribe;
266 int is_register;
267
268 is_subscribe = pjsip_method_cmp(method, pjsip_get_subscribe_method()) == 0;
269 is_register = pjsip_method_cmp(method, pjsip_get_register_method()) == 0;
270
271 /* Any in-dialog request is always allowed through. */
272 to = rdata->msg_info.to;
273 if (to->tag.slen > 0) {
274 return PJ_FALSE;
275 }
276
277 /* SUBSCRIBE or REGISTER with Expires: 0: allow un-subscribe / de-register. */
278 if (is_subscribe || is_register) {
279 expires_hdr = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
280 if (expires_hdr && expires_hdr->ivalue == 0) {
281 return PJ_FALSE;
282 }
283 }
284
285 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
286 if (!endpoint) {
287 return PJ_FALSE;
288 }
289
291 if (!entry) {
292 ao2_ref(endpoint, -1);
293 return PJ_FALSE;
294 }
295 ao2_ref(entry, -1);
296
297 ast_log(LOG_NOTICE, "PJSIP: Endpoint '%s' is in maintenance mode; rejecting new %.*s from %s\n",
299 (int)method->name.slen, method->name.ptr,
300 rdata->pkt_info.src_name);
301
302 ao2_ref(endpoint, -1);
303
304 pj_list_init(&hdr_list);
305 retry_after = pjsip_generic_int_hdr_create(rdata->tp_info.pool,
306 &str_retry_after, 300);
307 if (retry_after) {
308 pj_list_push_back(&hdr_list, retry_after);
309 }
310
311 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL,
312 retry_after ? &hdr_list : NULL, NULL);
313
314 return PJ_TRUE;
315}
316
317static struct pjsip_module maintenance_pjsip_mod = {
318 .name = { "Maintenance Module", 18 },
319 /*
320 * Run after endpoint identification (endpoint_mod,
321 * PJSIP_MOD_PRIORITY_TSX_LAYER - 3) so that
322 * ast_pjsip_rdata_get_endpoint() returns the identified endpoint,
323 * but before the request authenticator
324 * (PJSIP_MOD_PRIORITY_APPLICATION - 2) so that a maintenance
325 * endpoint receives 503 rather than a 401 challenge.
326 */
327 .priority = PJSIP_MOD_PRIORITY_APPLICATION - 3,
328 .on_rx_request = maintenance_on_rx_request,
329};
330
331/* Sorcery observer - clean up stale entries when an endpoint is deleted. */
332
333static void maint_endpoint_deleted(const void *obj)
334{
336}
337
341
342/* CLI helpers */
343
344/*!
345 * \internal
346 * \brief Tab-complete a PJSIP endpoint name.
347 */
348static char *cli_complete_endpoint(const char *word)
349{
350 int wordlen = strlen(word);
351 struct ao2_container *endpoints;
352 struct ast_sip_endpoint *endpoint;
353 struct ao2_iterator i;
354
356 "endpoint", word, wordlen);
357 if (!endpoints) {
358 return NULL;
359 }
360
362 while ((endpoint = ao2_iterator_next(&i))) {
364 ao2_cleanup(endpoint);
365 }
367 ao2_ref(endpoints, -1);
368
369 return NULL;
370}
371
372/* CLI: pjsip set maintenance <on|off> <endpoint|all> */
373
374static char *handle_cli_pjsip_set_maintenance(struct ast_cli_entry *e, int cmd,
375 struct ast_cli_args *a)
376{
377 struct ast_sip_endpoint *endpoint;
378 struct ao2_container *all_endpoints;
379 struct ao2_iterator it;
380 const char *endpoint_name;
381 int enable;
382 int rc;
383 int count;
384 int failed;
385
386 switch (cmd) {
387 case CLI_INIT:
388 e->command = "pjsip set maintenance";
389 e->usage =
390 "Usage: pjsip set maintenance <on|off> <endpoint|all>\n"
391 " Place a PJSIP endpoint into or out of maintenance mode.\n"
392 " Use 'all' to toggle maintenance mode for every endpoint.\n"
393 " While in maintenance mode new inbound out-of-dialog requests\n"
394 " to that endpoint are rejected with 503 (except SUBSCRIBE/\n"
395 " REGISTER with Expires: 0), and outbound originations are\n"
396 " refused.\n";
397 return NULL;
398 case CLI_GENERATE:
399 if (a->pos == 3) {
400 static const char * const opts[] = { "on", "off", NULL };
401 return ast_cli_complete(a->word, opts, a->n);
402 }
403 if (a->pos == 4) {
404 if (!strncasecmp("all", a->word, strlen(a->word))) {
406 }
407 return cli_complete_endpoint(a->word);
408 }
409 return NULL;
410 }
411
412 if (a->argc != 5) {
413 return CLI_SHOWUSAGE;
414 }
415
416 if (!strcasecmp(a->argv[3], "on")) {
417 enable = 1;
418 } else if (!strcasecmp(a->argv[3], "off")) {
419 enable = 0;
420 } else {
421 return CLI_SHOWUSAGE;
422 }
423
424 endpoint_name = a->argv[4];
425
426 if (!strcasecmp(endpoint_name, "all")) {
427 all_endpoints = ast_sip_get_endpoints();
428 if (!all_endpoints) {
429 ast_cli(a->fd, "Failed to retrieve endpoint list\n");
430 return CLI_SUCCESS;
431 }
432 count = 0;
433 failed = 0;
434 it = ao2_iterator_init(all_endpoints, 0);
435 while ((endpoint = ao2_iterator_next(&it))) {
437 if (rc > 0) {
438 count++;
439 } else if (rc < 0) {
440 failed++;
441 }
442 ao2_ref(endpoint, -1);
443 }
445 ao2_ref(all_endpoints, -1);
446 if (count > 0) {
447 manager_event(EVENT_FLAG_SYSTEM, "PJSIPMaintenanceStatus",
448 "Endpoint: all\r\n"
449 "Status: %s\r\n",
450 enable ? "enabled" : "disabled");
451 ast_log(LOG_NOTICE, "PJSIP: Maintenance mode %s for all endpoints "
452 "(%d endpoint%s affected)\n",
453 enable ? "enabled" : "disabled",
454 count, count == 1 ? "" : "s");
455 }
456 ast_cli(a->fd, "Maintenance mode %s for %d endpoint%s%s\n",
457 enable ? "ENABLED" : "DISABLED",
458 count, count == 1 ? "" : "s",
459 failed ? " (some failed)" : "");
460 return CLI_SUCCESS;
461 }
462
463 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);
464 if (!endpoint) {
465 ast_cli(a->fd, "Endpoint '%s' not found\n", endpoint_name);
466 return CLI_SUCCESS;
467 }
468 ao2_ref(endpoint, -1);
469
470 rc = apply_maintenance_state(endpoint_name, enable);
471 if (rc > 0) {
472 manager_event(EVENT_FLAG_SYSTEM, "PJSIPMaintenanceStatus",
473 "Endpoint: %s\r\n"
474 "Status: %s\r\n",
475 endpoint_name, enable ? "enabled" : "disabled");
476 ast_log(LOG_NOTICE, "PJSIP: Maintenance mode %s for endpoint '%s'\n",
477 enable ? "enabled" : "disabled", endpoint_name);
478 ast_cli(a->fd, "Maintenance mode %s for endpoint '%s'\n",
479 enable ? "ENABLED" : "DISABLED", endpoint_name);
480 } else if (rc == 0 && enable) {
481 ast_cli(a->fd, "Endpoint '%s' is already in maintenance mode\n", endpoint_name);
482 } else if (rc == 0) {
483 ast_cli(a->fd, "Endpoint '%s' was not in maintenance mode\n", endpoint_name);
484 } else {
485 ast_cli(a->fd, "Failed to %s maintenance mode for endpoint '%s'\n",
486 enable ? "enable" : "disable", endpoint_name);
487 }
488
489 return CLI_SUCCESS;
490}
491
492/* CLI: pjsip show maintenance [endpoint] */
493
494/*! \brief ao2_callback used to print one maintenance entry to the CLI */
495static int cli_maint_entry_cb(void *obj, void *arg, int flags)
496{
497 const char *name = obj;
498 int fd = *(int *)arg;
499 ast_cli(fd, " %-40s ON\n", name);
500 return 0;
501}
502
503static char *handle_cli_pjsip_show_maintenance(struct ast_cli_entry *e, int cmd,
504 struct ast_cli_args *a)
505{
506 const char *endpoint_name;
507 char *entry;
508 int fd;
509 int count;
510
511 switch (cmd) {
512 case CLI_INIT:
513 e->command = "pjsip show maintenance";
514 e->usage =
515 "Usage: pjsip show maintenance [endpoint]\n"
516 " Display endpoints currently in maintenance mode.\n"
517 " If [endpoint] is given, show the status for that endpoint only.\n";
518 return NULL;
519 case CLI_GENERATE:
520 if (a->pos == 3) {
521 return cli_complete_endpoint(a->word);
522 }
523 return NULL;
524 }
525
526 if (a->argc == 4) {
527 endpoint_name = a->argv[3];
528 entry = ao2_find(maintenance_set, endpoint_name, OBJ_SEARCH_KEY);
529 if (entry) {
530 ast_cli(a->fd, "Endpoint '%s' is in maintenance mode\n", endpoint_name);
531 ao2_ref(entry, -1);
532 } else {
533 ast_cli(a->fd, "Endpoint '%s' is NOT in maintenance mode\n", endpoint_name);
534 }
535 return CLI_SUCCESS;
536 }
537
538 if (a->argc != 3) {
539 return CLI_SHOWUSAGE;
540 }
541
542 ast_cli(a->fd, "\n");
543 ast_cli(a->fd, " %-40s %s\n", "Endpoint", "State");
544 ast_cli(a->fd, " %-40s -----\n", "----------------------------------------");
545 fd = a->fd;
547
549 ast_cli(a->fd, "\n %d endpoint%s in maintenance mode\n\n",
550 count, count == 1 ? "" : "s");
551
552 return CLI_SUCCESS;
553}
554
556 AST_CLI_DEFINE(handle_cli_pjsip_set_maintenance, "Set PJSIP endpoint maintenance mode"),
557 AST_CLI_DEFINE(handle_cli_pjsip_show_maintenance, "Show PJSIP endpoint maintenance status"),
558};
559
560/* AMI: PJSIPSetMaintenance, PJSIPShowMaintenance */
561
562static int ami_set_maintenance(struct mansession *s, const struct message *m)
563{
564 const char *endpoint_name;
565 const char *state_str;
566 struct ast_sip_endpoint *endpoint;
567 struct ao2_container *all_endpoints;
568 struct ao2_iterator it;
569 int enable;
570 int rc;
571
572 endpoint_name = astman_get_header(m, "Endpoint");
573 state_str = astman_get_header(m, "State");
574
575 if (ast_strlen_zero(endpoint_name)) {
576 astman_send_error(s, m, "Endpoint parameter missing");
577 return 0;
578 }
579 if (ast_strlen_zero(state_str)) {
580 astman_send_error(s, m, "State parameter missing");
581 return 0;
582 }
583
584 if (!strcasecmp(state_str, "on")) {
585 enable = 1;
586 } else if (!strcasecmp(state_str, "off")) {
587 enable = 0;
588 } else {
589 astman_send_error(s, m, "State must be 'on' or 'off'");
590 return 0;
591 }
592
593 if (!strcasecmp(endpoint_name, "all")) {
594 int count = 0;
595
596 all_endpoints = ast_sip_get_endpoints();
597 if (!all_endpoints) {
598 astman_send_error(s, m, "Failed to retrieve endpoint list");
599 return 0;
600 }
601 it = ao2_iterator_init(all_endpoints, 0);
602 while ((endpoint = ao2_iterator_next(&it))) {
603 if (apply_maintenance_state(ast_sorcery_object_get_id(endpoint), enable) > 0) {
604 count++;
605 }
606 ao2_ref(endpoint, -1);
607 }
609 ao2_ref(all_endpoints, -1);
610 if (count > 0) {
611 manager_event(EVENT_FLAG_SYSTEM, "PJSIPMaintenanceStatus",
612 "Endpoint: all\r\n"
613 "Status: %s\r\n",
614 enable ? "enabled" : "disabled");
615 ast_log(LOG_NOTICE, "PJSIP: Maintenance mode %s for all endpoints "
616 "(%d endpoint%s affected)\n",
617 enable ? "enabled" : "disabled",
618 count, count == 1 ? "" : "s");
619 }
620 astman_send_ack(s, m,
621 enable ? "Maintenance mode enabled for all endpoints"
622 : "Maintenance mode disabled for all endpoints");
623 return 0;
624 }
625
626 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);
627 if (!endpoint) {
628 astman_send_error_va(s, m, "Endpoint '%s' not found", endpoint_name);
629 return 0;
630 }
631 ao2_ref(endpoint, -1);
632
633 rc = apply_maintenance_state(endpoint_name, enable);
634 if (rc < 0) {
635 astman_send_error_va(s, m, "Failed to %s maintenance mode for endpoint '%s'",
636 enable ? "enable" : "disable", endpoint_name);
637 } else {
638 if (rc > 0) {
639 manager_event(EVENT_FLAG_SYSTEM, "PJSIPMaintenanceStatus",
640 "Endpoint: %s\r\n"
641 "Status: %s\r\n",
642 endpoint_name, enable ? "enabled" : "disabled");
643 ast_log(LOG_NOTICE, "PJSIP: Maintenance mode %s for endpoint '%s'\n",
644 enable ? "enabled" : "disabled", endpoint_name);
645 }
646 astman_send_ack(s, m,
647 enable ? "Maintenance mode enabled" : "Maintenance mode disabled");
648 }
649
650 return 0;
651}
652
653/*! \brief ao2_callback used to emit one PJSIPMaintenanceStatus AMI list entry */
654static int ami_maint_entry_cb(void *obj, void *arg, int flags)
655{
656 const char *name = obj;
657 struct ast_sip_ami *ami = arg;
658 struct ast_str *buf;
659
660 buf = ast_sip_create_ami_event("PJSIPMaintenanceStatus", ami);
661 if (!buf) {
662 return 0;
663 }
664 ast_str_append(&buf, 0, "Endpoint: %s\r\nStatus: enabled\r\n", name);
665 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
666 ast_free(buf);
667 ++ami->count;
668
669 return 0;
670}
671
672static int ami_show_maintenance(struct mansession *s, const struct message *m)
673{
674 const char *endpoint_name;
675 struct ast_sip_ami ami;
676 char *entry;
677 struct ast_str *buf;
678
679 endpoint_name = astman_get_header(m, "Endpoint");
680
681 ami.s = s;
682 ami.m = m;
683 ami.action_id = astman_get_header(m, "ActionID");
684 ami.arg = NULL;
685 ami.count = 0;
686
687 astman_send_listack(s, m, "Maintenance status events follow", "start");
688
689 if (!ast_strlen_zero(endpoint_name)) {
690 buf = ast_sip_create_ami_event("PJSIPMaintenanceStatus", &ami);
691 if (buf) {
692 entry = ao2_find(maintenance_set, endpoint_name, OBJ_SEARCH_KEY);
693 ast_str_append(&buf, 0, "Endpoint: %s\r\nStatus: %s\r\n",
694 endpoint_name, entry ? "enabled" : "disabled");
695 if (entry) {
696 ao2_ref(entry, -1);
697 }
698 astman_append(s, "%s\r\n", ast_str_buffer(buf));
699 ast_free(buf);
700 }
701 ami.count = 1;
702 } else {
704 }
705
706 astman_send_list_complete_start(s, m, "PJSIPMaintenanceStatusComplete", ami.count);
708
709 return 0;
710}
711
712/* Module load / unload */
713
734
747
748AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Endpoint Maintenance Mode",
749 .support_level = AST_MODULE_SUPPORT_EXTENDED,
750 .load = load_module,
751 .unload = unload_module,
752 .load_pri = AST_MODPRI_APP_DEPEND,
753 .requires = "res_pjsip,res_pjsip_session",
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_log
Definition astobj2.c:42
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
@ AO2_ALLOC_OPT_LOCK_RWLOCK
Definition astobj2.h:365
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_find(container, arg, flags)
Definition astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_UNLINK
Definition astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition main/cli.c:2845
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition main/cli.c:1931
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
short word
char buf[BUFSIZE]
Definition eagi_proxy.c:66
static const char name[]
Definition format_mp3.c:68
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition manager.c:2030
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:1988
void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt,...)
Send error in manager transaction (with va_args support)
Definition manager.c:1993
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition manager.c:2066
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition manager.c:2020
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1649
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2074
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1909
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7704
Support for logging to various files, console and syslog Configuration in file logger....
#define LOG_ERROR
#define LOG_NOTICE
static struct ao2_container * endpoints
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_REPORTING
Definition manager.h:84
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition manager.h:255
#define EVENT_FLAG_SYSTEM
Definition manager.h:75
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition manager.h:193
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_APP_DEPEND
Definition module.h:342
@ AST_MODULE_SUPPORT_EXTENDED
Definition module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
const char * method
Definition res_pjsip.c:1273
void ast_sip_unregister_service(pjsip_module *module)
Definition res_pjsip.c:127
struct ao2_container * ast_sip_get_endpoints(void)
Retrieve any endpoints available to sorcery.
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition res_pjsip.c:111
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition res_pjsip.c:514
@ AST_SIP_SUPPLEMENT_PRIORITY_FIRST
Definition res_pjsip.h:3359
struct ast_sip_endpoint * ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
Get the looked-up endpoint on an out-of dialog request or response.
struct ast_str * ast_sip_create_ami_event(const char *event, struct ast_sip_ami *ami)
Creates a string to store AMI event data in.
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
static char * handle_cli_pjsip_show_maintenance(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@ MAINT_HASH_BUCKETS
static int ami_maint_entry_cb(void *obj, void *arg, int flags)
ao2_callback used to emit one PJSIPMaintenanceStatus AMI list entry
static char * cli_complete_endpoint(const char *word)
static struct pjsip_module maintenance_pjsip_mod
static int cli_maint_entry_cb(void *obj, void *arg, int flags)
ao2_callback used to print one maintenance entry to the CLI
static int maint_set_remove(const char *endpoint_name)
static int maint_session_create(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, const char *location, const char *request_user, struct ast_stream_topology *req_topology)
static int ami_set_maintenance(struct mansession *s, const struct message *m)
static struct ast_sip_session_supplement maintenance_session_supplement
static char * handle_cli_pjsip_set_maintenance(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static pj_bool_t maintenance_on_rx_request(pjsip_rx_data *rdata)
static int maint_set_add(const char *endpoint_name)
static int apply_maintenance_state(const char *endpoint_name, int enable)
static int load_module(void)
static struct ao2_container * maintenance_set
static struct ast_cli_entry cli_maintenance[]
static int unload_module(void)
static int ami_show_maintenance(struct mansession *s, const struct message *m)
static const struct ast_sorcery_observer endpoint_observer
static void maint_endpoint_deleted(const void *obj)
#define ast_sip_session_register_supplement(supplement)
void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement)
Unregister a an supplement to SIP session processing.
#define NULL
Definition resample.c:96
Sorcery Data Access Layer API.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition sorcery.c:2381
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
Definition sorcery.c:2487
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition sorcery.c:1917
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
Definition sorcery.c:2455
struct ao2_container * ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len)
Retrieve multiple objects whose id begins with the specified prefix.
Definition sorcery.c:2053
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
struct ao2_container * ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
Allocates a hash container for bare strings.
Definition strings.c:200
int ast_str_container_add(struct ao2_container *str_container, const char *add)
Adds a string to a string container allocated by ast_str_container_alloc.
Definition strings.c:205
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
AMI variable container.
Definition res_pjsip.h:3225
const char * action_id
Definition res_pjsip.h:3231
struct mansession * s
Definition res_pjsip.h:3227
const struct message * m
Definition res_pjsip.h:3229
Contact associated with an address of record.
Definition res_pjsip.h:390
An entity with which Asterisk communicates.
Definition res_pjsip.h:1061
A supplement to SIP message processing.
int(* session_create)(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, const char *location, const char *request_user, struct ast_stream_topology *req_topology)
Called before an outgoing session is created.
Interface for a sorcery object type observer.
Definition sorcery.h:332
void(* deleted)(const void *object)
Callback for when an object is deleted.
Definition sorcery.h:340
Support for dynamic strings.
Definition strings.h:623
In case you didn't read that giant block of text above the mansession_session struct,...
Definition manager.c:323
static struct test_val a
#define ARRAY_LEN(a)
Definition utils.h:706