Asterisk - The Open Source Telephony Project GIT-master-7e7a603
res_pjsip_history.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2015, Digium, Inc.
5 *
6 * Matt Jordan <mjordan@digium.com>
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 History
22 *
23 * \author Matt Jordan <mjordan@digium.com>
24 *
25 */
26
27/*** MODULEINFO
28 <depend>pjproject</depend>
29 <depend>res_pjsip</depend>
30 <support_level>extended</support_level>
31 ***/
32
33#include "asterisk.h"
34
35#include <pjsip.h>
36#include <regex.h>
37
38#include "asterisk/res_pjsip.h"
39#include "asterisk/module.h"
40#include "asterisk/logger.h"
41#include "asterisk/cli.h"
42#include "asterisk/netsock2.h"
43#include "asterisk/vector.h"
44#include "asterisk/lock.h"
46
47#define HISTORY_INITIAL_SIZE 256
48
49/*! \brief Pool factory used by pjlib to allocate memory. */
50static pj_caching_pool cachingpool;
51
52/*! \brief Whether or not we are storing history */
53static int enabled;
54
55/*! \brief Packet count */
56static int packet_number;
57
58/*! \brief An item in the history */
60 /*! \brief Packet number */
61 int number;
62 /*! \brief Whether or not we transmitted the packet */
64 /*! \brief Time the packet was transmitted/received */
65 struct timeval timestamp;
66 /*! \brief Source address */
67 pj_sockaddr src;
68 /*! \brief Destination address */
69 pj_sockaddr dst;
70 /*! \brief Memory pool used to allocate \c msg */
71 pj_pool_t *pool;
72 /*! \brief The actual SIP message */
73 pjsip_msg *msg;
74};
75
76/*! \brief Mutex that protects \c vector_history */
78
79struct expression_token;
80
81/*! \brief An operator that we understand in an expression */
82struct operator {
83 /*! \brief Our operator's symbol */
84 const char *symbol;
85 /*! \brief Precedence of the symbol */
87 /*! \brief Non-zero if the operator is evaluated right-to-left */
89 /*! \brief Number of operands the operator takes */
91 /*!
92 * \brief Evaluation function for unary operators
93 *
94 * \param op The operator being evaluated
95 * \param type The type of value contained in \c operand
96 * \param operand A pointer to the value to evaluate
97 *
98 * \retval -1 error
99 * \retval 0 evaluation is False
100 * \retval 1 evaluation is True
101 */
102 int (* const evaluate_unary)(struct operator *op, enum aco_option_type type, void *operand);
103 /*!
104 * \brief Evaluation function for binary operators
105 *
106 * \param op The operator being evaluated
107 * \param type The type of value contained in \c op_left
108 * \param op_left A pointer to the value to evaluate (a result or extracted from an entry)
109 * \param op_right The expression token containing the other value (a result or user-provided)
110 *
111 * \retval -1 error
112 * \retval 0 evaluation is False
113 * \retval 1 evaluation is True
114 */
115 int (* const evaluate)(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right);
116};
117
118/*! \brief A field that we understand and can perform operations on */
120 /*! \brief The representation of the field */
121 const char *symbol;
122 /*! \brief The type /c get_field returns */
124 /*!
125 * \brief Function that returns the field from a pjsip_history_entry
126 *
127 * Note that the function must return a pointer to the location in
128 * \c pjsip_history_entry - no memory should be allocated as the caller
129 * will not dispose of any
130 */
131 void *(* const get_field)(struct pjsip_history_entry *entry);
132};
133
134/*! \brief The type of token that has been parsed out of an expression */
136 /*! The \c expression_token contains a field */
138 /*! The \c expression_token contains an operator */
140 /*! The \c expression_token contains a previous result */
143
144/*! \brief A token in the expression or an evaluated part of the expression */
146 /*! \brief The next expression token in the queue */
148 /*! \brief The type of value stored in the expression token */
150 /*! \brief An operator that evaluates expressions */
151 struct operator *op;
152 /*! \brief The result of an evaluated expression */
154 /*! \brief The field in the expression */
155 char field[];
156};
157
158/*! \brief Log level for history output */
159static int log_level = -1;
160
161/*! \brief The one and only history that we've captured */
163
164/*!
165 * \brief Operator callback for determining equality
166 */
167static int evaluate_equal(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
168{
169 switch (type) {
170 case OPT_BOOL_T:
171 case OPT_BOOLFLAG_T:
172 case OPT_INT_T:
173 case OPT_UINT_T:
174 {
175 int right;
176
177 if (sscanf(op_right->field, "%30d", &right) != 1) {
178 ast_log(LOG_WARNING, "Unable to extract field '%s': not an integer\n", op_right->field);
179 return -1;
180 }
181 return (*(int *)op_left) == right;
182 }
183 case OPT_DOUBLE_T:
184 {
185 double right;
186
187 if (sscanf(op_right->field, "%lf", &right) != 1) {
188 ast_log(LOG_WARNING, "Unable to extract field '%s': not a double\n", op_right->field);
189 return -1;
190 }
191 return (*(double *)op_left) == right;
192 }
193 case OPT_CHAR_ARRAY_T:
195 /* In our case, we operate on pj_str_t */
196 return pj_strcmp2(op_left, op_right->field) == 0;
197 case OPT_NOOP_T:
198 /* Used for timeval */
199 {
200 struct timeval right = { 0, };
201
202 if ((right.tv_sec = ast_string_to_time_t(op_right->field)) == -1) {
203 ast_log(LOG_WARNING, "Unable to extract field '%s': not a timestamp\n", op_right->field);
204 return -1;
205 }
206
207 return ast_tvcmp(*(struct timeval *)op_left, right) == 0;
208 }
209 case OPT_SOCKADDR_T:
210 /* In our case, we operate only on pj_sockaddr_t */
211 {
212 pj_sockaddr right;
213 pj_str_t str_right;
214
215 pj_cstr(&str_right, op_right->field);
216 if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &str_right, &right) != PJ_SUCCESS) {
217 ast_log(LOG_WARNING, "Unable to convert field '%s': not an IPv4 or IPv6 address\n", op_right->field);
218 return -1;
219 }
220
221 return pj_sockaddr_cmp(op_left, &right) == 0;
222 }
223 default:
224 ast_log(LOG_WARNING, "Cannot evaluate field '%s': invalid type for operator '%s'\n",
225 op_right->field, op->symbol);
226 }
227
228 return -1;
229}
230
231/*!
232 * \brief Operator callback for determining inequality
233 */
234static int evaluate_not_equal(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
235{
236 return !evaluate_equal(op, type, op_left, op_right);
237}
238
239/*!
240 * \brief Operator callback for determining if one operand is less than another
241 */
242static int evaluate_less_than(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
243{
244 switch (type) {
245 case OPT_BOOL_T:
246 case OPT_BOOLFLAG_T:
247 case OPT_INT_T:
248 case OPT_UINT_T:
249 {
250 int right;
251
252 if (sscanf(op_right->field, "%30d", &right) != 1) {
253 ast_log(LOG_WARNING, "Unable to extract field '%s': not an integer\n", op_right->field);
254 return -1;
255 }
256 return (*(int *)op_left) < right;
257 }
258 case OPT_DOUBLE_T:
259 {
260 double right;
261
262 if (sscanf(op_right->field, "%lf", &right) != 1) {
263 ast_log(LOG_WARNING, "Unable to extract field '%s': not a double\n", op_right->field);
264 return -1;
265 }
266 return (*(double *)op_left) < right;
267 }
268 case OPT_NOOP_T:
269 /* Used for timeval */
270 {
271 struct timeval right = { 0, };
272
273 if ((right.tv_sec = ast_string_to_time_t(op_right->field)) == -1) {
274 ast_log(LOG_WARNING, "Unable to extract field '%s': not a timestamp\n", op_right->field);
275 return -1;
276 }
277
278 return ast_tvcmp(*(struct timeval *)op_left, right) == -1;
279 }
280 default:
281 ast_log(LOG_WARNING, "Cannot evaluate field '%s': invalid type for operator '%s'\n",
282 op_right->field, op->symbol);
283 }
284
285 return -1;
286}
287
288/*!
289 * \brief Operator callback for determining if one operand is greater than another
290 */
291static int evaluate_greater_than(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
292{
293 switch (type) {
294 case OPT_BOOL_T:
295 case OPT_BOOLFLAG_T:
296 case OPT_INT_T:
297 case OPT_UINT_T:
298 {
299 int right;
300
301 if (sscanf(op_right->field, "%30d", &right) != 1) {
302 ast_log(LOG_WARNING, "Unable to extract field '%s': not an integer\n", op_right->field);
303 return -1;
304 }
305 return (*(int *)op_left) > right;
306 }
307 case OPT_DOUBLE_T:
308 {
309 double right;
310
311 if (sscanf(op_right->field, "%lf", &right) != 1) {
312 ast_log(LOG_WARNING, "Unable to extract field '%s': not a double\n", op_right->field);
313 return -1;
314 }
315 return (*(double *)op_left) > right;
316 }
317 case OPT_NOOP_T:
318 /* Used for timeval */
319 {
320 struct timeval right = { 0, };
321
322 if ((right.tv_sec = ast_string_to_time_t(op_right->field)) == -1) {
323 ast_log(LOG_WARNING, "Unable to extract field '%s': not a timestamp\n", op_right->field);
324 return -1;
325 }
326
327 return ast_tvcmp(*(struct timeval *)op_left, right) == 1;
328 }
329 default:
330 ast_log(LOG_WARNING, "Cannot evaluate field '%s': invalid type for operator '%s'\n",
331 op_right->field, op->symbol);
332 }
333
334 return -1;
335}
336
337/*!
338 * \brief Operator callback for determining if one operand is less than or equal to another
339 */
340static int evaluate_less_than_or_equal(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
341{
342 return !evaluate_greater_than(op, type, op_left, op_right);
343}
344
345/*!
346 * \brief Operator callback for determining if one operand is greater than or equal to another
347 */
348static int evaluate_greater_than_or_equal(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
349{
350 return !evaluate_less_than(op, type, op_left, op_right);
351}
352
353/*!
354 * \brief Operator callback for determining logical NOT
355 */
356static int evaluate_not(struct operator *op, enum aco_option_type type, void *operand)
357{
358 switch (type) {
359 case OPT_BOOL_T:
360 case OPT_BOOLFLAG_T:
361 case OPT_INT_T:
362 case OPT_UINT_T:
363 return !(*(int *)operand);
364 default:
365 ast_log(LOG_WARNING, "Cannot evaluate: invalid operand type for operator '%s'\n", op->symbol);
366 }
367
368 return -1;
369}
370
371/*!
372 * \brief Operator callback for determining logical AND
373 */
374static int evaluate_and(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
375{
376 switch (type) {
377 case OPT_BOOL_T:
378 case OPT_BOOLFLAG_T:
379 case OPT_INT_T:
380 case OPT_UINT_T:
381 return (*(int *)op_left && op_right->result);
382 default:
383 ast_log(LOG_WARNING, "Cannot evaluate: invalid operand type for operator '%s'\n", op->symbol);
384 }
385
386 return -1;
387}
388
389/*!
390 * \brief Operator callback for determining logical OR
391 */
392static int evaluate_or(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
393{
394 switch (type) {
395 case OPT_BOOL_T:
396 case OPT_BOOLFLAG_T:
397 case OPT_INT_T:
398 case OPT_UINT_T:
399 return (*(int *)op_left || op_right->result);
400 default:
401 ast_log(LOG_WARNING, "Cannot evaluate: invalid operand type for operator '%s'\n", op->symbol);
402 }
403
404 return -1;
405}
406
407/*!
408 * \brief Operator callback for regex 'like'
409 */
410static int evaluate_like(struct operator *op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
411{
412 switch (type) {
413 case OPT_CHAR_ARRAY_T:
415 /* In our case, we operate on pj_str_t */
416 {
417 int result;
418 regex_t regexbuf;
419 char buf[pj_strlen(op_left) + 1];
420
421 ast_copy_pj_str(buf, op_left, pj_strlen(op_left));
422 if (regcomp(&regexbuf, op_right->field, REG_EXTENDED | REG_NOSUB)) {
423 ast_log(LOG_WARNING, "Failed to compile '%s' into a regular expression\n", op_right->field);
424 return -1;
425 }
426
427 result = (regexec(&regexbuf, buf, 0, NULL, 0) == 0);
428 regfree(&regexbuf);
429
430 return result;
431 }
432 default:
433 ast_log(LOG_WARNING, "Cannot evaluate: invalid operand type for operator '%s'\n", op->symbol);
434 }
435
436 return -1;
437}
438
439/*!
440 * \brief Operator token for a left parenthesis.
441 *
442 * While this is used by the shunting-yard algorithm implementation,
443 * it should never appear in the resulting RPN queue of expression tokens
444 */
445static struct operator left_paren = {
446 .symbol = "(",
447 .precedence = 15
448};
449
450/*!
451 * \brief Our allowed operations
452 */
453static struct operator allowed_operators[] = {
454 { .symbol = "=", .precedence = 7, .operands = 2, .evaluate = evaluate_equal, },
455 { .symbol = "==", .precedence = 7, .operands = 2, .evaluate = evaluate_equal, },
456 { .symbol = "!=", .precedence = 7, .operands = 2, .evaluate = evaluate_not_equal, },
457 { .symbol = "<", .precedence = 6, .operands = 2, .evaluate = evaluate_less_than, },
458 { .symbol = ">", .precedence = 6, .operands = 2, .evaluate = evaluate_greater_than, },
459 { .symbol = "<=", .precedence = 6, .operands = 2, .evaluate = evaluate_less_than_or_equal, },
460 { .symbol = ">=", .precedence = 6, .operands = 2, .evaluate = evaluate_greater_than_or_equal, },
461 { .symbol = "!", .precedence = 2, .operands = 1, .right_to_left = 1, .evaluate_unary = evaluate_not, },
462 { .symbol = "&&", .precedence = 11, .operands = 2, .evaluate = evaluate_and, },
463 { .symbol = "||", .precedence = 12, .operands = 2, .evaluate = evaluate_or, },
464 { .symbol = "like", .precedence = 7, .operands = 2, .evaluate = evaluate_like, },
465 { .symbol = "and", .precedence = 11, .operands = 2, .evaluate = evaluate_and, },
466 { .symbol = "or", .precedence = 11, .operands = 2, .evaluate = evaluate_or, },
467 { .symbol = "not", .precedence = 2, .operands = 1, .right_to_left = 1, .evaluate_unary = evaluate_not, },
468};
469
470/*! \brief Callback to retrieve the entry index number */
472{
473 return &entry->number;
474}
475
476/*! \brief Callback to retrieve the entry's timestamp */
478{
479 return &entry->timestamp;
480}
481
482/*! \brief Callback to retrieve the entry's destination address */
484{
485 if (entry->transmitted) {
486 return &entry->dst;
487 } else {
488 return &entry->src;
489 }
490}
491
492/*! \brief Callback to retrieve the entry's SIP request method type */
494{
495 if (entry->msg->type != PJSIP_REQUEST_MSG) {
496 return NULL;
497 }
498
499 return &entry->msg->line.req.method.name;
500}
501
502/*! \brief Callback to retrieve the entry's SIP Call-ID header */
504{
505 pjsip_cid_hdr *cid_hdr;
506
507 cid_hdr = PJSIP_MSG_CID_HDR(entry->msg);
508
509 return &cid_hdr->id;
510}
511
512/*! \brief The fields we allow */
513static struct allowed_field allowed_fields[] = {
514 { .symbol = "number", .return_type = OPT_INT_T, .get_field = entry_get_number, },
515 /* We co-op the NOOP type here for timeval */
516 { .symbol = "timestamp", .return_type = OPT_NOOP_T, .get_field = entry_get_timestamp, },
517 { .symbol = "addr", .return_type = OPT_SOCKADDR_T, .get_field = entry_get_addr, },
518 { .symbol = "sip.msg.request.method", .return_type = OPT_CHAR_ARRAY_T, .get_field = entry_get_sip_msg_request_method, },
519 { .symbol = "sip.msg.call-id", .return_type = OPT_CHAR_ARRAY_T, .get_field = entry_get_sip_msg_call_id, },
520};
521
522/*! \brief Free an expression token and all others it references */
524{
525 struct expression_token *it_token;
526
527 it_token = token;
528 while (it_token) {
529 struct expression_token *prev = it_token;
530
531 it_token = it_token->next;
532 ast_free(prev);
533 }
534
535 return NULL;
536}
537
538/*!
539 * \brief Allocate an expression token
540 *
541 * \param token_type The type of token in the expression
542 * \param value The value/operator/result to pack into the token
543 *
544 * \retval NULL on failure
545 * \retval expression_token on success
546 */
548{
549 struct expression_token *token;
550
551 switch (token_type) {
554 token = ast_calloc(1, sizeof(*token));
555 break;
556 case TOKEN_TYPE_FIELD:
557 token = ast_calloc(1, sizeof(*token) + strlen((const char *)value) + 1);
558 break;
559 default:
560 ast_assert(0);
561 return NULL;
562 }
563
564 if (!token) {
565 return NULL;
566 }
567 token->token_type = token_type;
568
569 switch (token_type) {
571 token->result = *(int *)value;
572 break;
574 token->op = value;
575 break;
576 case TOKEN_TYPE_FIELD:
577 strcpy(token->field, value); /* safe */
578 break;
579 default:
580 ast_assert(0);
581 }
582
583 return token;
584}
585
586/*! \brief Determine if the expression token matches a field in \c allowed_fields */
588{
589 int i;
590
592
593 for (i = 0; i < ARRAY_LEN(allowed_fields); i++) {
594 if (strcasecmp(allowed_fields[i].symbol, token->field)) {
595 continue;
596 }
597
598 return &allowed_fields[i];
599 }
600
601 return NULL;
602}
603
604/*! \brief AO2 destructor for \c pjsip_history_entry */
605static void pjsip_history_entry_dtor(void *obj)
606{
607 struct pjsip_history_entry *entry = obj;
608
609 if (entry->pool) {
610 /* This mimics the behavior of pj_pool_safe_release
611 * which was introduced in pjproject 2.6.
612 */
613 pj_pool_t *temp_pool = entry->pool;
614
615 entry->pool = NULL;
616 pj_pool_release(temp_pool);
617 }
618}
619
620/*!
621 * \brief Create a \c pjsip_history_entry AO2 object
622 *
623 * \param msg The PJSIP message that this history entry wraps
624 *
625 * \retval An AO2 \c pjsip_history_entry object on success
626 * \retval NULL on failure
627 */
629{
631
633 if (!entry) {
634 return NULL;
635 }
637 entry->timestamp = ast_tvnow();
638 entry->timestamp.tv_usec = 0;
639
640 entry->pool = pj_pool_create(&cachingpool.factory, NULL, PJSIP_POOL_RDATA_LEN,
641 PJSIP_POOL_RDATA_INC, NULL);
642 if (!entry->pool) {
643 ao2_ref(entry, -1);
644 return NULL;
645 }
646
647 entry->msg = pjsip_msg_clone(entry->pool, msg);
648 if (!entry->msg) {
649 ao2_ref(entry, -1);
650 return NULL;
651 }
652
653 return entry;
654}
655
656/*! \brief Format single line history entry */
657static void sprint_list_entry(struct pjsip_history_entry *entry, char *line, int len)
658{
659 char addr[64], secs[AST_TIME_T_LEN];
660
661 if (entry->transmitted) {
662 pj_sockaddr_print(&entry->dst, addr, sizeof(addr), 3);
663 } else {
664 pj_sockaddr_print(&entry->src, addr, sizeof(addr), 3);
665 }
666
667 ast_time_t_to_string(entry->timestamp.tv_sec, secs, sizeof(secs));
668
669 if (entry->msg->type == PJSIP_REQUEST_MSG) {
670 char uri[128];
671
672 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, entry->msg->line.req.uri, uri, sizeof(uri));
673 snprintf(line, len, "%-5.5d %-10.10s %-5.5s %-24.24s %.*s %s SIP/2.0",
674 entry->number,
675 secs,
676 entry->transmitted ? "* ==>" : "* <==",
677 addr,
678 (int)pj_strlen(&entry->msg->line.req.method.name),
679 pj_strbuf(&entry->msg->line.req.method.name),
680 uri);
681 } else {
682 snprintf(line, len, "%-5.5d %-10.10s %-5.5s %-24.24s SIP/2.0 %u %.*s",
683 entry->number,
684 secs,
685 entry->transmitted ? "* ==>" : "* <==",
686 addr,
687 entry->msg->line.status.code,
688 (int)pj_strlen(&entry->msg->line.status.reason),
689 pj_strbuf(&entry->msg->line.status.reason));
690 }
691}
692
693/*! \brief PJSIP callback when a SIP message is transmitted */
694static pj_status_t history_on_tx_msg(pjsip_tx_data *tdata)
695{
697
698 if (!enabled) {
699 return PJ_SUCCESS;
700 }
701
702 entry = pjsip_history_entry_alloc(tdata->msg);
703 if (!entry) {
704 return PJ_SUCCESS;
705 }
706 entry->transmitted = 1;
707 pj_sockaddr_cp(&entry->src, &tdata->tp_info.transport->local_addr);
708 pj_sockaddr_cp(&entry->dst, &tdata->tp_info.dst_addr);
709
712 ao2_ref(entry, -1);
713 entry = NULL;
714 }
716
717 if (log_level != -1 && entry) {
718 char line[256];
719
720 sprint_list_entry(entry, line, sizeof(line));
721 ast_log_dynamic_level(log_level, "%s\n", line);
722 }
723
724 return PJ_SUCCESS;
725}
726
727/*! \brief PJSIP callback when a SIP message is received */
728static pj_bool_t history_on_rx_msg(pjsip_rx_data *rdata)
729{
731
732 if (!enabled) {
733 return PJ_FALSE;
734 }
735
736 if (!rdata->msg_info.msg) {
737 return PJ_FALSE;
738 }
739
740 entry = pjsip_history_entry_alloc(rdata->msg_info.msg);
741 if (!entry) {
742 return PJ_FALSE;
743 }
744
745 if (rdata->tp_info.transport->addr_len) {
746 pj_sockaddr_cp(&entry->dst, &rdata->tp_info.transport->local_addr);
747 }
748
749 if (rdata->pkt_info.src_addr_len) {
750 pj_sockaddr_cp(&entry->src, &rdata->pkt_info.src_addr);
751 }
752
755 ao2_ref(entry, -1);
756 entry = NULL;
757 }
759
760 if (log_level != -1 && entry) {
761 char line[256];
762
763 sprint_list_entry(entry, line, sizeof(line));
764 ast_log_dynamic_level(log_level, "%s\n", line);
765 }
766
767 return PJ_FALSE;
768}
769
770/*! \brief Vector callback that releases the reference for the entry in a history vector */
772{
773 ao2_ref(entry, -1);
774}
775
776/*!
777 * \brief Remove all entries from \c vector_history
778 *
779 * This must be called from a registered PJSIP thread
780 */
781static int clear_history_entries(void *obj)
782{
785 packet_number = 0;
787
788 return 0;
789}
790
791/*!
792 * \brief Build a reverse polish notation expression queue
793 *
794 * This function is an implementation of the Shunting-Yard Algorithm. It takes
795 * a user provided infix-notation expression and converts it into a reverse
796 * polish notation expression, which is a queue of tokens that can be easily
797 * parsed.
798 *
799 * \param a The CLI arguments provided by the User, containing the infix expression
800 *
801 * \retval NULL error
802 * \retval expression_token A 'queue' of expression tokens in RPN
803 */
805{
806 AST_VECTOR(, struct operator *) operators; /* A stack of saved operators */
807 struct expression_token *output = NULL; /* The output queue */
808 struct expression_token *head = NULL; /* Pointer to the head of /c output */
809 int i;
810
811#define APPEND_TO_OUTPUT(output, token) do { \
812 if ((output)) { \
813 (output)->next = (token); \
814 (output) = (token); \
815 } else { \
816 (output) = (token); \
817 head = (output); \
818 } \
819} while (0)
820
821 if (AST_VECTOR_INIT(&operators, 8)) {
822 return NULL;
823 }
824
825 for (i = 4; i < a->argc; i++) {
826 struct expression_token *out_token;
827 char *token = ast_strdupa(a->argv[i]);
828 int j;
829
830 /* Strip off and append any left parentheses */
831 if (token[0] == '(') {
832 AST_VECTOR_APPEND(&operators, &left_paren);
833 if (!token[1]) {
834 continue;
835 }
836 token = &token[1];
837 }
838
839 /* Handle the case where the token is an operator */
840 for (j = 0; j < ARRAY_LEN(allowed_operators); j++) {
841 int k;
842
843 if (strcasecmp(token, allowed_operators[j].symbol)) {
844 continue;
845 }
846
847 for (k = AST_VECTOR_SIZE(&operators) - 1; k >= 0; k--) {
848 struct operator *top = AST_VECTOR_GET(&operators, k);
849
850 /* Remove and push queued up operators, if they are of
851 * less precedence than this operator
852 */
855
856 if (!(out_token = expression_token_alloc(TOKEN_TYPE_OPERATOR, top))) {
857 goto error;
858 }
859 APPEND_TO_OUTPUT(output, out_token);
860 AST_VECTOR_REMOVE(&operators, k, 1);
861 }
862 }
863
864 AST_VECTOR_APPEND(&operators, &allowed_operators[j]);
865 token = NULL;
866 break;
867 }
868
869 /* Token was an operator; continue to next token */
870 if (!token) {
871 continue;
872 }
873
874 /* Handle a right parentheses either by itself or as part of the token.
875 * If part of the token, push the token onto the output queue first
876 */
877 if (token[0] == ')' || token[strlen(token) - 1] == ')') {
878
879 if (token[strlen(token) - 1] == ')') {
880 token[strlen(token) - 1] = '\0';
881
882 if (!(out_token = expression_token_alloc(TOKEN_TYPE_FIELD, token))) {
883 goto error;
884 }
885 APPEND_TO_OUTPUT(output, out_token);
886 token = NULL;
887 }
888
889 for (j = AST_VECTOR_SIZE(&operators) - 1; j >= 0; j--) {
890 struct operator *top = AST_VECTOR_GET(&operators, j);
891
892 AST_VECTOR_REMOVE(&operators, j, 1);
893 if (top == &left_paren) {
894 break;
895 }
896
897 if (!(out_token = expression_token_alloc(TOKEN_TYPE_OPERATOR, top))) {
898 goto error;
899 }
900 APPEND_TO_OUTPUT(output, out_token);
901 }
902 }
903
904 /* Just a plain token, push to the output queue */
905 if (token) {
906 if (!(out_token = expression_token_alloc(TOKEN_TYPE_FIELD, token))) {
907 goto error;
908 }
909 APPEND_TO_OUTPUT(output, out_token);
910 }
911 }
912
913 /* Remove any non-applied operators that remain, applying them
914 * to the output queue
915 */
916 for (i = AST_VECTOR_SIZE(&operators) - 1; i >= 0; i--) {
917 struct operator *top = AST_VECTOR_GET(&operators, i);
918 struct expression_token *out_token;
919
920 AST_VECTOR_REMOVE(&operators, i, 1);
921 if (top == &left_paren) {
922 ast_log(LOG_WARNING, "Unbalanced '(' parentheses in expression!\n");
923 continue;
924 }
925
926 if (!(out_token = expression_token_alloc(TOKEN_TYPE_OPERATOR, top))) {
927 goto error;
928 }
929 APPEND_TO_OUTPUT(output, out_token);
930 }
931
932 AST_VECTOR_FREE(&operators);
933 return head;
934
935error:
936 AST_VECTOR_FREE(&operators);
937 expression_token_free(output);
938 return NULL;
939}
940
941/*!
942 * \brief Evaluate a single entry in this history using a RPN expression
943 *
944 * \param entry The entry in the history to evaluate
945 * \param queue The RPN expression
946 *
947 * \retval 0 The expression evaluated FALSE on \c entry
948 * \retval 1 The expression evaluated TRUE on \c entry
949 * \retval -1 The expression errored
950 */
952{
953 AST_VECTOR(, struct expression_token *) stack; /* Our stack of results and operands */
954 struct expression_token *it_queue;
955 struct expression_token *final;
956 int result;
957 int i;
958
959 if (AST_VECTOR_INIT(&stack, 16)) {
960 return -1;
961 }
962
963 for (it_queue = queue; it_queue; it_queue = it_queue->next) {
964 struct expression_token *op_one;
965 struct expression_token *op_two = NULL;
966 struct expression_token *result;
967 int res = 0;
968
969 /* If this is not an operator, push it to the stack */
970 if (!it_queue->op) {
971 if (AST_VECTOR_APPEND(&stack, it_queue)) {
972 goto error;
973 }
974 continue;
975 }
976
977 if (AST_VECTOR_SIZE(&stack) < it_queue->op->operands) {
978 ast_log(LOG_WARNING, "Unable to evaluate expression operator '%s': not enough operands\n",
979 it_queue->op->symbol);
980 goto error;
981 }
982
983 if (it_queue->op->operands == 1) {
984 /* Unary operators currently consist only of 'not', which can only act
985 * upon an evaluated condition result.
986 */
987 ast_assert(it_queue->op->evaluate_unary != NULL);
988
989 op_one = AST_VECTOR_REMOVE(&stack, AST_VECTOR_SIZE(&stack) - 1, 1);
990 if (op_one->token_type != TOKEN_TYPE_RESULT) {
991 ast_log(LOG_WARNING, "Unable to evaluate '%s': operand is not the result of an operation\n",
992 it_queue->op->symbol);
993 goto error;
994 }
995
996 res = it_queue->op->evaluate_unary(it_queue->op, OPT_INT_T, &op_one->result) == 0 ? 0 : 1;
997 } else if (it_queue->op->operands == 2) {
998 struct allowed_field *field;
1000 void *value;
1001
1002 ast_assert(it_queue->op->evaluate != NULL);
1003
1004 op_one = AST_VECTOR_REMOVE(&stack, AST_VECTOR_SIZE(&stack) - 1, 1);
1005 op_two = AST_VECTOR_REMOVE(&stack, AST_VECTOR_SIZE(&stack) - 1, 1);
1006
1007 /* If operand two is a field, then it must be a field we recognize. */
1008 if (op_two->token_type == TOKEN_TYPE_FIELD) {
1009 field = get_allowed_field(op_two);
1010 if (!field) {
1011 ast_log(LOG_WARNING, "Unknown or unrecognized field: %s\n", op_two->field);
1012 goto error;
1013 }
1014
1015 type = field->return_type;
1016 value = field->get_field(entry);
1017 } else if (op_two->token_type == TOKEN_TYPE_RESULT) {
1018 type = OPT_INT_T;
1019 value = &op_two->result;
1020 } else {
1021 ast_log(LOG_WARNING, "Attempting to evaluate an operator: %s\n", op_two->op->symbol);
1022 goto error;
1023 }
1024
1025 if (value) {
1026 res = it_queue->op->evaluate(it_queue->op, type, value, op_one) == 0 ? 0 : 1;
1027 } else {
1028 res = 0;
1029 }
1030 } else {
1031 ast_log(LOG_WARNING, "Operator '%s' has an invalid number of operands\n", it_queue->op->symbol);
1032 ast_assert(0);
1033 goto error;
1034 }
1035
1036 /* Results are temporary; clean used ones up */
1037 if (op_one && op_one->token_type == TOKEN_TYPE_RESULT) {
1038 ast_free(op_one);
1039 }
1040 if (op_two && op_two->token_type == TOKEN_TYPE_RESULT) {
1041 ast_free(op_two);
1042 }
1043
1044 /* Push the result onto the stack */
1046 if (!result) {
1047 goto error;
1048 }
1049 if (AST_VECTOR_APPEND(&stack, result)) {
1051
1052 goto error;
1053 }
1054 }
1055
1056 /*
1057 * When the evaluation is complete, we must have:
1058 * - A single result remaining on the stack
1059 * - An actual result
1060 */
1061 if (AST_VECTOR_SIZE(&stack) != 1) {
1062 ast_log(LOG_WARNING, "Expression was unbalanced: %zu results remained after evaluation\n",
1063 AST_VECTOR_SIZE(&stack));
1064 goto error;
1065 }
1066
1067 final = AST_VECTOR_GET(&stack, 0);
1068 if (final->token_type != TOKEN_TYPE_RESULT) {
1069 ast_log(LOG_WARNING, "Expression did not create a usable result\n");
1070 goto error;
1071 }
1072 result = final->result;
1073 ast_free(final);
1074 AST_VECTOR_FREE(&stack);
1075
1076 return result;
1077
1078error:
1079 /* Clean out any remaining result expression tokens */
1080 for (i = 0; i < AST_VECTOR_SIZE(&stack); i++) {
1081 struct expression_token *failed_token = AST_VECTOR_GET(&stack, i);
1082
1083 if (failed_token->token_type == TOKEN_TYPE_RESULT) {
1084 ast_free(failed_token);
1085 }
1086 }
1087 AST_VECTOR_FREE(&stack);
1088 return -1;
1089}
1090
1091/*!
1092 * \brief Create a filtered history based on a user provided expression
1093 *
1094 * \param a The CLI arguments containing the expression
1095 *
1096 * \retval NULL on error
1097 * \retval A vector containing the filtered history on success
1098 */
1100{
1101 struct vector_history_t *output;
1102 struct expression_token *queue;
1103 int i;
1104
1105 output = ast_malloc(sizeof(*output));
1106 if (!output) {
1107 return NULL;
1108 }
1109
1110 if (AST_VECTOR_INIT(output, HISTORY_INITIAL_SIZE / 2)) {
1111 ast_free(output);
1112 return NULL;
1113 }
1114
1115 queue = build_expression_queue(a);
1116 if (!queue) {
1117 AST_VECTOR_PTR_FREE(output);
1118 return NULL;
1119 }
1120
1122 for (i = 0; i < AST_VECTOR_SIZE(&vector_history); i++) {
1124 int res;
1125
1126 res = evaluate_history_entry(entry, queue);
1127 if (res == -1) {
1128 /* Error in expression evaluation; bail */
1131 AST_VECTOR_FREE(output);
1132 ast_free(output);
1133 expression_token_free(queue);
1134 return NULL;
1135 } else if (!res) {
1136 continue;
1137 } else {
1138 ao2_bump(entry);
1139 if (AST_VECTOR_APPEND(output, entry)) {
1141 }
1142 }
1143 }
1145
1146 expression_token_free(queue);
1147
1148 return output;
1149}
1150
1151/*! \brief Print a detailed view of a single entry in the history to the CLI */
1153{
1154 char addr[64], secs[AST_TIME_T_LEN];
1155 char *buf;
1156
1157 buf = ast_calloc(1, PJSIP_MAX_PKT_LEN * sizeof(char));
1158 if (!buf) {
1159 return;
1160 }
1161
1162 if (pjsip_msg_print(entry->msg, buf, PJSIP_MAX_PKT_LEN) == -1) {
1163 ast_log(LOG_WARNING, "Unable to print SIP message %d: packet too large!\n", entry->number);
1164 ast_free(buf);
1165 return;
1166 }
1167
1168 if (entry->transmitted) {
1169 pj_sockaddr_print(&entry->dst, addr, sizeof(addr), 3);
1170 } else {
1171 pj_sockaddr_print(&entry->src, addr, sizeof(addr), 3);
1172 }
1173
1174 ast_time_t_to_string(entry->timestamp.tv_sec, secs, sizeof(secs));
1175 ast_cli(a->fd, "<--- History Entry %d %s %s at %-10.10s --->\n",
1176 entry->number,
1177 entry->transmitted ? "Sent to" : "Received from",
1178 addr,
1179 secs);
1180 ast_cli(a->fd, "%s\n", buf);
1181
1182 ast_free(buf);
1183}
1184
1185/*! \brief Print a list of the entries to the CLI */
1186static void display_entry_list(struct ast_cli_args *a, struct vector_history_t *vec)
1187{
1188 int i;
1189
1190 ast_cli(a->fd, "%-5.5s %-10.10s %-30.30s %-35.35s\n",
1191 "No.",
1192 "Timestamp",
1193 "(Dir) Address",
1194 "SIP Message");
1195 ast_cli(a->fd, "===== ========== ============================== ===================================\n");
1196
1197 for (i = 0; i < AST_VECTOR_SIZE(vec); i++) {
1198 struct pjsip_history_entry *entry;
1199 char line[256];
1200
1201 entry = AST_VECTOR_GET(vec, i);
1202 sprint_list_entry(entry, line, sizeof(line));
1203
1204 ast_cli(a->fd, "%s\n", line);
1205 }
1206}
1207
1208/*! \brief Cleanup routine for a history vector, serviced on a registered PJSIP thread */
1209static int safe_vector_cleanup(void *obj)
1210{
1211 struct vector_history_t *vec = obj;
1212
1214 AST_VECTOR_FREE(vec);
1215 ast_free(vec);
1216
1217 return 0;
1218}
1219
1220static char *pjsip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1221{
1222 struct vector_history_t *vec = &vector_history;
1223 struct pjsip_history_entry *entry = NULL;
1224
1225 if (cmd == CLI_INIT) {
1226 e->command = "pjsip show history";
1227 e->usage =
1228 "Usage: pjsip show history [entry <num>|where [...]]\n"
1229 " Displays the currently collected history or an\n"
1230 " entry within the history.\n\n"
1231 " * Running the command with no options will display\n"
1232 " the entire history.\n"
1233 " * Providing 'entry <num>' will display the full\n"
1234 " detail of a particular entry in this history.\n"
1235 " * Providing 'where ...' will allow for filtering\n"
1236 " the history. The history can be filtered using\n"
1237 " any of the following fields:\n"
1238 " - number: The history entry number\n"
1239 " - timestamp: The time associated with the history entry\n"
1240 " - addr: The source/destination address of the SIP message\n"
1241 " - sip.msg.request.method: The request method type\n"
1242 " - sip.msg.call-id: The Call-ID header of the SIP message\n"
1243 "\n"
1244 " When filtering, standard Boolean operators can be used,\n"
1245 " as well as 'like' for regexs.\n"
1246 "\n"
1247 " Example:\n"
1248 " 'pjsip show history where number > 5 and (addr = \"192.168.0.3:5060\" or addr = \"192.168.0.5:5060\")'\n";
1249 return NULL;
1250 } else if (cmd == CLI_GENERATE) {
1251 return NULL;
1252 }
1253
1254 if (a->argc > 3) {
1255 if (!strcasecmp(a->argv[3], "entry") && a->argc == 5) {
1256 int num;
1257
1258 if (sscanf(a->argv[4], "%30d", &num) != 1) {
1259 ast_cli(a->fd, "'%s' is not a valid entry number\n", a->argv[4]);
1260 return CLI_FAILURE;
1261 }
1262
1263 /* Get the entry at the provided position */
1265 if (num >= AST_VECTOR_SIZE(&vector_history) || num < 0) {
1266 ast_cli(a->fd, "Entry '%d' does not exist\n", num);
1268 return CLI_FAILURE;
1269 }
1272 } else if (!strcasecmp(a->argv[3], "where")) {
1273 vec = filter_history(a);
1274 if (!vec) {
1275 return CLI_FAILURE;
1276 }
1277 } else {
1278 return CLI_SHOWUSAGE;
1279 }
1280 }
1281
1282 if (AST_VECTOR_SIZE(vec) == 1) {
1283 if (vec == &vector_history) {
1285 }
1286 entry = ao2_bump(AST_VECTOR_GET(vec, 0));
1287 if (vec == &vector_history) {
1289 }
1290 }
1291
1292 if (entry) {
1294 } else {
1295 if (vec == &vector_history) {
1297 }
1298
1299 display_entry_list(a, vec);
1300
1301 if (vec == &vector_history) {
1303 }
1304 }
1305
1306 if (vec != &vector_history) {
1308 }
1310
1311 return CLI_SUCCESS;
1312}
1313
1314static char *pjsip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1315{
1316 const char *what;
1317
1318 if (cmd == CLI_INIT) {
1319 e->command = "pjsip set history {on|off|clear}";
1320 e->usage =
1321 "Usage: pjsip set history {on|off|clear}\n"
1322 " Enables/disables/clears the PJSIP history.\n\n"
1323 " Enabling the history will start recording transmitted/received\n"
1324 " packets. Disabling the history will stop recording, but keep\n"
1325 " the already received packets. Clearing the history will wipe\n"
1326 " the received packets from memory.\n\n"
1327 " As the PJSIP history is maintained in memory, and includes\n"
1328 " all received/transmitted requests and responses, it should\n"
1329 " only be enabled for debugging purposes, and cleared when done.\n";
1330 return NULL;
1331 } else if (cmd == CLI_GENERATE) {
1332 return NULL;
1333 }
1334
1335 what = a->argv[e->args - 1]; /* Guaranteed to exist */
1336
1337 if (a->argc == e->args) {
1338 if (!strcasecmp(what, "on")) {
1339 enabled = 1;
1340 ast_cli(a->fd, "PJSIP History enabled\n");
1341 return CLI_SUCCESS;
1342 } else if (!strcasecmp(what, "off")) {
1343 enabled = 0;
1344 ast_cli(a->fd, "PJSIP History disabled\n");
1345 return CLI_SUCCESS;
1346 } else if (!strcasecmp(what, "clear")) {
1348 ast_cli(a->fd, "PJSIP History cleared\n");
1349 return CLI_SUCCESS;
1350 }
1351 }
1352
1353 return CLI_SHOWUSAGE;
1354}
1355
1356static pjsip_module logging_module = {
1357 .name = { "History Module", 14 },
1358 .priority = 0,
1359 .on_rx_request = history_on_rx_msg,
1360 .on_rx_response = history_on_rx_msg,
1361 .on_tx_request = history_on_tx_msg,
1362 .on_tx_response = history_on_tx_msg,
1363};
1364
1365static struct ast_cli_entry cli_pjsip[] = {
1366 AST_CLI_DEFINE(pjsip_set_history, "Enable/Disable PJSIP History"),
1367 AST_CLI_DEFINE(pjsip_show_history, "Display PJSIP History"),
1368};
1369
1370static int load_module(void)
1371{
1372 log_level = ast_logger_register_level("PJSIP_HISTORY");
1373 if (log_level < 0) {
1374 ast_log(LOG_WARNING, "Unable to register history log level\n");
1375 }
1376
1377 ast_pjproject_caching_pool_init(&cachingpool, &pj_pool_factory_default_policy, 0);
1378
1380
1383
1385}
1386
1387static int unload_module(void)
1388{
1391
1394
1396
1397 if (log_level != -1) {
1398 ast_logger_unregister_level("PJSIP_HISTORY");
1399 }
1400
1401 return 0;
1402}
1403
1405 .support_level = AST_MODULE_SUPPORT_EXTENDED,
1406 .load = load_module,
1407 .unload = unload_module,
1408 .load_pri = AST_MODPRI_APP_DEPEND,
1409 .requires = "res_pjsip",
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
static PGresult * result
Definition: cel_pgsql.c:84
static const char type[]
Definition: chan_ooh323.c:109
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
aco_option_type
The option types.
@ OPT_UINT_T
Type for default option handler for unsigned integers.
@ OPT_NOOP_T
Type for a default handler that should do nothing.
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_BOOLFLAG_T
Type for default option handler for bools (ast_true/ast_false) that are stored in a flag.
@ OPT_CHAR_ARRAY_T
Type for default option handler for character array strings.
@ OPT_SOCKADDR_T
Type for default handler for ast_sockaddrs.
@ OPT_INT_T
Type for default option handler for signed integers.
@ OPT_DOUBLE_T
Type for default option handler for doubles.
@ OPT_STRINGFIELD_T
Type for default option handler for stringfields.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:2099
int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to SIP servants and wait for it to complete.
Definition: res_pjsip.c:2165
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_log_dynamic_level(level,...)
Send a log message to a dynamically registered log level.
int ast_logger_register_level(const char *name)
Register a new logger level.
Definition: logger.c:2839
#define LOG_WARNING
void ast_logger_unregister_level(const char *name)
Unregister a previously registered logger level.
Definition: logger.c:2897
Asterisk locking-related definitions:
#define ast_mutex_unlock(a)
Definition: lock.h:190
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
#define ast_mutex_lock(a)
Definition: lock.h:189
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_APP_DEPEND
Definition: module.h:328
@ 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
Network socket handling.
void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp)
Destroy caching pool factory and all cached pools.
void ast_pjproject_caching_pool_init(pj_caching_pool *cp, const pj_pool_factory_policy *policy, pj_size_t max_capacity)
Initialize the caching pool factory.
void ast_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:133
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:117
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:2201
static struct operator allowed_operators[]
Our allowed operations.
static int enabled
Whether or not we are storing history.
static struct allowed_field * get_allowed_field(struct expression_token *token)
Determine if the expression token matches a field in allowed_fields.
static int evaluate_equal(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining equality.
static void * entry_get_addr(struct pjsip_history_entry *entry)
Callback to retrieve the entry's destination address.
static pj_caching_pool cachingpool
Pool factory used by pjlib to allocate memory.
#define HISTORY_INITIAL_SIZE
static void sprint_list_entry(struct pjsip_history_entry *entry, char *line, int len)
Format single line history entry.
static int evaluate_less_than(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining if one operand is less than another.
static int packet_number
Packet count.
static void * entry_get_sip_msg_request_method(struct pjsip_history_entry *entry)
Callback to retrieve the entry's SIP request method type.
static struct expression_token * expression_token_alloc(enum expression_token_type token_type, void *value)
Allocate an expression token.
static struct ast_cli_entry cli_pjsip[]
static void clear_history_entry_cb(struct pjsip_history_entry *entry)
Vector callback that releases the reference for the entry in a history vector.
static char * pjsip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static struct pjsip_history_entry * pjsip_history_entry_alloc(pjsip_msg *msg)
Create a pjsip_history_entry AO2 object.
static int safe_vector_cleanup(void *obj)
Cleanup routine for a history vector, serviced on a registered PJSIP thread.
static pj_bool_t history_on_rx_msg(pjsip_rx_data *rdata)
PJSIP callback when a SIP message is received.
static pjsip_module logging_module
static struct vector_history_t * filter_history(struct ast_cli_args *a)
Create a filtered history based on a user provided expression.
static void display_single_entry(struct ast_cli_args *a, struct pjsip_history_entry *entry)
Print a detailed view of a single entry in the history to the CLI.
static void pjsip_history_entry_dtor(void *obj)
AO2 destructor for pjsip_history_entry.
static int evaluate_greater_than(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining if one operand is greater than another.
#define APPEND_TO_OUTPUT(output, token)
static int evaluate_and(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining logical AND.
static int evaluate_not_equal(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining inequality.
static ast_mutex_t history_lock
Mutex that protects vector_history.
static struct expression_token * expression_token_free(struct expression_token *token)
Free an expression token and all others it references.
static void * entry_get_sip_msg_call_id(struct pjsip_history_entry *entry)
Callback to retrieve the entry's SIP Call-ID header.
expression_token_type
The type of token that has been parsed out of an expression.
@ TOKEN_TYPE_FIELD
@ TOKEN_TYPE_RESULT
@ TOKEN_TYPE_OPERATOR
static struct expression_token * build_expression_queue(struct ast_cli_args *a)
Build a reverse polish notation expression queue.
static char * pjsip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int evaluate_less_than_or_equal(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining if one operand is less than or equal to another.
static int log_level
Log level for history output.
static int evaluate_not(struct operator*op, enum aco_option_type type, void *operand)
Operator callback for determining logical NOT.
static int evaluate_history_entry(struct pjsip_history_entry *entry, struct expression_token *queue)
Evaluate a single entry in this history using a RPN expression.
static void * entry_get_timestamp(struct pjsip_history_entry *entry)
Callback to retrieve the entry's timestamp.
static int load_module(void)
static void display_entry_list(struct ast_cli_args *a, struct vector_history_t *vec)
Print a list of the entries to the CLI.
static struct allowed_field allowed_fields[]
The fields we allow.
static int unload_module(void)
static struct vector_history_t vector_history
static pj_status_t history_on_tx_msg(pjsip_tx_data *tdata)
PJSIP callback when a SIP message is transmitted.
static int clear_history_entries(void *obj)
Remove all entries from vector_history.
static struct operator left_paren
Operator token for a left parenthesis.
static int evaluate_like(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for regex 'like'.
static void * entry_get_number(struct pjsip_history_entry *entry)
Callback to retrieve the entry index number.
static int evaluate_or(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining logical OR.
static int evaluate_greater_than_or_equal(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Operator callback for determining if one operand is greater than or equal to another.
#define NULL
Definition: resample.c:96
A field that we understand and can perform operations on.
enum aco_option_type return_type
The type /c get_field returns.
const char * symbol
The representation of the field.
void *(*const get_field)(struct pjsip_history_entry *entry)
Function that returns the field from a pjsip_history_entry.
descriptor for a cli entry.
Definition: cli.h:171
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Definition: search.h:40
A token in the expression or an evaluated part of the expression.
struct expression_token * next
The next expression token in the queue.
char field[]
The field in the expression.
enum expression_token_type token_type
The type of value stored in the expression token.
int result
The result of an evaluated expression.
struct operator* op
An operator that evaluates expressions.
An operator that we understand in an expression.
int(*const evaluate)(struct operator*op, enum aco_option_type type, void *op_left, struct expression_token *op_right)
Evaluation function for binary operators.
int precedence
Precedence of the symbol.
int(*const evaluate_unary)(struct operator*op, enum aco_option_type type, void *operand)
Evaluation function for unary operators.
int operands
Number of operands the operator takes.
const char * symbol
Our operator's symbol.
int right_to_left
Non-zero if the operator is evaluated right-to-left.
An item in the history.
pjsip_msg * msg
The actual SIP message.
struct timeval timestamp
Time the packet was transmitted/received.
pj_pool_t * pool
Memory pool used to allocate msg.
int number
Packet number.
pj_sockaddr src
Source address.
int transmitted
Whether or not we transmitted the packet.
pj_sockaddr dst
Destination address.
The one and only history that we've captured.
int value
Definition: syslog.c:37
static struct test_val a
int ast_time_t_to_string(time_t time, char *buf, size_t length)
Converts to a string representation of a time_t as decimal seconds since the epoch....
Definition: time.c:152
time_t ast_string_to_time_t(const char *str)
Returns a time_t from a string containing seconds since the epoch.
Definition: time.c:163
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compress two struct timeval instances returning -1, 0, 1 if the first arg is smaller,...
Definition: time.h:137
#define AST_TIME_T_LEN
Definition: time.h:45
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int error(const char *format,...)
Definition: utils/frame.c:999
#define ast_assert(a)
Definition: utils.h:739
#define ARRAY_LEN(a)
Definition: utils.h:666
Vector container support.
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:625
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_REMOVE(vec, idx, preserve_ordered)
Remove an element from a vector by index.
Definition: vector.h:412
#define AST_VECTOR_PTR_FREE(vec)
Deallocates this vector pointer.
Definition: vector.h:189
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680