Optionally display the value of several variables within the Status command.
[asterisk/asterisk.git] / funcs / func_devstate.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007, Digium, Inc.
5  *
6  * Russell Bryant <russell@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 Manually controlled blinky lights
22  *
23  * \author Russell Bryant <russell@digium.com> 
24  *
25  * \ingroup functions
26  *
27  * \note Props go out to Ahrimanes in \#asterisk for requesting this at 4:30 AM
28  *       when I couldn't sleep.  :)
29  */
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/module.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/devicestate.h"
41 #include "asterisk/cli.h"
42 #include "asterisk/astdb.h"
43 #include "asterisk/app.h"
44
45 static const char astdb_family[] = "CustomDevstate";
46
47 static int devstate_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
48 {
49         ast_copy_string(buf, ast_devstate_str(ast_device_state(data)), len);
50
51         return 0;
52 }
53
54 static int devstate_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
55 {
56         size_t len = strlen("Custom:");
57         enum ast_device_state state_val;
58
59         if (strncasecmp(data, "Custom:", len)) {
60                 ast_log(LOG_WARNING, "The DEVICE_STATE function can only be used to set 'Custom:' device state!\n");
61                 return -1;
62         }
63         data += len;
64         if (ast_strlen_zero(data)) {
65                 ast_log(LOG_WARNING, "DEVICE_STATE function called with no custom device name!\n");
66                 return -1;
67         }
68
69         state_val = ast_devstate_val(value);
70
71         if (state_val == AST_DEVICE_UNKNOWN) {
72                 ast_log(LOG_ERROR, "DEVICE_STATE function given invalid state value '%s'\n", value);
73                 return -1;
74         }
75
76         ast_db_put(astdb_family, data, value);
77
78         ast_devstate_changed(state_val, "Custom:%s", data);
79
80         return 0;
81 }
82
83 enum {
84         HINT_OPT_NAME = (1 << 0),
85 };
86
87 AST_APP_OPTIONS(hint_options, BEGIN_OPTIONS
88         AST_APP_OPTION('n', HINT_OPT_NAME),
89 END_OPTIONS );
90
91 static int hint_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
92 {
93         char *exten, *context;
94         AST_DECLARE_APP_ARGS(args,
95                 AST_APP_ARG(exten);
96                 AST_APP_ARG(options);
97         );
98         struct ast_flags opts = { 0, };
99         int res;
100
101         if (ast_strlen_zero(data)) {
102                 ast_log(LOG_WARNING, "The HINT function requires an extension\n");
103                 return -1;
104         }
105
106         AST_STANDARD_APP_ARGS(args, data);
107
108         if (ast_strlen_zero(args.exten)) {
109                 ast_log(LOG_WARNING, "The HINT function requires an extension\n");
110                 return -1;
111         }
112
113         context = exten = args.exten;
114         strsep(&context, "@");
115         if (ast_strlen_zero(context))
116                 context = "default";
117
118         if (!ast_strlen_zero(args.options))
119                 ast_app_parse_options(hint_options, &opts, NULL, args.options);
120
121         if (ast_test_flag(&opts, HINT_OPT_NAME))
122                 res = ast_get_hint(NULL, 0, buf, len, chan, context, exten);
123         else
124                 res = ast_get_hint(buf, len, NULL, 0, chan, context, exten);
125
126         return !res; /* ast_get_hint returns non-zero on success */
127 }
128
129 static enum ast_device_state custom_devstate_callback(const char *data)
130 {
131         char buf[256] = "";
132
133         ast_db_get(astdb_family, data, buf, sizeof(buf));
134
135         return ast_devstate_val(buf);
136 }
137
138 static char *handle_cli_funcdevstate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
139 {
140         struct ast_db_entry *db_entry, *db_tree;
141
142         switch (cmd) {
143         case CLI_INIT:
144                 e->command = "funcdevstate list";
145                 e->usage =
146                         "Usage: funcdevstate list\n"
147                         "       List all custom device states that have been set by using\n"
148                         "       the DEVICE_STATE dialplan function.\n";
149                 return NULL;
150         case CLI_GENERATE:
151                 return NULL;
152         }
153
154         if (a->argc != e->args)
155                 return CLI_SHOWUSAGE;
156
157         ast_cli(a->fd, "\n"
158                 "---------------------------------------------------------------------\n"
159                 "--- Custom Device States --------------------------------------------\n"
160                 "---------------------------------------------------------------------\n"
161                 "---\n");
162
163         db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
164         for (; db_entry; db_entry = db_entry->next) {
165                 const char *dev_name = strrchr(db_entry->key, '/') + 1;
166                 if (dev_name <= (const char *) 1)
167                         continue;
168                 ast_cli(a->fd, "--- Name: 'Custom:%s'  State: '%s'\n"
169                                "---\n", dev_name, db_entry->data);
170         }
171         ast_db_freetree(db_tree);
172         db_tree = NULL;
173
174         ast_cli(a->fd,
175                 "---------------------------------------------------------------------\n"
176                 "---------------------------------------------------------------------\n"
177                 "\n");
178
179         return CLI_SUCCESS;
180 }
181
182 static char *handle_cli_devstate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
183 {
184         struct ast_db_entry *db_entry, *db_tree;
185
186         switch (cmd) {
187         case CLI_INIT:
188                 e->command = "devstate list";
189                 e->usage =
190                         "Usage: devstate list\n"
191                         "       List all custom device states that have been set by using\n"
192                         "       the DEVICE_STATE dialplan function.\n";
193                 return NULL;
194         case CLI_GENERATE:
195                 return NULL;
196         }
197
198         if (a->argc != e->args)
199                 return CLI_SHOWUSAGE;
200
201         ast_cli(a->fd, "\n"
202                 "---------------------------------------------------------------------\n"
203                 "--- Custom Device States --------------------------------------------\n"
204                 "---------------------------------------------------------------------\n"
205                 "---\n");
206
207         db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
208         for (; db_entry; db_entry = db_entry->next) {
209                 const char *dev_name = strrchr(db_entry->key, '/') + 1;
210                 if (dev_name <= (const char *) 1)
211                         continue;
212                 ast_cli(a->fd, "--- Name: 'Custom:%s'  State: '%s'\n"
213                                "---\n", dev_name, db_entry->data);
214         }
215         ast_db_freetree(db_tree);
216         db_tree = NULL;
217
218         ast_cli(a->fd,
219                 "---------------------------------------------------------------------\n"
220                 "---------------------------------------------------------------------\n"
221                 "\n");
222
223         return CLI_SUCCESS;
224 }
225
226 static char *handle_cli_devstate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
227 {
228     size_t len;
229         const char *dev, *state;
230         enum ast_device_state state_val;
231
232         switch (cmd) {
233         case CLI_INIT:
234                 e->command = "devstate change";
235                 e->usage =
236                         "Usage: devstate change <device> <state>\n"
237                         "       Change a custom device to a new state.\n"
238                         "       The possible values for the state are:\n"
239                         "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
240                         "RINGINUSE | ONHOLD\n",
241                         "\n"
242                         "Examples:\n"
243                         "       devstate change Custom:mystate1 INUSE\n"
244                         "       devstate change Custom:mystate1 NOT_INUSE\n"
245                         "       \n";
246                 return NULL;
247         case CLI_GENERATE:
248         {
249                 static char * const cmds[] = { "UNKNOWN", "NOT_INUSE", "INUSE", "BUSY",
250                         "UNAVAILALBE", "RINGING", "RINGINUSE", "ONHOLD", NULL };
251
252                 if (a->pos == e->args + 1)
253                         return ast_cli_complete(a->word, cmds, a->n);
254
255                 return NULL;
256         }
257         }
258
259         if (a->argc != e->args + 2)
260                 return CLI_SHOWUSAGE;
261
262         len = strlen("Custom:");
263         dev = a->argv[e->args];
264         state = a->argv[e->args + 1];
265
266         if (strncasecmp(dev, "Custom:", len)) {
267                 ast_cli(a->fd, "The devstate command can only be used to set 'Custom:' device state!\n");
268                 return CLI_FAILURE;
269         }
270
271         dev += len;
272         if (ast_strlen_zero(dev))
273                 return CLI_SHOWUSAGE;
274
275         state_val = ast_devstate_val(state);
276
277         if (state_val == AST_DEVICE_UNKNOWN)
278                 return CLI_SHOWUSAGE;
279
280         ast_cli(a->fd, "Changing %s to %s\n", dev, state);
281
282         ast_db_put(astdb_family, dev, state);
283
284         ast_devstate_changed(state_val, "Custom:%s", dev);
285
286         return CLI_SUCCESS;
287 }
288
289 static struct ast_cli_entry cli_funcdevstate_list_deprecated = AST_CLI_DEFINE(handle_cli_funcdevstate_list, "List currently known custom device states");
290 static struct ast_cli_entry cli_funcdevstate[] = {
291         AST_CLI_DEFINE(handle_cli_devstate_list, "List currently known custom device states", .deprecate_cmd = &cli_funcdevstate_list_deprecated),
292         AST_CLI_DEFINE(handle_cli_devstate_change, "Change a custom device state"),
293 };
294
295 static struct ast_custom_function devstate_function = {
296         .name = "DEVICE_STATE",
297         .synopsis = "Get or Set a device state",
298         .syntax = "DEVICE_STATE(device)",
299         .desc =
300         "  The DEVICE_STATE function can be used to retrieve the device state from any\n"
301         "device state provider.  For example:\n"
302         "   NoOp(SIP/mypeer has state ${DEVICE_STATE(SIP/mypeer)})\n"
303         "   NoOp(Conference number 1234 has state ${DEVICE_STATE(MeetMe:1234)})\n"
304         "\n"
305         "  The DEVICE_STATE function can also be used to set custom device state from\n"
306         "the dialplan.  The \"Custom:\" prefix must be used.  For example:\n"
307         "  Set(DEVICE_STATE(Custom:lamp1)=BUSY)\n"
308         "  Set(DEVICE_STATE(Custom:lamp2)=NOT_INUSE)\n"
309         "You can subscribe to the status of a custom device state using a hint in\n"
310         "the dialplan:\n"
311         "  exten => 1234,hint,Custom:lamp1\n"
312         "\n"
313         "  The possible values for both uses of this function are:\n"
314         "UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
315         "RINGINUSE | ONHOLD\n",
316         .read = devstate_read,
317         .write = devstate_write,
318 };
319
320 static struct ast_custom_function hint_function = {
321         .name = "HINT",
322         .synopsis = "Get the devices set for a dialplan hint",
323         .syntax = "HINT(extension[@context][|options])",
324         .desc =
325         "  The HINT function can be used to retrieve the list of devices that are\n"
326         "mapped to a dialplan hint.  For example:\n"
327         "   NoOp(Hint for Extension 1234 is ${HINT(1234)})\n"
328         "Options:\n"
329         "   'n' - Retrieve name on the hint instead of list of devices\n"
330         "",
331         .read = hint_read,
332 };
333
334 static int unload_module(void)
335 {
336         int res = 0;
337
338         res |= ast_custom_function_unregister(&devstate_function);
339         res |= ast_custom_function_unregister(&hint_function);
340         res |= ast_devstate_prov_del("Custom");
341         res |= ast_cli_unregister_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
342
343         return res;
344 }
345
346 static int load_module(void)
347 {
348         int res = 0;
349         struct ast_db_entry *db_entry, *db_tree;
350
351         /* Populate the device state cache on the system with all of the currently
352          * known custom device states. */
353         db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
354         for (; db_entry; db_entry = db_entry->next) {
355                 const char *dev_name = strrchr(db_entry->key, '/') + 1;
356                 if (dev_name <= (const char *) 1)
357                         continue;
358                 ast_devstate_changed(ast_devstate_val(db_entry->data),
359                         "Custom:%s\n", dev_name);
360         }
361         ast_db_freetree(db_tree);
362         db_tree = NULL;
363
364         res |= ast_custom_function_register(&devstate_function);
365         res |= ast_custom_function_register(&hint_function);
366         res |= ast_devstate_prov_add("Custom", custom_devstate_callback);
367         res |= ast_cli_register_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
368
369         return res;
370 }
371
372 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gets or sets a device state in the dialplan");