pbx_gtkconsole.c 13 KB

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