ARI: Rotate log channels.
[asterisk/asterisk.git] / res / ari / resource_asterisk.c
1 /* -*- C -*-
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 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 Implementation for ARI stubs.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_REGISTER_FILE()
33
34 #include "asterisk/ast_version.h"
35 #include "asterisk/buildinfo.h"
36 #include "asterisk/logger.h"
37 #include "asterisk/module.h"
38 #include "asterisk/paths.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/sorcery.h"
41 #include "resource_asterisk.h"
42
43 static void return_sorcery_object(struct ast_sorcery *sorcery, void *sorcery_obj,
44         struct ast_ari_response *response)
45 {
46         RAII_VAR(struct ast_json *, return_set, NULL, ast_json_unref);
47         struct ast_variable *change_set;
48         struct ast_variable *it_change_set;
49
50         return_set = ast_json_array_create();
51         if (!return_set) {
52                 ast_ari_response_alloc_failed(response);
53                 return;
54         }
55
56         /* Note that we can't use the sorcery JSON change set directly,
57          * as it will hand us back an Object (with fields), and we need
58          * a more generic representation of whatever the API call asked
59          * for, i.e., a list of tuples.
60          */
61         change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj);
62         if (!change_set) {
63                 ast_ari_response_alloc_failed(response);
64                 return;
65         }
66
67         for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
68                 struct ast_json *tuple;
69
70                 tuple = ast_json_pack("{s: s, s: s}",
71                         "attribute", it_change_set->name,
72                         "value", it_change_set->value);
73                 if (!tuple) {
74                         ast_variables_destroy(change_set);
75                         ast_ari_response_alloc_failed(response);
76                         return;
77                 }
78
79                 if (ast_json_array_append(return_set, tuple)) {
80                         ast_json_unref(tuple);
81                         ast_variables_destroy(change_set);
82                         ast_ari_response_alloc_failed(response);
83                         return;
84                 }
85         }
86         ast_variables_destroy(change_set);
87
88         ast_ari_response_ok(response, ast_json_ref(return_set));
89 }
90
91 void ast_ari_asterisk_get_object(struct ast_variable *headers,
92         struct ast_ari_asterisk_get_object_args *args,
93         struct ast_ari_response *response)
94 {
95         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
96         RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
97         RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
98
99
100         sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
101         if (!sorcery) {
102                 ast_ari_response_error(
103                         response, 404, "Not Found",
104                         "configClass '%s' not found",
105                         args->config_class);
106                 return;
107         }
108
109         object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
110         if (!object_type) {
111                 ast_ari_response_error(
112                         response, 404, "Not Found",
113                         "objectType '%s' not found",
114                         args->object_type);
115                 return;
116         }
117
118         sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
119         if (!sorcery_obj) {
120                 ast_ari_response_error(
121                         response, 404, "Not Found",
122                         "Object with id '%s' not found",
123                         args->id);
124                 return;
125         }
126
127         return_sorcery_object(sorcery, sorcery_obj, response);
128 }
129
130 void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response)
131 {
132         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
133         RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
134         RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
135         struct ast_json *fields;
136         struct ast_variable *update_set = NULL;
137         int created = 0;
138
139         sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
140         if (!sorcery) {
141                 ast_ari_response_error(
142                         response, 404, "Not Found",
143                         "configClass '%s' not found",
144                         args->config_class);
145                 return;
146         }
147
148         object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
149         if (!object_type) {
150                 ast_ari_response_error(
151                         response, 404, "Not Found",
152                         "objectType '%s' not found",
153                         args->object_type);
154                 return;
155         }
156
157         sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
158         if (!sorcery_obj) {
159                 ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id);
160                 sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id);
161                 if (!sorcery_obj) {
162                         ast_ari_response_alloc_failed(response);
163                         return;
164                 }
165
166                 created = 1;
167         } else {
168                 void *copy;
169
170                 copy = ast_sorcery_copy(sorcery, sorcery_obj);
171                 if (!copy) {
172                         ast_ari_response_alloc_failed(response);
173                         return;
174                 }
175
176                 ao2_ref(sorcery_obj, -1);
177                 sorcery_obj = copy;
178         }
179
180         fields = ast_json_object_get(args->fields, "fields");
181         if (!fields && !created) {
182                 /* Whoops. We need data. */
183                 ast_ari_response_error(
184                         response, 400, "Bad request",
185                         "Fields must be provided to update object '%s'",
186                         args->id);
187                 return;
188         } else if (fields) {
189                 size_t i;
190
191                 for (i = 0; i < ast_json_array_size(fields); i++) {
192                         struct ast_variable *new_var;
193                         struct ast_json *json_value = ast_json_array_get(fields, i);
194
195                         if (!json_value) {
196                                 continue;
197                         }
198
199                         new_var = ast_variable_new(
200                                 ast_json_string_get(ast_json_object_get(json_value, "attribute")),
201                                 ast_json_string_get(ast_json_object_get(json_value, "value")),
202                                 "");
203                         if (!new_var) {
204                                 ast_variables_destroy(update_set);
205                                 ast_ari_response_alloc_failed(response);
206                                 return;
207                         }
208                         ast_variable_list_append(&update_set, new_var);
209                 }
210         }
211
212         /* APPLY! Note that a NULL update_set is fine (and necessary), as it
213          * will force validation on a newly created object.
214          */
215         if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) {
216                 ast_variables_destroy(update_set);
217                 ast_ari_response_error(
218                         response, 400, "Bad request",
219                         "%s of object '%s' failed field value validation",
220                         created ? "Creation" : "Update",
221                         args->id);
222                 return;
223         }
224
225         ast_variables_destroy(update_set);
226
227         if (created) {
228                 if (ast_sorcery_create(sorcery, sorcery_obj)) {
229                         ast_ari_response_error(
230                                 response, 403, "Forbidden",
231                                 "Cannot create sorcery objects of type '%s'",
232                                 args->object_type);
233                         return;
234                 }
235         } else {
236                 if (ast_sorcery_update(sorcery, sorcery_obj)) {
237                         ast_ari_response_error(
238                                 response, 403, "Forbidden",
239                                 "Cannot update sorcery objects of type '%s'",
240                                 args->object_type);
241                         return;
242                 }
243         }
244
245         return_sorcery_object(sorcery, sorcery_obj, response);
246 }
247
248
249 void ast_ari_asterisk_delete_object(struct ast_variable *headers,
250         struct ast_ari_asterisk_delete_object_args *args,
251         struct ast_ari_response *response)
252 {
253         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
254         RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
255         RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
256
257         sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
258         if (!sorcery) {
259                 ast_ari_response_error(
260                         response, 404, "Not Found",
261                         "configClass '%s' not found",
262                         args->config_class);
263                 return;
264         }
265
266         object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
267         if (!object_type) {
268                 ast_ari_response_error(
269                         response, 404, "Not Found",
270                         "objectType '%s' not found",
271                         args->object_type);
272                 return;
273         }
274
275         sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
276         if (!sorcery_obj) {
277                 ast_ari_response_error(
278                         response, 404, "Not Found",
279                         "Object with id '%s' not found",
280                         args->id);
281                 return;
282         }
283
284         if (ast_sorcery_delete(sorcery, sorcery_obj)) {
285                 ast_ari_response_error(
286                         response, 403, "Forbidden",
287                         "Could not delete object with id '%s'",
288                         args->id);
289                 return;
290         }
291
292         ast_ari_response_no_content(response);
293 }
294
295
296 void ast_ari_asterisk_get_info(struct ast_variable *headers,
297         struct ast_ari_asterisk_get_info_args *args,
298         struct ast_ari_response *response)
299 {
300         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
301         int show_all = args->only_count == 0;
302         int show_build = show_all;
303         int show_system = show_all;
304         int show_config = show_all;
305         int show_status = show_all;
306         size_t i;
307         int res = 0;
308
309         for (i = 0; i < args->only_count; ++i) {
310                 if (strcasecmp("build", args->only[i]) == 0) {
311                         show_build = 1;
312                 } else if (strcasecmp("system", args->only[i]) == 0) {
313                         show_system = 1;
314                 } else if (strcasecmp("config", args->only[i]) == 0) {
315                         show_config = 1;
316                 } else if (strcasecmp("status", args->only[i]) == 0) {
317                         show_status = 1;
318                 } else {
319                         ast_log(LOG_WARNING, "Unrecognized info section '%s'\n",
320                                 args->only[i]);
321                 }
322         }
323
324         json = ast_json_object_create();
325
326         if (show_build) {
327                 res |= ast_json_object_set(json, "build",
328                         ast_json_pack(
329                                 "{ s: s, s: s, s: s,"
330                                 "  s: s, s: s, s: s }",
331
332                                 "os", ast_build_os,
333                                 "kernel", ast_build_kernel,
334                                 "machine", ast_build_machine,
335
336                                 "options", AST_BUILDOPTS,
337                                 "date", ast_build_date,
338                                 "user", ast_build_user));
339         }
340
341         if (show_system) {
342                 char eid_str[128];
343
344                 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
345
346                 res |= ast_json_object_set(json, "system",
347                         ast_json_pack("{ s: s, s: s }",
348                                 "version", ast_get_version(),
349                                 "entity_id", eid_str));
350         }
351
352         if (show_config) {
353                 struct ast_json *config = ast_json_pack(
354                         "{ s: s, s: s,"
355                         " s: { s: s, s: s } }",
356
357                         "name", ast_config_AST_SYSTEM_NAME,
358                         "default_language", ast_defaultlanguage,
359
360                         "setid",
361                         "user", ast_config_AST_RUN_USER,
362                         "group", ast_config_AST_RUN_GROUP);
363
364                 res |= ast_json_object_set(json, "config", config);
365
366                 if (ast_option_maxcalls) {
367                         res |= ast_json_object_set(config, "max_channels",
368                                 ast_json_integer_create(ast_option_maxcalls));
369                 }
370
371                 if (ast_option_maxfiles) {
372                         res |= ast_json_object_set(config, "max_open_files",
373                                 ast_json_integer_create(ast_option_maxfiles));
374                 }
375
376                 if (ast_option_maxload) {
377                         res |= ast_json_object_set(config, "max_load",
378                                 ast_json_real_create(ast_option_maxload));
379                 }
380         }
381
382         if (show_status) {
383                 res |= ast_json_object_set(json, "status",
384                         ast_json_pack("{ s: o, s: o }",
385                                 "startup_time",
386                                 ast_json_timeval(ast_startuptime, NULL),
387                                 "last_reload_time",
388                                 ast_json_timeval(ast_lastreloadtime, NULL)));
389         }
390
391         if (res != 0) {
392                 ast_ari_response_alloc_failed(response);
393                 return;
394         }
395
396         ast_ari_response_ok(response, ast_json_ref(json));
397 }
398
399 /*!
400  * \brief Process module information and append to a json array
401  * \param module Resource name
402  * \param description Resource description
403  * \param usecnt Resource use count
404  * \param status Resource running status
405  * \param like
406  * \param support_level Resource support level
407  * \param module_data_list Resource array
408  *
409  * \retval 0 if no resource exists
410  * \retval 1 if resource exists
411  */
412 static int process_module_list(const char *module, const char *description, int usecnt,
413                                const char *status, const char *like,
414                                enum ast_module_support_level support_level, void *module_data_list)
415 {
416         struct ast_json *module_info;
417
418         module_info = ast_json_pack("{s: s, s: s, s: i, s: s, s: s}",
419                               "name", module,
420                               "description", description,
421                               "use_count", usecnt,
422                               "status", status,
423                               "support_level", ast_module_support_level_to_string(support_level));
424         if (!module_info) {
425                 return 0;
426         }
427         ast_json_array_append(module_data_list, module_info);
428         return 1;
429 }
430
431 void ast_ari_asterisk_list_modules(struct ast_variable *headers,
432         struct ast_ari_asterisk_list_modules_args *args,
433         struct ast_ari_response *response)
434 {
435         struct ast_json *json;
436
437         json = ast_json_array_create();
438         ast_update_module_list_data(&process_module_list, NULL, json);
439
440         ast_ari_response_ok(response, json);
441 }
442
443 /*!
444  * \brief Identify module by name and process resource information
445  * \param module Resource name
446  * \param description Resource description
447  * \param usecnt Resource use count
448  * \param status Resource running status
449  * \param like
450  * \param support_level Resource support level
451  * \param data JSON body for resource
452  * \param condition Name to match resource to
453  *
454  * \retval 0 if no resource exists
455  * \retval 1 if resource exists
456  */
457 static int identify_module(const char *module, const char *description, int usecnt,
458                            const char *status, const char *like,
459                            enum ast_module_support_level support_level, void *data,
460                            const char *condition)
461 {
462         int json_obj_set = 0;
463
464         if (strcmp(condition, module) != 0) {
465                 return 0;
466         }
467
468         json_obj_set += ast_json_object_set(data, "name", ast_json_string_create(module));
469         json_obj_set += ast_json_object_set(data, "description", ast_json_string_create(description));
470         json_obj_set += ast_json_object_set(data, "use_count", ast_json_integer_create(usecnt));
471         json_obj_set += ast_json_object_set(data, "status", ast_json_string_create(status));
472         json_obj_set += ast_json_object_set(data, "support_level", ast_json_string_create(
473                                             ast_module_support_level_to_string(support_level)));
474
475         if (json_obj_set != 0) {
476                 return 0;
477         }
478
479         return 1;
480 }
481
482 void ast_ari_asterisk_get_module(struct ast_variable *headers,
483         struct ast_ari_asterisk_get_module_args *args,
484         struct ast_ari_response *response)
485 {
486         struct ast_json *json;
487         int module_retrieved = 0;
488
489         ast_assert(response != NULL);
490
491         if (!ast_module_check(args->module_name)) {
492                 ast_ari_response_error(
493                         response, 404, "Not Found",
494                         "Module could not be found in running modules");
495                 return;
496         }
497
498         json = ast_json_object_create();
499         if (!json) {
500                 ast_ari_response_alloc_failed(response);
501                 return;
502         }
503
504         module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json,
505                                                             args->module_name);
506         if (!module_retrieved) {
507                 ast_ari_response_error(
508                         response, 409, "Conflict",
509                         "Module information could not be retrieved");
510                 return;
511         }
512
513         ast_ari_response_ok(response, json);
514 }
515
516 void ast_ari_asterisk_load_module(struct ast_variable *headers,
517         struct ast_ari_asterisk_load_module_args *args,
518         struct ast_ari_response *response)
519 {
520         enum ast_module_load_result load_result;
521
522         ast_assert(response != NULL);
523
524         if (ast_module_check(args->module_name)) {
525                 ast_ari_response_error(
526                         response, 409, "Conflict",
527                         "Module is already loaded");
528                 return;
529         }
530
531         load_result = ast_load_resource(args->module_name);
532
533         if (load_result == AST_MODULE_LOAD_DECLINE) {
534                 ast_ari_response_error(
535                         response, 409, "Conflict",
536                         "Module load declined");
537                 return;
538         } else if (load_result == AST_MODULE_LOAD_SKIP) {
539                 ast_ari_response_error(
540                         response, 409, "Conflict",
541                         "Module was skipped");
542                 return;
543         } else if (load_result == AST_MODULE_LOAD_FAILURE) {
544                 ast_ari_response_error(
545                         response, 409, "Conflict",
546                         "Module could not be loaded properly");
547                 return;
548         }
549
550         ast_ari_response_no_content(response);
551 }
552
553 void ast_ari_asterisk_unload_module(struct ast_variable *headers,
554         struct ast_ari_asterisk_unload_module_args *args,
555         struct ast_ari_response *response)
556 {
557         int unload_result;
558         enum ast_module_unload_mode unload_mode = AST_FORCE_SOFT;
559
560         ast_assert(response != NULL);
561
562         if (!ast_module_check(args->module_name)) {
563                 ast_ari_response_error(
564                         response, 404, "Not Found",
565                         "Module not found in running modules");
566                 return;
567         }
568
569         unload_result = ast_unload_resource(args->module_name, unload_mode);
570
571         if (unload_result != 0) {
572                 ast_ari_response_error(
573                         response, 409, "Conflict",
574                         "Module could not be unloaded");
575                 return;
576         }
577
578         ast_ari_response_no_content(response);
579 }
580
581 void ast_ari_asterisk_reload_module(struct ast_variable *headers,
582         struct ast_ari_asterisk_reload_module_args *args,
583         struct ast_ari_response *response)
584 {
585         enum ast_module_reload_result reload_result;
586
587         ast_assert(response != NULL);
588
589         if (!ast_module_check(args->module_name)) {
590                 ast_ari_response_error(
591                         response, 404, "Not Found",
592                         "Module not found in running modules");
593                 return;
594         }
595
596         reload_result = ast_module_reload(args->module_name);
597
598         if (reload_result == AST_MODULE_RELOAD_NOT_FOUND) {
599                 ast_ari_response_error(
600                         response, 404, "Not Found",
601                         "Module could not be found");
602                 return;
603         } else if (reload_result == AST_MODULE_RELOAD_ERROR) {
604                 ast_ari_response_error(
605                         response, 409, "Conflict",
606                         "An unknown error occurred while reloading the module");
607                 return;
608         } else if (reload_result == AST_MODULE_RELOAD_IN_PROGRESS) {
609                 ast_ari_response_error(
610                         response, 409, "Conflict",
611                         "Another reload is currently in progress");
612                 return;
613         } else if (reload_result == AST_MODULE_RELOAD_UNINITIALIZED) {
614                 ast_ari_response_error(
615                         response, 409, "Conflict",
616                         "Module has not been initialized");
617                 return;
618         } else if (reload_result == AST_MODULE_RELOAD_NOT_IMPLEMENTED) {
619                 ast_ari_response_error(
620                         response, 409, "Conflict",
621                         "Module does not support reloading");
622                 return;
623         } else if (reload_result == AST_MODULE_RELOAD_QUEUED) {
624                 ast_ari_response_accepted(response);
625                 return;
626         }
627
628         ast_ari_response_no_content(response);
629 }
630
631 void ast_ari_asterisk_rotate_log(struct ast_variable *headers,
632         struct ast_ari_asterisk_rotate_log_args *args,
633         struct ast_ari_response *response)
634 {
635         int success;
636
637         ast_assert(response != NULL);
638
639         success = ast_logger_rotate_channel(args->log_channel_name);
640
641         if (success == 0) {
642                 ast_ari_response_error(
643                         response, 404, "Not Found",
644                         "Log channel does not exist");
645                 return;
646         } else if (success == -1) {
647                 ast_ari_response_error(
648                         response, 500, "Internal Server Error",
649                         "Allocation failed");
650                 return;
651         }
652
653         ast_ari_response_no_content(response);
654 }
655
656 void ast_ari_asterisk_get_global_var(struct ast_variable *headers,
657         struct ast_ari_asterisk_get_global_var_args *args,
658         struct ast_ari_response *response)
659 {
660         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
661         RAII_VAR(struct ast_str *, tmp, NULL, ast_free);
662
663         const char *value;
664
665         ast_assert(response != NULL);
666
667         if (ast_strlen_zero(args->variable)) {
668                 ast_ari_response_error(
669                         response, 400, "Bad Request",
670                         "Variable name is required");
671                 return;
672         }
673
674         tmp = ast_str_create(32);
675         if (!tmp) {
676                 ast_ari_response_alloc_failed(response);
677                 return;
678         }
679
680         value = ast_str_retrieve_variable(&tmp, 0, NULL, NULL, args->variable);
681
682         if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) {
683                 ast_ari_response_alloc_failed(response);
684                 return;
685         }
686
687         ast_ari_response_ok(response, ast_json_ref(json));
688 }
689
690 void ast_ari_asterisk_set_global_var(struct ast_variable *headers,
691         struct ast_ari_asterisk_set_global_var_args *args,
692         struct ast_ari_response *response)
693 {
694         ast_assert(response != NULL);
695
696         if (ast_strlen_zero(args->variable)) {
697                 ast_ari_response_error(
698                         response, 400, "Bad Request",
699                         "Variable name is required");
700                 return;
701         }
702
703         pbx_builtin_setvar_helper(NULL, args->variable, args->value);
704
705         ast_ari_response_no_content(response);
706 }