Merge BSD stack size work (bug #2067)
[asterisk/asterisk.git] / pbx / pbx_gtkconsole.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * GTK Console monitor -- very kludgy right now
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 /* 
15  * I know this might seem somewhat pointless in its current phase, but one
16  * of the most important parts of this module is demonstrate that modules
17  * can require other external libraries and still be loaded (in this
18  * case, a host of libraries involving gtk), so long as they are properly
19  * linked (see the Makefile)
20  */
21
22 #include <sys/types.h>
23 #include <asterisk/pbx.h>
24 #include <asterisk/config.h>
25 #include <asterisk/module.h>
26 #include <asterisk/logger.h>
27 #include <asterisk/options.h>
28 #include <asterisk/cli.h>
29 #include <asterisk/utils.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <signal.h>
37 #include <sys/time.h>
38
39 #include <gtk/gtk.h>
40 #include <glib.h>
41 /* For where to put dynamic tables */
42 #include "../asterisk.h"
43 #include "../astconf.h"
44
45 AST_MUTEX_DEFINE_STATIC(verb_lock);
46
47 static pthread_t console_thread;
48
49 static int inuse=0;
50 static int clipipe[2];
51 static int cleanupid = -1;
52
53 static char *dtext = "Asterisk PBX Console (GTK Version)";
54
55 static GtkWidget *window;
56 static GtkWidget *quit;
57 static GtkWidget *closew;
58 static GtkWidget *verb;
59 static GtkWidget *modules;
60 static GtkWidget *statusbar;
61 static GtkWidget *cli;
62
63 static struct timeval last;
64
65 static void update_statusbar(char *msg)
66 {
67         gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 1);
68         gtk_statusbar_push(GTK_STATUSBAR(statusbar), 1, msg);
69 }
70
71 int unload_module(void)
72 {
73         if (inuse) {
74                 /* Kill off the main thread */
75                 pthread_cancel(console_thread);
76                 gdk_threads_enter();
77                 gtk_widget_destroy(window);
78                 gdk_threads_leave();
79                 close(clipipe[0]);
80                 close(clipipe[1]);
81         }
82         return 0;
83 }
84
85 static int cleanup(void *useless)
86 {
87         gdk_threads_enter();
88         gtk_clist_thaw(GTK_CLIST(verb));
89         gtk_widget_queue_resize(verb->parent);
90         gtk_clist_moveto(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1, 0, 0, 0);
91         cleanupid = -1;
92         gdk_threads_leave();
93         return 0;
94 }
95
96
97 static void __verboser(const char *stuff, int opos, int replacelast, int complete)
98 {
99         char *s2[2];
100         struct timeval tv;
101         int ms;
102         s2[0] = (char *)stuff;
103         s2[1] = NULL;
104         gtk_clist_freeze(GTK_CLIST(verb));
105         if (replacelast) 
106                 gtk_clist_remove(GTK_CLIST(verb), GTK_CLIST(verb)->rows - 1);
107         gtk_clist_append(GTK_CLIST(verb), s2);
108         if (last.tv_sec || last.tv_usec) {
109                 gdk_threads_leave();
110                 gettimeofday(&tv, NULL);
111                 if (cleanupid > -1)
112                         gtk_timeout_remove(cleanupid);
113                 ms = (tv.tv_sec - last.tv_sec) * 1000 + (tv.tv_usec - last.tv_usec) / 1000;
114                 if (ms < 100) {
115                         /* We just got a message within 100ms, so just schedule an update
116                            in the near future */
117                         cleanupid = gtk_timeout_add(200, cleanup, NULL);
118                 } else {
119                         cleanup(&cleanupid);
120                 }
121                 last = tv;
122         } else {
123                 gettimeofday(&last, NULL);
124         }
125 }
126
127 static void verboser(const char *stuff, int opos, int replacelast, int complete) 
128 {
129         ast_mutex_lock(&verb_lock);
130         /* Lock appropriately if we're really being called in verbose mode */
131         __verboser(stuff, opos, replacelast, complete);
132         ast_mutex_unlock(&verb_lock);
133 }
134
135 static void cliinput(void *data, int source, GdkInputCondition ic)
136 {
137         static char buf[256];
138         static int offset = 0;
139         int res;
140         char *c;
141         char *l;
142         char n;
143         /* Read as much stuff is there */
144         res = read(source, buf + offset, sizeof(buf) - 1 - offset);
145         if (res > -1)
146                 buf[res + offset] = '\0';
147         /* make sure we've null terminated whatever we have so far */
148         c = buf;
149         l = buf;
150         while(*c) {
151                 if (*c == '\n') {
152                         /* Keep the trailing \n */
153                         c++;
154                         n = *c;
155                         *c = '\0';
156                         __verboser(l, 0, 0, 1);
157                         *(c - 1) = '\0';
158                         *c = n;
159                         l = c;
160                 } else
161                 c++;
162         }
163         if (strlen(l)) {
164                 /* We have some left over */
165                 memmove(buf, l, strlen(l) + 1);
166                 offset = strlen(buf);
167         } else {
168                 offset = 0;
169         }
170
171 }
172
173
174 static void remove_module(void)
175 {
176         int res;
177         char *module;
178         char buf[256];
179         if (GTK_CLIST(modules)->selection) {
180                 module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (int) GTK_CLIST(modules)->selection->data);
181                 gdk_threads_leave();
182                 res = ast_unload_resource(module, 0);
183                 gdk_threads_enter();
184                 if (res) {
185                         snprintf(buf, sizeof(buf), "Module '%s' is in use", module);
186                         update_statusbar(buf);
187                 } else {
188                         snprintf(buf, sizeof(buf), "Module '%s' removed", module);
189                         update_statusbar(buf);
190                 }
191         }
192 }
193 static void reload_module(void)
194 {
195         int res, x;
196         char *module;
197         char buf[256];
198         if (GTK_CLIST(modules)->selection) {
199                 module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (int) GTK_CLIST(modules)->selection->data);
200                 module = strdup(module);
201                 if (module) {
202                         gdk_threads_leave();
203                         res = ast_unload_resource(module, 0);
204                         gdk_threads_enter();
205                         if (res) {
206                                 snprintf(buf, sizeof(buf), "Module '%s' is in use", module);
207                                 update_statusbar(buf);
208                         } else {
209                                 gdk_threads_leave();
210                                 res = ast_load_resource(module);
211                                 gdk_threads_enter();
212                                 if (res) {
213                                         snprintf(buf, sizeof(buf), "Error reloading module '%s'", module);
214                                 } else {
215                                         snprintf(buf, sizeof(buf), "Module '%s' reloaded", module);
216                                 }
217                                 for (x=0; x < GTK_CLIST(modules)->rows; x++) {
218                                         if (!strcmp((char *)gtk_clist_get_row_data(GTK_CLIST(modules), x), module)) {
219                                                 gtk_clist_select_row(GTK_CLIST(modules), x, -1);
220                                                 break;
221                                         }
222                                 }
223                                 update_statusbar(buf);
224                                 
225                         }
226                         free(module);
227                 }
228         }
229 }
230
231 static void file_ok_sel(GtkWidget *w, GtkFileSelection *fs)
232 {
233         char tmp[AST_CONFIG_MAX_PATH];
234         char *module = gtk_file_selection_get_filename(fs);
235         char buf[256];
236         snprintf(tmp, sizeof(tmp), "%s/", ast_config_AST_MODULE_DIR);
237         if (!strncmp(module, (char *)tmp, strlen(tmp))) 
238                 module += strlen(tmp);
239         gdk_threads_leave();
240         if (ast_load_resource(module)) {
241                 snprintf(buf, sizeof(buf), "Error loading module '%s'.", module);
242                 update_statusbar(buf);
243         } else {
244                 snprintf(buf, sizeof(buf), "Module '%s' loaded", module);
245                 update_statusbar(buf);
246         }
247         gdk_threads_enter();
248         gtk_widget_destroy(GTK_WIDGET(fs));
249 }
250
251 static void add_module(void)
252 {
253         char tmp[AST_CONFIG_MAX_PATH];
254         GtkWidget *filew;
255         snprintf(tmp, sizeof(tmp), "%s/*.so", ast_config_AST_MODULE_DIR);
256         filew = gtk_file_selection_new("Load Module");
257         gtk_signal_connect(GTK_OBJECT (GTK_FILE_SELECTION(filew)->ok_button),
258                                         "clicked", GTK_SIGNAL_FUNC(file_ok_sel), filew);
259         gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION(filew)->cancel_button),
260                                         "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(filew));
261         gtk_file_selection_set_filename(GTK_FILE_SELECTION(filew), (char *)tmp);
262         gtk_widget_show(filew);
263 }
264
265 static int add_mod(char *module, char *description, int usecount)
266 {
267         char use[10];
268         char *pass[4];
269         int row;
270         snprintf(use, sizeof(use), "%d", usecount);
271         pass[0] = module;
272         pass[1] = description;
273         pass[2] = use;
274         pass[3] = NULL;
275         row = gtk_clist_append(GTK_CLIST(modules), pass);
276         gtk_clist_set_row_data(GTK_CLIST(modules), row, module);
277         return 0;       
278 }
279
280 static int mod_update(void)
281 {
282         char *module= NULL;
283         /* Update the mod stuff */
284         if (GTK_CLIST(modules)->selection) {
285                 module= (char *)gtk_clist_get_row_data(GTK_CLIST(modules), (int) GTK_CLIST(modules)->selection->data);
286         }
287         gtk_clist_freeze(GTK_CLIST(modules));
288         gtk_clist_clear(GTK_CLIST(modules));
289         ast_update_module_list(add_mod);
290         if (module)
291                 gtk_clist_select_row(GTK_CLIST(modules), gtk_clist_find_row_from_data(GTK_CLIST(modules), module), -1);
292         gtk_clist_thaw(GTK_CLIST(modules));
293         return 1;
294 }
295
296 static void exit_now(GtkWidget *widget, gpointer data)
297 {
298         ast_loader_unregister(mod_update);
299         gtk_main_quit();
300         inuse--;
301         ast_update_use_count();
302         ast_unregister_verbose(verboser);
303         ast_unload_resource("pbx_gtkconsole", 0);
304         if (option_verbose > 1)
305                 ast_verbose(VERBOSE_PREFIX_2 "GTK Console Monitor Exiting\n");
306         /* XXX Trying to quit after calling this makes asterisk segfault XXX */
307 }
308
309 static void exit_completely(GtkWidget *widget, gpointer data)
310 {
311 #if 0
312         /* Clever... */
313         ast_cli_command(clipipe[1], "quit");
314 #else
315         kill(getpid(), SIGTERM);
316 #endif
317 }
318
319 static void exit_nicely(GtkWidget *widget, gpointer data)
320 {
321         fflush(stdout);
322         gtk_widget_destroy(window);
323 }
324
325 static void *consolethread(void *data)
326 {
327         gtk_widget_show(window);
328         gdk_threads_enter();
329         gtk_main();
330         gdk_threads_leave();
331         return NULL;
332 }
333
334 static int cli_activate(void)
335 {
336         char buf[256] = "";
337         strncpy(buf, gtk_entry_get_text(GTK_ENTRY(cli)), sizeof(buf) - 1);
338         gtk_entry_set_text(GTK_ENTRY(cli), "");
339         if (strlen(buf)) {
340                 ast_cli_command(clipipe[1], buf);
341         }
342         return TRUE;
343 }
344
345 static int show_console(void)
346 {
347         GtkWidget *hbox;
348         GtkWidget *wbox;
349         GtkWidget *notebook;
350         GtkWidget *sw;
351         GtkWidget *bbox, *hbbox, *add, *removew, *reloadw;
352         char *modtitles[3] = { "Module", "Description", "Use Count" };
353         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
354         
355         statusbar = gtk_statusbar_new();
356         gtk_widget_show(statusbar);
357         
358         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
359                         GTK_SIGNAL_FUNC (exit_nicely), window);
360         gtk_signal_connect(GTK_OBJECT(window), "destroy",
361                         GTK_SIGNAL_FUNC (exit_now), window);
362         gtk_container_set_border_width(GTK_CONTAINER(window), 10);
363
364         quit = gtk_button_new_with_label("Quit Asterisk");
365         gtk_signal_connect(GTK_OBJECT(quit), "clicked",
366                         GTK_SIGNAL_FUNC (exit_completely), window);
367         gtk_widget_show(quit);
368
369         closew = gtk_button_new_with_label("Close Window");
370         gtk_signal_connect(GTK_OBJECT(closew), "clicked",
371                         GTK_SIGNAL_FUNC (exit_nicely), window);
372         gtk_widget_show(closew);
373
374         notebook = gtk_notebook_new();
375         verb = gtk_clist_new(1);
376         gtk_clist_columns_autosize(GTK_CLIST(verb));
377         sw = gtk_scrolled_window_new(NULL, NULL);
378         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
379         gtk_container_add(GTK_CONTAINER(sw), verb);
380         gtk_widget_show(verb);
381         gtk_widget_show(sw);
382         gtk_widget_set_usize(verb, 640, 400);
383         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, gtk_label_new("Verbose Status"));
384
385         
386         modules = gtk_clist_new_with_titles(3, modtitles);
387         gtk_clist_columns_autosize(GTK_CLIST(modules));
388         gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 0, TRUE);
389         gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 1, TRUE);
390         gtk_clist_set_column_auto_resize(GTK_CLIST(modules), 2, TRUE);
391         gtk_clist_set_sort_column(GTK_CLIST(modules), 0);
392         gtk_clist_set_auto_sort(GTK_CLIST(modules), TRUE);
393         gtk_clist_column_titles_passive(GTK_CLIST(modules));
394         sw = gtk_scrolled_window_new(NULL, NULL);
395         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
396         gtk_container_add(GTK_CONTAINER(sw), modules);
397         gtk_clist_set_selection_mode(GTK_CLIST(modules), GTK_SELECTION_BROWSE);
398         gtk_widget_show(modules);
399         gtk_widget_show(sw);
400
401         add = gtk_button_new_with_label("Load...");
402         gtk_widget_show(add);
403         removew = gtk_button_new_with_label("Unload");
404         gtk_widget_show(removew);
405         reloadw = gtk_button_new_with_label("Reload");
406         gtk_widget_show(reloadw);
407         gtk_signal_connect(GTK_OBJECT(removew), "clicked",
408                         GTK_SIGNAL_FUNC (remove_module), window);
409         gtk_signal_connect(GTK_OBJECT(add), "clicked",
410                         GTK_SIGNAL_FUNC (add_module), window);
411         gtk_signal_connect(GTK_OBJECT(reloadw), "clicked",
412                         GTK_SIGNAL_FUNC (reload_module), window);
413                 
414         bbox = gtk_vbox_new(FALSE, 5);
415         gtk_widget_show(bbox);
416
417         gtk_widget_set_usize(bbox, 100, -1);
418         gtk_box_pack_start(GTK_BOX(bbox), add, FALSE, FALSE, 5);
419         gtk_box_pack_start(GTK_BOX(bbox), removew, FALSE, FALSE, 5);
420         gtk_box_pack_start(GTK_BOX(bbox), reloadw, FALSE, FALSE, 5);
421
422         hbbox = gtk_hbox_new(FALSE, 5);
423         gtk_widget_show(hbbox);
424         
425         gtk_box_pack_start(GTK_BOX(hbbox), sw, TRUE, TRUE, 5);
426         gtk_box_pack_start(GTK_BOX(hbbox), bbox, FALSE, FALSE, 5);
427
428         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbbox, gtk_label_new("Module Information"));
429
430         gtk_widget_show(notebook);
431
432         wbox = gtk_hbox_new(FALSE, 5);
433         gtk_widget_show(wbox);
434         gtk_box_pack_end(GTK_BOX(wbox), quit, FALSE, FALSE, 5);
435         gtk_box_pack_end(GTK_BOX(wbox), closew, FALSE, FALSE, 5);
436
437         hbox = gtk_vbox_new(FALSE, 0);
438         gtk_widget_show(hbox);
439         
440         /* Command line */
441         cli = gtk_entry_new();
442         gtk_widget_show(cli);
443
444         gtk_signal_connect(GTK_OBJECT(cli), "activate",
445                         GTK_SIGNAL_FUNC (cli_activate), NULL);
446
447         gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 5);
448         gtk_box_pack_start(GTK_BOX(hbox), wbox, FALSE, FALSE, 5);
449         gtk_box_pack_start(GTK_BOX(hbox), cli, FALSE, FALSE, 0);
450         gtk_box_pack_start(GTK_BOX(hbox), statusbar, FALSE, FALSE, 0);
451         gtk_container_add(GTK_CONTAINER(window), hbox);
452         gtk_window_set_title(GTK_WINDOW(window), "Asterisk Console");
453         gtk_widget_grab_focus(cli);
454         ast_pthread_create(&console_thread, NULL, consolethread, NULL);
455         /* XXX Okay, seriously fix me! XXX */
456         usleep(100000);
457         ast_register_verbose(verboser);
458         gtk_clist_freeze(GTK_CLIST(verb));
459         ast_loader_register(mod_update);
460         gtk_clist_thaw(GTK_CLIST(verb));
461         gdk_input_add(clipipe[0], GDK_INPUT_READ, cliinput, NULL);
462         mod_update();
463         update_statusbar("Asterisk Console Ready");
464         return 0;
465 }
466
467
468 int load_module(void)
469 {
470         if (pipe(clipipe)) {
471                 ast_log(LOG_WARNING, "Unable to create CLI pipe\n");
472                 return -1;
473         }
474         g_thread_init(NULL);
475         if (gtk_init_check(NULL, NULL))  {
476                 if (!show_console()) {
477                         inuse++;
478                         ast_update_use_count();
479                         if (option_verbose > 1)
480                                 ast_verbose( VERBOSE_PREFIX_2 "Launched GTK Console monitor\n");                
481                 } else
482                         ast_log(LOG_WARNING, "Unable to start GTK console\n");
483         } else {
484                 if (option_debug)
485                         ast_log(LOG_DEBUG, "Unable to start GTK console monitor -- ignoring\n");
486                 else if (option_verbose > 1)
487                         ast_verbose( VERBOSE_PREFIX_2 "GTK is not available -- skipping monitor\n");
488         }
489         return 0;
490 }
491
492 int usecount(void)
493 {
494         return inuse;
495 }
496
497 char *description(void)
498 {
499         return dtext;
500 }
501
502 char *key(void)
503 {
504         return ASTERISK_GPL_KEY;
505 }