de1cecbc8e776a50c52cd808d3cee5d9a164d53b
[asterisk/asterisk.git] / res / res_pjproject.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@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 /*! \file
20  *
21  * \brief Bridge PJPROJECT logging to Asterisk logging.
22  * \author David M. Lee, II <dlee@digium.com>
23  *
24  * PJPROJECT logging doesn't exactly match Asterisk logging, but mapping the two is
25  * not too bad. PJPROJECT log levels are identified by a single int. Limits are
26  * not specified by PJPROJECT, but their implementation used 1 through 6.
27  *
28  * The mapping is as follows:
29  *  - 0: LOG_ERROR
30  *  - 1: LOG_ERROR
31  *  - 2: LOG_WARNING
32  *  - 3 and above: equivalent to ast_debug(level, ...) for res_pjproject.so
33  */
34
35 /*** MODULEINFO
36         <depend>pjproject</depend>
37         <support_level>core</support_level>
38  ***/
39
40 #include "asterisk.h"
41
42 ASTERISK_REGISTER_FILE()
43
44 #include <stdarg.h>
45 #include <pjlib.h>
46 #include <pjsip.h>
47 #include <pj/log.h>
48
49 #include "asterisk/logger.h"
50 #include "asterisk/module.h"
51 #include "asterisk/cli.h"
52 #include "asterisk/res_pjproject.h"
53 #include "asterisk/vector.h"
54
55 static pj_log_func *log_cb_orig;
56 static unsigned decor_orig;
57
58 static AST_VECTOR(buildopts, char *) buildopts;
59
60 static void log_forwarder(int level, const char *data, int len)
61 {
62         int ast_level;
63         /* PJPROJECT doesn't provide much in the way of source info */
64         const char * log_source = "pjproject";
65         int log_line = 0;
66         const char *log_func = "<?>";
67         int mod_level;
68
69         /* Lower number indicates higher importance */
70         switch (level) {
71         case 0: /* level zero indicates fatal error, according to docs */
72         case 1: /* 1 seems to be used for errors */
73                 ast_level = __LOG_ERROR;
74                 break;
75         case 2: /* 2 seems to be used for warnings and errors */
76                 ast_level = __LOG_WARNING;
77                 break;
78         default:
79                 ast_level = __LOG_DEBUG;
80
81                 /* For levels 3 and up, obey the debug level for res_pjproject */
82                 mod_level = ast_opt_dbg_module ?
83                         ast_debug_get_by_module("res_pjproject") : 0;
84                 if (option_debug < level && mod_level < level) {
85                         return;
86                 }
87                 break;
88         }
89
90         /* PJPROJECT uses indention to indicate function call depth. We'll prepend
91          * log statements with a tab so they'll have a better shot at lining
92          * up */
93         ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data);
94 }
95
96 static void capture_buildopts_cb(int level, const char *data, int len)
97 {
98         if (strstr(data, "Teluu") || strstr(data, "Dumping")) {
99                 return;
100         }
101
102         AST_VECTOR_ADD_SORTED(&buildopts, ast_strdup(ast_skip_blanks(data)), strcmp);
103 }
104
105 int ast_pjproject_get_buildopt(char *option, char *format_string, ...)
106 {
107         int res = 0;
108         char *format_temp;
109         int i;
110
111         format_temp = ast_alloca(strlen(option) + strlen(" : ") + strlen(format_string) + 1);
112         sprintf(format_temp, "%s : %s", option, format_string);
113
114         for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
115                 va_list arg_ptr;
116                 va_start(arg_ptr, format_string);
117                 res = vsscanf(AST_VECTOR_GET(&buildopts, i), format_temp, arg_ptr);
118                 va_end(arg_ptr);
119                 if (res) {
120                         break;
121                 }
122         }
123
124         return res;
125 }
126
127 void ast_pjproject_ref(void)
128 {
129         ast_module_ref(ast_module_info->self);
130 }
131
132 void ast_pjproject_unref(void)
133 {
134         ast_module_unref(ast_module_info->self);
135 }
136
137 static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
138 {
139         int i;
140
141         switch (cmd) {
142         case CLI_INIT:
143                 e->command = "pjproject show buildopts";
144                 e->usage =
145                         "Usage: pjproject show buildopts\n"
146                         "       Show the compile time config of the pjproject that Asterisk is\n"
147                         "       running against.\n";
148                 return NULL;
149         case CLI_GENERATE:
150                 return NULL;
151         }
152
153         ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n");
154
155         for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
156                 ast_cli(a->fd, "%s\n", AST_VECTOR_GET(&buildopts, i));
157         }
158
159         return CLI_SUCCESS;
160 }
161
162 static struct ast_cli_entry pjproject_cli[] = {
163         AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
164 };
165
166 static int load_module(void)
167 {
168         ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
169
170         pj_init();
171
172         decor_orig = pj_log_get_decor();
173         log_cb_orig = pj_log_get_log_func();
174
175         if (AST_VECTOR_INIT(&buildopts, 64)) {
176                 return AST_MODULE_LOAD_DECLINE;
177         }
178
179         /*
180          * On startup, we want to capture the dump once and store it.
181          */
182         pj_log_set_log_func(capture_buildopts_cb);
183         pj_log_set_decor(0);
184         pj_dump_config();
185         pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT);
186         pj_log_set_log_func(log_forwarder);
187
188         ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
189
190         return AST_MODULE_LOAD_SUCCESS;
191 }
192
193 #define NOT_EQUALS(a, b) (a != b)
194
195 static int unload_module(void)
196 {
197         ast_cli_unregister_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
198         pj_log_set_log_func(log_cb_orig);
199         pj_log_set_decor(decor_orig);
200
201         AST_VECTOR_REMOVE_CMP_UNORDERED(&buildopts, NULL, NOT_EQUALS, ast_free);
202         AST_VECTOR_FREE(&buildopts);
203
204         ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n");
205
206         pj_shutdown();
207
208         return 0;
209 }
210
211 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
212         .support_level = AST_MODULE_SUPPORT_CORE,
213         .load = load_module,
214         .unload = unload_module,
215         .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
216 );