Totally revamp thread debugging to support locating and removing deadlocks
[asterisk/asterisk.git] / res / res_indications.c
1 /** @file res_indications.c 
2  *
3  * Asterisk -- A telephony toolkit for Linux.
4  *
5  * Load the indications
6  * 
7  * Copyright (C) 2002, Pauline Middelink
8  *
9  * Pauline Middelink <middelink@polyware.nl>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  *
14  * Load the country specific dialtones into the asterisk PBX.
15  */
16  
17 #include <unistd.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <stdlib.h>
21 #include <pthread.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <asterisk/lock.h>
26 #include <asterisk/file.h>
27 #include <asterisk/cli.h>
28 #include <asterisk/logger.h>
29 #include <asterisk/config.h>
30 #include <asterisk/channel.h>
31 #include <asterisk/pbx.h>
32 #include <asterisk/module.h>
33 #include <asterisk/translate.h>
34 #include <asterisk/indications.h>
35
36
37 // Globals
38 static const char dtext[] = "Indications Configuration";
39 static const char config[] = "indications.conf";
40
41 /*
42  * Help for commands provided by this module ...
43  */
44 static char help_add_indication[] =
45 "Usage: add indication <country> <indication> \"<tonelist>\"\n"
46 "       Add the given indication to the country.\n";
47
48 static char help_remove_indication[] =
49 "Usage: remove indication <country> <indication>\n"
50 "       Remove the given indication from the country.\n";
51
52 static char help_show_indications[] =
53 "Usage: show indications [<country> ...]\n"
54 "       Show either a condensed for of all country/indications, or the\n"
55 "       indications for the specified countries.\n";
56
57 /*
58  * Implementation of functions provided by this module
59  */
60
61 /*
62  * ADD INDICATION command stuff
63  */
64 static int handle_add_indication(int fd, int argc, char *argv[])
65 {
66         struct tone_zone *tz;
67         int created_country = 0;
68         if (argc != 5) return RESULT_SHOWUSAGE;
69
70         tz = ast_get_indication_zone(argv[2]);
71         if (!tz) {
72                 /* country does not exist, create it */
73                 ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n",argv[2]);
74
75                 tz = malloc(sizeof(struct tone_zone));
76                 if (!tz) {
77                         ast_log(LOG_WARNING, "Out of memory\n");
78                         return -1;
79                 }
80                 memset(tz,0,sizeof(struct tone_zone));
81                 strncpy(tz->country,argv[2],sizeof(tz->country)-1);
82                 if (ast_register_indication_country(tz)) {
83                         ast_log(LOG_WARNING, "Unable to register new country\n");
84                         free(tz);
85                         return -1;
86                 }
87                 created_country = 1;
88         }
89         if (ast_register_indication(tz,argv[3],argv[4])) {
90                 ast_log(LOG_WARNING, "Unable to register indication %s/%s\n",argv[2],argv[3]);
91                 if (created_country)
92                         ast_unregister_indication_country(argv[2]);
93                 return -1;
94         }
95         return 0;
96 }
97
98 /*
99  * REMOVE INDICATION command stuff
100  */
101 static int handle_remove_indication(int fd, int argc, char *argv[])
102 {
103         struct tone_zone *tz;
104         if (argc != 3 && argc != 4) return RESULT_SHOWUSAGE;
105
106         if (argc == 3) {
107                 /* remove entiry country */
108                 if (ast_unregister_indication_country(argv[2])) {
109                         ast_log(LOG_WARNING, "Unable to unregister indication country %s\n",argv[2]);
110                         return -1;
111                 }
112                 return 0;
113         }
114
115         tz = ast_get_indication_zone(argv[2]);
116         if (!tz) {
117                 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n",argv[2],argv[3]);
118                 return -1;
119         }
120         if (ast_unregister_indication(tz,argv[3])) {
121                 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n",argv[2],argv[3]);
122                 return -1;
123         }
124         return 0;
125 }
126
127 /*
128  * SHOW INDICATIONS command stuff
129  */
130 static int handle_show_indications(int fd, int argc, char *argv[])
131 {
132         struct tone_zone *tz;
133         char buf[256];
134         int found_country = 0;
135
136         if (ast_mutex_lock(&tzlock)) {
137                 ast_log(LOG_WARNING, "Unable to lock tone_zones list\n");
138                 return 0;
139         }
140         if (argc == 2) {
141                 /* no arguments, show a list of countries */
142                 ast_cli(fd,"Country Alias   Description\n"
143                            "===========================\n");
144                 for (tz=tone_zones; tz; tz=tz->next) {
145                         ast_cli(fd,"%-7.7s %-7.7s %s\n", tz->country, tz->alias, tz->description);
146                 }
147                 ast_mutex_unlock(&tzlock);
148                 return 0;
149         }
150         /* there was a request for specific country(ies), lets humor them */
151         for (tz=tone_zones; tz; tz=tz->next) {
152                 int i,j;
153                 for (i=2; i<argc; i++) {
154                         if (strcasecmp(tz->country,argv[i])==0 &&
155                             !tz->alias[0]) {
156                                 struct tone_zone_sound* ts;
157                                 if (!found_country) {
158                                         found_country = 1;
159                                         ast_cli(fd,"Country Indication      PlayList\n"
160                                                    "=====================================\n");
161                                 }
162                                 j = snprintf(buf,sizeof(buf),"%-7.7s %-15.15s ",tz->country,"<ringcadance>");
163                                 for (i=0; i<tz->nrringcadance; i++) {
164                                         j += snprintf(buf+j,sizeof(buf)-j,"%d,",tz->ringcadance[i]);
165                                 }
166                                 if (tz->nrringcadance) j--;
167                                 strncpy(buf+j,"\n",sizeof(buf)-j);
168                                 ast_cli(fd,buf);
169                                 for (ts=tz->tones; ts; ts=ts->next)
170                                         ast_cli(fd,"%-7.7s %-15.15s %s\n",tz->country,ts->name,ts->data);
171                                 break;
172                         }
173                 }
174         }
175         if (!found_country)
176                 ast_cli(fd,"No countries matched your criteria.\n");
177         ast_mutex_unlock(&tzlock);
178         return -1;
179 }
180
181 /*
182  * Playtones command stuff
183  */
184 static int handle_playtones(struct ast_channel *chan, void *data)
185 {
186         struct tone_zone_sound *ts;
187         int res;
188
189         if (!data || !((char*)data)[0]) {
190                 ast_log(LOG_NOTICE,"Nothing to play\n");
191                 return -1;
192         }
193         ts = ast_get_indication_tone(chan->zone, (const char*)data);
194         if (ts && ts->data[0])
195                 res = ast_playtones_start(chan, 0, ts->data, 0);
196         else
197                 res = ast_playtones_start(chan, 0, (const char*)data, 0);
198         if (res)
199                 ast_log(LOG_NOTICE,"Unable to start playtones\n");
200         return res;
201 }
202
203 /*
204  * StopPlaylist command stuff
205  */
206 static int handle_stopplaytones(struct ast_channel *chan, void *data)
207 {
208         ast_playtones_stop(chan);
209         return 0;
210 }
211
212 /*
213  * Load module stuff
214  */
215 static int ind_load_module(void)
216 {
217         struct ast_config *cfg;
218         struct ast_variable *v;
219         char *cxt;
220         char *c;
221         struct tone_zone *tones;
222         const char *country = NULL;
223
224         /* that the following cast is needed, is yuk! */
225         /* yup, checked it out. It is NOT written to. */
226         cfg = ast_load((char *)config);
227         if (!cfg)
228                 return 0;
229
230         /* Use existing config to populate the Indication table */
231         cxt = ast_category_browse(cfg, NULL);
232         while(cxt) {
233                 /* All categories but "general" are considered countries */
234                 if (!strcasecmp(cxt, "general")) {
235                         cxt = ast_category_browse(cfg, cxt);
236                         continue;
237                 }
238                 tones = malloc(sizeof(struct tone_zone));
239                 if (!tones) {
240                         ast_log(LOG_WARNING,"Out of memory\n");
241                         return -1;
242                 }
243                 memset(tones,0,sizeof(struct tone_zone));
244                 strncpy(tones->country,cxt,sizeof(tones->country));
245
246                 v = ast_variable_browse(cfg, cxt);
247                 while(v) {
248                         if (!strcasecmp(v->name, "description")) {
249                                 strncpy(tones->description, v->value, sizeof(tones->description)-1);
250                         } else if (!strcasecmp(v->name,"ringcadance")) {
251                                 char *ring,*rings = ast_strdupa(v->value);
252                                 c = rings;
253                                 ring = strsep(&c,",");
254                                 while (ring) {
255                                         int *tmp, val;
256                                         if (!isdigit(ring[0]) || (val=atoi(ring))==-1) {
257                                                 ast_log(LOG_WARNING,"Invalid ringcadance given '%s' at line %d.\n",ring,v->lineno);
258                                                 ring = strsep(&c,",");
259                                                 continue;
260                                         }
261                                         tmp = realloc(tones->ringcadance,(tones->nrringcadance+1)*sizeof(int));
262                                         if (!tmp) {
263                                                 ast_log(LOG_WARNING, "Out of memory\n");
264                                                 return -1;
265                                         }
266                                         tones->ringcadance = tmp;
267                                         tmp[tones->nrringcadance] = val;
268                                         tones->nrringcadance++;
269                                         /* next item */
270                                         ring = strsep(&c,",");
271                                 }
272                         } else if (!strcasecmp(v->name,"alias")) {
273                                 char *countries = ast_strdupa(v->value);
274                                 c = countries;
275                                 country = strsep(&c,",");
276                                 while (country) {
277                                         struct tone_zone* azone = malloc(sizeof(struct tone_zone));
278                                         if (!azone) {
279                                                 ast_log(LOG_WARNING,"Out of memory\n");
280                                                 return -1;
281                                         }
282                                         memset(azone,0,sizeof(struct tone_zone));
283                                         strncpy(azone->country,country,sizeof(azone->country));
284                                         strncpy(azone->alias, cxt, sizeof(azone->alias)-1);
285                                         if (ast_register_indication_country(azone)) {
286                                                 ast_log(LOG_WARNING, "Unable to register indication alias at line %d.\n",v->lineno);
287                                                 free(tones);
288                                         }
289                                         /* next item */
290                                         country = strsep(&c,",");
291                                 }
292                         } else {
293                                 // add tone to country
294                                 struct tone_zone_sound *ps,*ts;
295                                 for (ps=NULL,ts=tones->tones; ts; ps=ts, ts=ts->next) {
296                                         if (strcasecmp(v->name,ts->name)==0) {
297                                                 /* already there */
298                                                 ast_log(LOG_NOTICE,"Duplicate entry '%s', skipped.\n",v->name);
299                                                 goto out;
300                                         }
301                                 }
302                                 /* not there, add it to the back */
303                                 ts = malloc(sizeof(struct tone_zone_sound));
304                                 if (!ts) {
305                                         ast_log(LOG_WARNING, "Out of memory\n");
306                                         return -1;
307                                 }
308                                 ts->next = NULL;
309                                 ts->name = strdup(v->name);
310                                 ts->data = strdup(v->value);
311                                 if (ps)
312                                         ps->next = ts;
313                                 else
314                                         tones->tones = ts;
315                         }
316 out:                    v = v->next;
317                 }
318                 if (tones->description[0] || tones->alias[0] || tones->tones) {
319                         if (ast_register_indication_country(tones)) {
320                                 ast_log(LOG_WARNING, "Unable to register indication at line %d.\n",v->lineno);
321                                 free(tones);
322                         }
323                 } else free(tones);
324
325                 cxt = ast_category_browse(cfg, cxt);
326         }
327
328         /* determine which country is the default */
329         country = ast_variable_retrieve(cfg,"general","country");
330         if (!country || !*country || ast_set_indication_country(country))
331                 ast_log(LOG_WARNING,"Unable to set the default country (for indication tones)\n");
332
333         ast_destroy(cfg);
334         return 0;
335 }
336
337 /*
338  * CLI entries for commands provided by this module
339  */
340 static struct ast_cli_entry add_indication_cli =
341         { { "add", "indication", NULL }, handle_add_indication,
342                 "Add the given indication to the country", help_add_indication,
343                 NULL };
344
345 static struct ast_cli_entry remove_indication_cli =
346         { { "remove", "indication", NULL }, handle_remove_indication,
347                 "Remove the given indication from the country", help_remove_indication,
348                 NULL };
349
350 static struct ast_cli_entry show_indications_cli =
351         { { "show", "indications", NULL }, handle_show_indications,
352                 "Show a list of all country/indications", help_show_indications,
353                 NULL };
354
355 /*
356  * Standard module functions ...
357  */
358 int unload_module(void)
359 {
360         /* remove the registed indications... */
361         ast_unregister_indication_country(NULL);
362
363         /* and the functions */
364         ast_cli_unregister(&add_indication_cli);
365         ast_cli_unregister(&remove_indication_cli);
366         ast_cli_unregister(&show_indications_cli);
367         ast_unregister_application("Playlist");
368         ast_unregister_application("StopPlaylist");
369         return 0;
370 }
371
372 int load_module(void)
373 {
374         if (ind_load_module()) return -1;
375  
376         ast_cli_register(&add_indication_cli);
377         ast_cli_register(&remove_indication_cli);
378         ast_cli_register(&show_indications_cli);
379         ast_register_application("Playtones", handle_playtones, "Play a tone list","Play a tone list, either registered (through indications.conf) or a direct list of tones and durations.");
380         ast_register_application("StopPlaytones", handle_stopplaytones, "Stop playing a tone list","Stop playing a tone list");
381
382         return 0;
383 }
384
385 int reload(void)
386 {
387         /* remove the registed indications... */
388         ast_unregister_indication_country(NULL);
389
390         return ind_load_module();
391 }
392
393 char *description(void)
394 {
395         /* that the following cast is needed, is yuk! */
396         return (char*)dtext;
397 }
398
399 int usecount(void)
400 {
401         return 0;
402 }
403
404 char *key()
405 {
406         return ASTERISK_GPL_KEY;
407 }