comparison mcabber/doc/HOWTO_modules.txt @ 1735:5093b5ca1572

New modules loading scheme
author Myhailo Danylenko <isbear@ukrpost.net>
date Thu, 04 Mar 2010 13:03:20 +0200
parents b09f82f61745
children 4a7c7900f600
comparison
equal deleted inserted replaced
1734:eae4a2637f2c 1735:5093b5ca1572
3 3
4 Mcabber module writing brief howto 4 Mcabber module writing brief howto
5 5
6 =========================================== 6 ===========================================
7 7
8 Mcabber loads modules via glib's GModule. 8 To obtain information on module mcabber uses struct
9 9 module_info_t, that module should provide in public
10 Thus, in your module you can provide functions 10 variable with name info_<modulename>. If module name
11 11 contains any extra symbols except [a-z0-9_] they should
12 -------------------------------------------------------- 12 be replaced with '_'.
13
14 --------------------------------------------------------
15 #include <mcabber/modules.h>
16
17 typedef void (*module_init_t)(void);
18 typedef void (*module_uninit_t)(void);
19
20 typedef struct {
21 const gchar *mcabber_version;
22 module_init_t init;
23 module_uninit_t uninit;
24 const gchar **requires;
25 } module_info_t;
26 --------------------------------------------------------
27
28 Callbacks init and uninit will be called after module
29 and it's dependencies loading. 'requires' should contain
30 NULL-terminated list of module names, that should be loaded
31 before this. 'mcabber_version' is required and should contain
32 mcabber version, that this module is designed to work with.
33 Three other fields may be NULL.
34
35 To load modules, mcabber uses glib's GModule, thus, in your
36 module you can also use functions
37
38 --------------------------------------------------------
39 #include <glib.h>
40 #include <gmodule.h>
41
13 const gchar* g_module_check_init (GModule *module); 42 const gchar* g_module_check_init (GModule *module);
14 void g_module_unload (GModule *module); 43 void g_module_unload (GModule *module);
15 -------------------------------------------------------- 44 --------------------------------------------------------
16 45
17 to do something when module is loaded and unloaded. On 46 to do something before any version/dependency checks will
18 success g_module_check_init should return NULL, and 47 be performed when module is loaded/unloaded. On success
19 error message otherwise. 48 g_module_check_init should return NULL, and error message
49 otherwise.
20 50
21 As module is loaded, you can use mcabber functions, 51 As module is loaded, you can use mcabber functions,
22 declared in mcabber's header files (though you should 52 declared in mcabber's header files (though you should
23 consider, that they may change their calling conventions 53 consider, that they may change their calling conventions
24 some day). 54 some day).
25 55
26 I will not explain them all, there are too much of 56 I will not explain them all, there are too much of
27 them, but will provide description for those, provided 57 them, but will provide description for those, provided
28 especially for module writers. 58 especially for module writers.
59
60 --------------------------------------------------------
61 #include <mcabber/modules.h>
62
63 const gchar *module_load (const gchar *name,
64 gboolean manual,
65 gboolean force);
66 const gchar *module_unload (const gchar *name,
67 gboolean manual,
68 gboolean force);
69 --------------------------------------------------------
70
71 These functions load and unload modules respectively.
72 You can use them to handle optional dependencies. What
73 happens, when module is loaded:
74 - check if module is present, and if present just
75 increase it's reference count
76 - load .so via glib (and call g_module_check_init, if
77 present)
78 - check for information structure presence
79 - check target mcabber version compatibility
80 - load modules, that this module requires (note, that
81 dependency problems will be reported as error
82 invariably, force flag have no effect on this check)
83 - module placed into a list of modules
84 - module init routine is called
85 And when unloaded:
86 - check if module is present
87 - decrease reference count, if it is not zero, return
88 - run module uninit routine
89 - unload modules, that were loaded as dependencies for
90 this
91 - remove from modules list
92 They return error message or NULL in case of success.
93 'manual' flag indicates, that module will be loaded by
94 direct user request. It serves the purpose of tracking
95 user and automatic references (user can have only one).
96 'force' flag on module loading causes mcabber to ignore
97 most of the loading errors. On unload it forces
98 unloading even if reference count is not zero.
29 99
30 -------------------------------------------------------- 100 --------------------------------------------------------
31 #include <mcabber/commands.h> 101 #include <mcabber/commands.h>
32 102
33 void cmd_add (const char *name, const char *help, 103 void cmd_add (const char *name, const char *help,
198 -------------------------------------------------------- 268 --------------------------------------------------------
199 269
200 Now, compile this file (hello.c) with 270 Now, compile this file (hello.c) with
201 271
202 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \ 272 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \
203 gmodule-2.0` -c hello.c 273 gmodule-2.0 mcabber` -c hello.c
204 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ 274 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \
205 `pkg-config --libs glib-2.0 gmodule-2.0` -o libhello.la \ 275 `pkg-config --libs glib-2.0 gmodule-2.0 mcabber` \
206 hello.lo 276 -o libhello.la hello.lo
207 277
208 (you should substitute /usr/lib/mcabber to directory, where 278 (you should substitute /usr/lib/mcabber to directory, where
209 your modules are located) and then install obtained module with 279 your modules are located) and then install obtained module with
210 280
211 libtool --mode=install install libhello.la \ 281 libtool --mode=install install libhello.la \
214 Note, that you, most likely need not run suggested by libtool 284 Note, that you, most likely need not run suggested by libtool
215 finish action, as we're working with module object, not system- 285 finish action, as we're working with module object, not system-
216 wide library, but maybe some systems require that. 286 wide library, but maybe some systems require that.
217 287
218 Now, set modules_dir mcabber variable to point to your modules 288 Now, set modules_dir mcabber variable to point to your modules
219 dir, and try to run /load hello. If all goes well, you should 289 dir, and try to run /module -f load hello. If all goes well,
220 see in status buffer message "Hello World!". 290 you should see in status buffer message "Hello World!" (as
221 Now unload module by running command /unload hello, that 291 well as some complaints, as we forced module loading).
222 should bring up message "Bye, World!". 292 Now unload module by running command /module unload hello,
293 that should bring up message "Bye, World!".
223 294
224 That's it, you just created very simple dynamically loadable 295 That's it, you just created very simple dynamically loadable
225 mcabber module. 296 mcabber module. But, as you noticed, it needs force to be
297 loaded. Now, let's add information structure, that mcabber
298 wants.
299
300 ==========================
301
302 Example: info struct
303
304 ==========================
305
306 --------------------------------------------------------
307 #include <mcabber/logprint.h>
308 /* module_info_t definition */
309 #include <mcabber/modules.h>
310
311 /* Print something on module loading */
312 void hello_init (void)
313 {
314 scr_LogPrint (LPRINT_NORMAL, "Hello, World!");
315 }
316
317 /* ... and unloading */
318 void hello_uninit (void)
319 {
320 scr_LogPrint (LPRINT_NORMAL, "Bye, World!");
321 }
322
323 module_info_t info_hello = {
324 .mcabber_version = "0.10.0",
325 .requires = NULL,
326 .init = hello_init,
327 .uninit = hello_uninit,
328 };
329
330 /* The End */
331 --------------------------------------------------------
332
333 Here we now do not use glib nor gmodule, so, we can omit
334 them in compilation lines:
335
336 libtool --mode=compile gcc `pkg-config --cflags mcabber` \
337 -c hello.c
338 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \
339 `pkg-config --libs mcabber` -o libhello.la hello.lo
340
341 Again compile it, copy, and try to load, now without -f
342 flag. As you may notice, when loading previous example,
343 mcabber first printed "Hello, World!", and only then
344 complaint about module not having information struct.
345 That's because g_module_check_init is called right
346 after module loading, before mcabber even have a chance
347 to look at module, while .init from info struct is
348 called afterwards by mcabber itself. You can try to
349 introduce some error (eg too high or missing target
350 mcabber version) and see the difference.
226 351
227 ======================= 352 =======================
228 353
229 Example: command 354 Example: command
230 355
231 ======================= 356 =======================
232 357
233 Now, let's allow our module to do some real work. 358 Now, let's allow our module to do some real work.
234 359
235 -------------------------------------------------------- 360 --------------------------------------------------------
236 #include <glib.h>
237 #include <gmodule.h>
238
239 #include <mcabber/logprint.h> 361 #include <mcabber/logprint.h>
240 #include <mcabber/commands.h> 362 #include <mcabber/commands.h>
363 #include <mcabber/modules.h>
241 364
242 /* Handler for command */ 365 /* Handler for command */
243 void do_hello (char *args) 366 void do_hello (char *args)
244 { 367 {
245 /* args contains command line with command 368 /* args contains command line with command
247 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", 370 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!",
248 *args != '\0' ? args : "World"); 371 *args != '\0' ? args : "World");
249 } 372 }
250 373
251 /* Register command */ 374 /* Register command */
252 const gchar* g_module_check_init (GModule *module) 375 void hello_init (void)
253 { 376 {
254 cmd_add ("hello", "", 0, 0, do_hello, NULL); 377 cmd_add ("hello", "", 0, 0, do_hello, NULL);
255 return NULL;
256 } 378 }
257 379
258 /* Unregister command */ 380 /* Unregister command */
259 void g_module_unload (GModule *module) 381 void hello_uninit (void)
260 { 382 {
261 cmd_del ("hello"); 383 cmd_del ("hello");
384 }
385
386 module_info_t hello_info = {
387 .mcabber_version = "0.10.0",
388 .requires = NULL,
389 .init = hello_init,
390 .uninit = hello_uninit,
262 } 391 }
263 392
264 /* The End */ 393 /* The End */
265 -------------------------------------------------------- 394 --------------------------------------------------------
266 395
279 Now let's investigate how to provide custom completion to 408 Now let's investigate how to provide custom completion to
280 your commands. You can as well use built-in completions, 409 your commands. You can as well use built-in completions,
281 their IDs are listed in compl.h. 410 their IDs are listed in compl.h.
282 411
283 -------------------------------------------------------- 412 --------------------------------------------------------
284 #include <glib.h>
285 #include <gmodule.h>
286
287 #include <mcabber/logprint.h> 413 #include <mcabber/logprint.h>
288 #include <mcabber/commands.h> 414 #include <mcabber/commands.h>
415 #include <mcabber/modules.h>
289 #include <mcabber/compl.h> 416 #include <mcabber/compl.h>
290 417
291 static guint hello_cid = 0; 418 static guint hello_cid = 0;
292 419
293 /* hello command handler */ 420 /* hello command handler */
301 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", 428 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!",
302 *args != '\0' ? args : "World"); 429 *args != '\0' ? args : "World");
303 } 430 }
304 431
305 /* Initialization */ 432 /* Initialization */
306 const gchar* g_module_check_init (GModule *module) 433 void hello_init (void)
307 { 434 {
308 /* Obtain handle for our completion 435 /* Obtain handle for our completion
309 * category */ 436 * category */
310 hello_cid = compl_new_category (); 437 hello_cid = compl_new_category ();
311 if (hello_cid) 438 if (hello_cid)
313 * completion list */ 440 * completion list */
314 compl_add_category_word (hello_cid, 441 compl_add_category_word (hello_cid,
315 "World"); 442 "World");
316 cmd_add ("hello", "", hello_cid, 0, do_hello, 443 cmd_add ("hello", "", hello_cid, 0, do_hello,
317 NULL); 444 NULL);
318 return NULL;
319 } 445 }
320 446
321 /* Deinitialization */ 447 /* Deinitialization */
322 void g_module_unload (GModule *module) 448 void hello_uninit (void)
323 { 449 {
324 /* Give back category handle */ 450 /* Give back category handle */
325 if (hello_cid) 451 if (hello_cid)
326 compl_del_category (hello_cid); 452 compl_del_category (hello_cid);
327 cmd_del ("hello"); 453 cmd_del ("hello");
454 }
455
456 module_info_t hello_info = {
457 .mcabber_version = "0.10.0",
458 .requires = NULL,
459 .init = hello_init,
460 .uninit = hello_uninit,
328 } 461 }
329 462
330 /* The End */ 463 /* The End */
331 -------------------------------------------------------- 464 --------------------------------------------------------
332 465
347 to do this? I am not satisfied with default mcabber's 480 to do this? I am not satisfied with default mcabber's
348 builtin beeper flexibility. I wanted beeping on any 481 builtin beeper flexibility. I wanted beeping on any
349 muc conference message, not just ones, directed to me. 482 muc conference message, not just ones, directed to me.
350 483
351 -------------------------------------------------------- 484 --------------------------------------------------------
352 #include <glib.h>
353 #include <gmodule.h>
354 #include <string.h> 485 #include <string.h>
355 486
356 #include <mcabber/logprint.h> 487 #include <mcabber/logprint.h>
357 #include <mcabber/commands.h> 488 #include <mcabber/commands.h>
358 #include <mcabber/compl.h> 489 #include <mcabber/compl.h>
359 #include <mcabber/hooks.h> 490 #include <mcabber/hooks.h>
360 #include <mcabber/screen.h> 491 #include <mcabber/screen.h>
361 #include <mcabber/settings.h> 492 #include <mcabber/settings.h>
493 #include <mcabber/module.h>
362 494
363 static guint beep_cid = 0; 495 static guint beep_cid = 0;
364 496
365 /* Event handler */ 497 /* Event handler */
366 void beep_hh (guint32 hid, hk_arg_t *args, gpointer userdata) 498 void beep_hh (guint32 hid, hk_arg_t *args, gpointer userdata)
398 scr_LogPrint (LPRINT_NORMAL, 530 scr_LogPrint (LPRINT_NORMAL,
399 "Beep on messages is disabled"); 531 "Beep on messages is disabled");
400 } 532 }
401 533
402 /* Initialization */ 534 /* Initialization */
403 const gchar* g_module_check_init (GModule *module) 535 void beep_init (void)
404 { 536 {
405 /* Create completions */ 537 /* Create completions */
406 beep_cid = compl_new_category (); 538 beep_cid = compl_new_category ();
407 if (beep_cid) { 539 if (beep_cid) {
408 compl_add_category_word (beep_cid, "enable"); 540 compl_add_category_word (beep_cid, "enable");
412 cmd_add ("beep", "", beep_cid, 0, do_beep, NULL); 544 cmd_add ("beep", "", beep_cid, 0, do_beep, NULL);
413 /* Add handler 545 /* Add handler
414 * We are only interested in incoming message events 546 * We are only interested in incoming message events
415 */ 547 */
416 hk_add_handler (beep_hh, HOOK_MESSAGE_IN, NULL); 548 hk_add_handler (beep_hh, HOOK_MESSAGE_IN, NULL);
417 return NULL;
418 } 549 }
419 550
420 /* Deinitialization */ 551 /* Deinitialization */
421 void g_module_unload (GModule *module) 552 void beep_uninit (void)
422 { 553 {
423 /* Unregister event handler */ 554 /* Unregister event handler */
424 hk_del_handler (beep_hh, NULL); 555 hk_del_handler (beep_hh, NULL);
425 /* Unregister command */ 556 /* Unregister command */
426 cmd_del ("beep"); 557 cmd_del ("beep");
427 /* Give back completion handle */ 558 /* Give back completion handle */
428 if (beep_cid) 559 if (beep_cid)
429 compl_del_category (beep_cid); 560 compl_del_category (beep_cid);
430 } 561 }
431 562
563 module_info_t beep_info = {
564 .mcabber_version = "0.10.0",
565 .requires = NULL,
566 .init = beep_init,
567 .uninit = beep_uninit,
568 }
569
432 /* The End */ 570 /* The End */
433 -------------------------------------------------------- 571 --------------------------------------------------------
434
435 Note, that to compile this we also need to add loudmouth-1.0
436 to pkg-config command line, so, you will have something like
437
438 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \
439 gmodule-2.0 loudmouth-1.0` -c beep.c
440 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \
441 `pkg-config --cflags glib-2.0 gmodule-2.0` -o libbeep.la \
442 beep.lo
443 libtool --mode=install install libbeep.la \
444 /usr/lib/mcabber/libbeep.la
445 572
446 If you use CMake (as do I), corresponding CMakeLists.txt 573 If you use CMake (as do I), corresponding CMakeLists.txt
447 snippet: 574 snippet:
448 575
449 -------------------------------------------------------- 576 --------------------------------------------------------
450 cmake_minimum_required(VERSION 2.6) 577 cmake_minimum_required(VERSION 2.6)
451 project(beep C) 578 project(beep C)
452 579
453 set(MCABBER_INCLUDE_DIR "/usr/include" CACHE FILEPATH
454 "Path to mcabber headers")
455
456 find_package(PkgConfig REQUIRED) 580 find_package(PkgConfig REQUIRED)
457 pkg_check_modules(GLIB REQUIRED glib-2.0) 581 pkg_check_modules(MCABBER REQUIRED mcabber)
458 pkg_check_modules(GMODULE REQUIRED gmodule-2.0)
459 pkg_check_modules(LM REQUIRED loudmouth-1.0)
460 # this one should be before any target definitions 582 # this one should be before any target definitions
461 link_directories(${GLIB_LIBRARY_DIRS} 583 link_directories(${MCABBER_LIBRARY_DIRS})
462 ${GMODULE_LIBRARY_DIRS})
463 584
464 add_library(beep MODULE beep.c) 585 add_library(beep MODULE beep.c)
465 586
466 include_directories(SYSTEM ${GLIB_INCLUDE_DIRS} 587 include_directories(SYSTEM ${MCABBER_INCLUDE_DIRS})
467 ${GMODULE_INCLUDE_DIRS} 588 target_link_libraries(beep ${MCABBER_LIBRARIES)
468 ${LM_INCLUDE_DIRS}
469 ${MCABBER_INCLUDE_DIR})
470 target_link_libraries(beep ${GLIB_LIBRARIES}
471 ${GMODULE_LIBRARIES})
472 include_directories(${beep_SOURCE_DIR} 589 include_directories(${beep_SOURCE_DIR}
473 ${beep_BINARY_DIR}) 590 ${beep_BINARY_DIR})
474 591
475 install(TARGETS beep DESTINATION lib/mcabber) 592 install(TARGETS beep DESTINATION lib/mcabber)
476 -------------------------------------------------------- 593 --------------------------------------------------------
594
595 ===========================
596
597 Example: dependencies
598
599 ===========================
600
601 I will not provide here a complete example of two
602 modules, one of which depends on other, only some
603 use cases.
604
605 Info struct for module, that depends on two other
606 modules:
607
608 --------------------------------------------------------
609 #include <mcabber/modules.h>
610
611 const gchar *a_deps[] = { "b", "c", NULL };
612
613 module_info_t info_a = {
614 .mcabber_version = "0.10.0",
615 .requires = a_deps,
616 .init = a_init,
617 .uninit = a_uninit,
618 };
619 --------------------------------------------------------
620
621 If your module needs to "authenticate" mcabber version
622 too, this can be done in g_module_check_init:
623
624 --------------------------------------------------------
625 #include <glib.h>
626 #include <gmodule.h>
627
628 #include <mcabber/main.h>
629
630 const gchar *g_module_check_init (GModule *module)
631 {
632 char *ver = mcabber_version ();
633
634 // ver now contains version in format
635 // X.X.X[-xxx][ (XXXXXXXXX)]
636 if (...)
637 return "Incompatible mcabber version";
638
639 g_free (ver);
640 return NULL;
641 }
642 --------------------------------------------------------
643
644 Also you can use glib check_init routine to modify
645 module information, that will be checked by mcabber,
646 eg. if you want your module to always pass mcabber
647 version check, you can assign version, obtained from
648 mcabber_version() to corresponding field in your struct.
649 Or you can modify your module's dependencies, though
650 direct module_load() will have the same effect, and
651 can be used for optional dependencies, that your module
652 can work without.
653
654 Note: remember, that g_module_check_init will be always
655 called, even if later module will not pass checks, thus:
656 - do not use functions from other modules there;
657 - provide g_module_unload to undo anything, check_init
658 has done.
477 659
478 ============== 660 ==============
479 661
480 Further 662 Further
481 663
507 a more suitable version of text in question). 689 a more suitable version of text in question).
508 690
509 -- Myhailo Danylenko 691 -- Myhailo Danylenko
510 -- mailto:isbear@ukrpost.net 692 -- mailto:isbear@ukrpost.net
511 -- xmpp:isbear@unixzone.org.ua 693 -- xmpp:isbear@unixzone.org.ua
512 -- Mon, 18 Jan 2010 15:52:40 +0200 694 -- Thu, 04 Mar 2010 09:32:38 +0200
513 695