Asterisk - The Open Source Telephony Project  18.5.0
logger_category.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2020, Sangoma Technologies Corporation
5  *
6  * Kevin Harwell <[email protected]>
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 #include "asterisk.h"
20 
21 #include "asterisk/cli.h"
22 #include "asterisk/conversions.h"
24 #include "asterisk/vector.h"
25 
26 struct category_t {
27  int sublevel;
28  uintmax_t id;
29  char name[0];
30 };
31 
32 AST_VECTOR_RW(categories_t, struct category_t *);
33 
35  int type;
36  int sublevel;
37  uintmax_t id_pool;
38  uintmax_t state;
39  struct categories_t categories;
40 };
41 
42 /*! \brief Retrieve the next available id.
43  *
44  * Ids must be a power of 2. This allows for fast lookup, and "or'ing" of ids
45  * in order to permit multiple categories in a log statement.
46  */
47 static uintmax_t get_next_id(struct categories_level_t *level)
48 {
49  if (level->id_pool == 0) {
50  level->id_pool = 1;
51  } else if (level->id_pool >= (UINTMAX_MAX / 2)) {
52  /* No more ids left*/
53  return 0;
54  } else {
55  level->id_pool <<= 1;
56  }
57 
58  return level->id_pool;
59 }
60 
61 static int cmp_by_name(const struct category_t *category, const char *name)
62 {
63  return !strcmp(category->name, name);
64 }
65 
66 static uintmax_t category_register(struct categories_level_t *level, const char *name)
67 {
68  int i;
69  struct category_t *category;
70 
72 
73  i = AST_VECTOR_GET_INDEX(&level->categories, name, cmp_by_name);
74  if (i >= 0) {
76  ast_log(LOG_ERROR, "Cannot register logger category '%s'. "
77  "Name already used for type.\n", name);
78  return 0;
79  }
80 
81  category = ast_calloc(1, sizeof(*category) + strlen(name) + 1);
82  if (!category) {
84  return 0;
85  }
86 
87  category->id = get_next_id(level);
89  strcpy(category->name, name); /* Safe */
90 
91  if (AST_VECTOR_APPEND(&level->categories, category)) {
93  ast_log(LOG_ERROR, "Cannot register logger category '%s'. "
94  "Unable to append.\n", name);
95  return 0;
96  }
97 
99  return category->id;
100 }
101 
102 static int category_unregister(struct categories_level_t *level, const char *name)
103 {
104  int res;
105 
109 
110  return res;
111 }
112 
113 static int casecmp_by_name(const struct category_t *category, const char *name)
114 {
115  return !strcasecmp(category->name, name);
116 }
117 
118 static int category_set_sublevel(struct category_t *category, struct categories_level_t *level,
119  const char *name, int sublevel)
120 {
121  int locked = 0;
122 
123  if (!category) {
124  struct category_t **obj;
125 
126  if (!name) {
127  return -1;
128  }
129 
130  locked = !AST_VECTOR_RW_WRLOCK(&level->categories);
131  if (!locked) {
132  return -1;
133  }
134 
135  obj = AST_VECTOR_GET_CMP(&level->categories, name, casecmp_by_name);
136  if (!obj) {
138  return -1;
139  }
140 
141  category = *obj;
142  }
143 
144  category->sublevel = sublevel;
145 
146  if (category->sublevel == AST_LOG_CATEGORY_DISABLED) {
147  level->state &= ~category->id;
148  } else {
149  level->state |= category->id;
150  }
151 
152  if (locked) {
154  }
155 
156  return 0;
157 }
158 
160  const char * const *names, size_t size, int default_sublevel)
161 {
162  int i;
163 
164  if (!names || !size) {
165  level->state = default_sublevel;
168  level, NULL, default_sublevel);
170  return 0;
171  }
172 
173  for (i = 0; i < size; ++i) {
174  const char *sublevel;
175  int num = default_sublevel;
176 
177  sublevel = strchr(names[i], ':');
178  if (sublevel) {
179  size_t len = ++sublevel - names[i];
180  char name[len];
181 
182  if (*sublevel && ast_str_to_int(sublevel, &num)) {
183  continue;
184  }
185 
186  ast_copy_string(name, names[i], len);
187  category_set_sublevel(NULL, level, name, num);
188  } else {
189  category_set_sublevel(NULL, level, names[i], num);
190  }
191  }
192 
193  return 0;
194 }
195 
196 static char *category_complete(struct categories_level_t *level, const char * const *argv,
197  int argc, const char *word, int state)
198 {
199  int wordlen = strlen(word);
200  int which = 0;
201  int i, j;
202 
204 
205  if (argc == AST_VECTOR_SIZE(&level->categories)) {
207  return NULL;
208  }
209 
210  for (i = 0; i < AST_VECTOR_SIZE(&level->categories); ++i) {
211  struct category_t *category = AST_VECTOR_GET(&level->categories, i);
212 
213  if (!strncasecmp(word, category->name, wordlen) && (++which > state)) {
214  /* Check to see if one is already been included */
215  for (j = 0; j < argc; ++j) {
216  if (!strncasecmp(category->name, argv[j], strlen(category->name))) {
217  break;
218  }
219  }
220 
221  if (j != argc) {
222  continue;
223  }
224 
225  if (state != -1) {
227  return ast_strdup(category->name);
228  }
229 
230  if (ast_cli_completion_add(ast_strdup(category->name))) {
232  return NULL;
233  }
234  }
235  }
236 
238  return NULL;
239 }
240 
241 static int category_is_allowed(int sublevel, struct categories_level_t *level, uintmax_t ids)
242 {
243  /* Check level, and potentially allow but only if there is a matching state enabled */
244  if (ids & level->state) {
245  int i;
246 
247  if (sublevel == AST_LOG_CATEGORY_ENABLED || sublevel == 0) {
248  /* If at least one id is enabled then always allow these sublevels */
249  return 1;
250  }
251 
253  for (i = 0; i < AST_VECTOR_SIZE(&level->categories); ++i) {
254  struct category_t *category = AST_VECTOR_GET(&level->categories, i);
255 
256  /*
257  * If there is at least one matching category, and that category is enabled
258  * or its sub-level is at or above the given sub-level then allow.
259  */
260  if ((category->id & ids) && category->sublevel != AST_LOG_CATEGORY_DISABLED &&
261  (category->sublevel == AST_LOG_CATEGORY_ENABLED || category->sublevel >= sublevel)) {
263  return 1;
264  }
265  }
267  }
268 
269  return 0;
270 }
271 
273  .type = __LOG_DEBUG,
274  .sublevel = 0,
275  .id_pool = 0,
276  .state = 0,
277 };
278 
279 uintmax_t ast_debug_category_register(const char *name)
280 {
281  return category_register(&debug_categories, name);
282 }
283 
285 {
286  return category_unregister(&debug_categories, name);
287 }
288 
290 {
291  return category_set_sublevel(NULL, &debug_categories, name, sublevel);
292 }
293 
294 int ast_debug_category_set_sublevels(const char * const *names,
295  size_t size, int default_sublevel)
296 {
297  return category_set_sublevels(&debug_categories, names, size, default_sublevel);
298 }
299 
300 char *ast_debug_category_complete(const char * const *argv, int argc,
301  const char *word, int state)
302 {
303  return category_complete(&debug_categories, argv, argc, word, state);
304 }
305 
307 {
308  return category_is_allowed(sublevel, &debug_categories, ids);
309 }
310 
312 {
313  AST_VECTOR_RW_FREE(&debug_categories.categories);
314  return 0;
315 }
316 
318 {
319  if (AST_VECTOR_RW_INIT(&debug_categories.categories, 10)) {
320  return -1;
321  }
322 
323  return 0;
324 }
#define AST_VECTOR_RW_INIT(vec, size)
Initialize a vector with a read/write lock.
Definition: vector.h:158
#define AST_VECTOR_GET_INDEX(vec, value, cmp)
Get the 1st index from a vector that matches the given comparison.
Definition: vector.h:721
Asterisk main include file. File version handling, generic pbx functions.
#define __LOG_DEBUG
Definition: logger.h:240
static int casecmp_by_name(const struct category_t *category, const char *name)
#define AST_VECTOR_REMOVE_CMP_UNORDERED(vec, value, cmp, cleanup)
Remove an element from a vector that matches the given comparison.
Definition: vector.h:488
static int category_is_allowed(int sublevel, struct categories_level_t *level, uintmax_t ids)
#define AST_VECTOR_RW_UNLOCK(vec)
Unlock vector.
Definition: vector.h:900
static int cmp_by_name(const struct category_t *category, const char *name)
static int category_set_sublevels(struct categories_level_t *level, const char *const *names, size_t size, int default_sublevel)
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR_RW_RDLOCK(vec)
Obtain read lock on vector.
Definition: vector.h:880
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
struct categories_t categories
uintmax_t ast_debug_category_register(const char *name)
Register a debug level logger category.
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
uintmax_t id
#define AST_VECTOR_RW_FREE(vec)
Deallocates this locked vector.
Definition: vector.h:202
AST_VECTOR_RW(categories_t, struct category_t *)
Conversion utility functions.
static struct categories_level_t debug_categories
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Definition: conversions.c:44
int ast_logger_category_load(void)
Load/Initialize system wide logger category functionality.
#define LOG_ERROR
Definition: logger.h:285
struct association categories[]
#define AST_LOG_CATEGORY_DISABLED
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_free(a)
Definition: astmm.h:182
int ast_debug_category_set_sublevels(const char *const *names, size_t size, int default_sublevel)
Set one or more debug category&#39;s sublevel.
#define AST_VECTOR_RW_WRLOCK(vec)
Obtain write lock on vector.
Definition: vector.h:890
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
Vector container support.
static int category_unregister(struct categories_level_t *level, const char *name)
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:733
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
int ast_logger_category_unload(void)
Unload system wide logger category functionality.
char * ast_debug_category_complete(const char *const *argv, int argc, const char *word, int state)
Add a unique (no duplicates) result to a request for completion for debug categories.
char name[0]
#define AST_LOG_CATEGORY_ENABLED
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2726
int ast_debug_category_set_sublevel(const char *name, int sublevel)
Set the debug category&#39;s sublevel.
static int category_set_sublevel(struct category_t *category, struct categories_level_t *level, const char *name, int sublevel)
static uintmax_t category_register(struct categories_level_t *level, const char *name)
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
int ast_debug_category_is_allowed(int sublevel, uintmax_t ids)
Check if a debug category is enabled, and allowed to output.
static uintmax_t get_next_id(struct categories_level_t *level)
Retrieve the next available id.
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:865
short word
int ast_debug_category_unregister(const char *name)
Un-register a debug level logger category.
static char * category_complete(struct categories_level_t *level, const char *const *argv, int argc, const char *word, int state)