]>
Commit | Line | Data |
---|---|---|
6b0275ac DL |
1 | # Module and Hook support (developer docs) |
2 | ||
3 | ## What it does | |
4 | ||
5 | It uses `dlopen()` to load DSOs at startup. | |
6 | ||
7 | ||
8 | ## Limitations | |
9 | ||
10 | * can't load, unload, or reload during runtime. This just needs some work | |
11 | and can probably be done in the future. | |
12 | * doesn't fix any of the "things need to be changed in the code in the library" | |
13 | issues. Most prominently, you can't add a CLI node because CLI nodes are | |
14 | listed in the library... | |
15 | * if your module crashes, the daemon crashes. Should be obvious. | |
16 | * **does not provide a stable API or ABI**. Your module must match a version | |
17 | of FRR and you may have to update it frequently to match changes. | |
18 | * **does not create a license boundary**. Your module will need to link | |
19 | libzebra and include header files from the daemons, meaning it will be | |
20 | GPL-encumbered. | |
21 | ||
22 | ||
23 | ## Installation | |
24 | ||
25 | Look for `moduledir` in `configure.ac`, default is normally | |
26 | `/usr/lib64/frr/modules` but depends on `--libdir` / `--prefix`. | |
27 | ||
28 | The daemon's name is prepended when looking for a module, e.g. "snmp" tries | |
29 | to find "zebra_snmp" first when used in zebra. This is just to make it nicer | |
30 | for the user, with the snmp module having the same name everywhere. | |
31 | ||
32 | Modules can be packaged separately from FRR. The SNMP and FPM modules are | |
33 | good candidates for this because they have dependencies (net-snmp / protobuf) | |
34 | that are not FRR dependencies. However, any distro packages should have an | |
35 | "exact-match" dependency onto the FRR package. Using a module from a | |
36 | different FRR version will probably blow up nicely. | |
37 | ||
38 | For snapcraft (and during development), modules can be loaded with full path | |
39 | (e.g. -M `$SNAP/lib/frr/modules/zebra_snmp.so`). Note that libtool puts output | |
40 | files in the .libs directory, so during development you have to use | |
41 | `./zebra -M .libs/zebra_snmp.so`. | |
42 | ||
43 | ||
44 | ## Creating a module | |
45 | ||
46 | ... best to look at the existing SNMP or FPM modules. | |
47 | ||
48 | Basic boilerplate: | |
49 | ||
50 | ``` | |
51 | #include "hook.h" | |
52 | #include "module.h" | |
53 | ||
54 | static int | |
55 | module_init (void) | |
56 | { | |
57 | hook_register(frr_late_init, module_late_init); | |
58 | return 0; | |
59 | } | |
60 | ||
61 | FRR_MODULE_SETUP( | |
62 | .name = "my module", | |
63 | .version = "0.0", | |
64 | .description = "my module", | |
65 | .init = module_init, | |
66 | ) | |
67 | ``` | |
68 | ||
69 | The `frr_late_init` hook will be called after the daemon has finished its | |
70 | other startup and is about to enter the main event loop; this is the best | |
71 | place for most initialisation. | |
72 | ||
73 | ||
74 | ## Compiler & Linker magic | |
75 | ||
76 | There's a `THIS_MODULE` (like in the Linux kernel), which uses `visibility` | |
77 | attributes to restrict it to the current module. If you get a linker error | |
78 | with `_frrmod_this_module`, there is some linker SNAFU. This shouldn't be | |
79 | possible, though one way to get it would be to not include libzebra (which | |
80 | provides a fallback definition for the symbol). | |
81 | ||
82 | libzebra and the daemons each have their own `THIS_MODULE`, as do all loadable | |
83 | modules. In any other libraries (e.g. `libfrrsnmp`), `THIS_MODULE` will use | |
84 | the definition in libzebra; same applies if the main executable doesn't use | |
85 | `FRR_DAEMON_INFO` (e.g. all testcases). | |
86 | ||
87 | The deciding factor here is "what dynamic linker unit are you using the symbol | |
88 | from." If you're in a library function and want to know who called you, you | |
89 | can't use `THIS_MODULE` (because that'll just tell you you're in the library). | |
90 | Put a macro around your function that adds `THIS_MODULE` in the *caller's | |
91 | code calling your function*. | |
92 | ||
93 | The idea is to use this in the future for module unloading. Hooks already | |
94 | remember which module they were installed by, as groundwork for a function | |
95 | that removes all of a module's installed hooks. | |
96 | ||
97 | There's also the `frr_module` symbol in modules, pretty much a standard entry | |
98 | point for loadable modules. | |
99 | ||
100 | ||
101 | ## Hooks | |
102 | ||
103 | Hooks are just points in the code where you can register your callback to | |
104 | be called. The parameter list is specific to the hook point. Since there is | |
105 | no stable API, the hook code has some extra type safety checks making sure | |
106 | you get a compiler warning when the hook parameter list doesn't match your | |
107 | callback. Don't ignore these warnings. | |
108 | ||
109 | ||
110 | ## Relation to MTYPE macros | |
111 | ||
112 | The MTYPE macros, while primarily designed to decouple MTYPEs from the library | |
113 | and beautify the code, also work very nicely with loadable modules -- both | |
114 | constructors and destructors are executed when loading/unloading modules. | |
115 | ||
116 | This means there is absolutely no change required to MTYPEs, you can just use | |
117 | them in a module and they will even clean up themselves when we implement | |
118 | module unloading and an unload happens. In fact, it's impossible to create | |
119 | a bug where unloading fails to de-register a MTYPE. |