]>
Commit | Line | Data |
---|---|---|
212380e3 AC |
1 | /* |
2 | * ircd-ratbox: A slightly useful ircd. | |
3 | * modules.c: A module loader. | |
4 | * | |
5 | * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center | |
6 | * Copyright (C) 1996-2002 Hybrid Development Team | |
7 | * Copyright (C) 2002-2005 ircd-ratbox development team | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
212380e3 AC |
23 | */ |
24 | ||
25 | #include "stdinc.h" | |
212380e3 | 26 | #include "modules.h" |
4016731b | 27 | #include "logger.h" |
212380e3 AC |
28 | #include "ircd.h" |
29 | #include "client.h" | |
30 | #include "send.h" | |
31 | #include "s_conf.h" | |
32 | #include "s_newconf.h" | |
33 | #include "numeric.h" | |
34 | #include "parse.h" | |
35 | #include "ircd_defs.h" | |
4562c604 | 36 | #include "match.h" |
15feac53 | 37 | #include "s_serv.h" |
8e9c6a75 | 38 | #include "capability.h" |
212380e3 | 39 | |
f272e7ab AC |
40 | #include <ltdl.h> |
41 | ||
212380e3 AC |
42 | struct module **modlist = NULL; |
43 | ||
44 | static const char *core_module_table[] = { | |
431a1a27 | 45 | "m_ban", |
212380e3 AC |
46 | "m_die", |
47 | "m_error", | |
48 | "m_join", | |
49 | "m_kick", | |
50 | "m_kill", | |
51 | "m_message", | |
52 | "m_mode", | |
53 | "m_nick", | |
54 | "m_part", | |
55 | "m_quit", | |
56 | "m_server", | |
212380e3 AC |
57 | "m_squit", |
58 | NULL | |
59 | }; | |
60 | ||
61 | #define MODS_INCREMENT 10 | |
62 | int num_mods = 0; | |
63 | int max_mods = MODS_INCREMENT; | |
64 | ||
330fc5c1 | 65 | static rb_dlink_list mod_paths; |
212380e3 | 66 | |
4a84a763 AC |
67 | static int mo_modload(struct MsgBuf *, struct Client *, struct Client *, int, const char **); |
68 | static int mo_modlist(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
69 | static int mo_modreload(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
70 | static int mo_modunload(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
71 | static int mo_modrestart(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
72 | ||
73 | static int me_modload(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
74 | static int me_modlist(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
75 | static int me_modreload(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
76 | static int me_modunload(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
77 | static int me_modrestart(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
15feac53 AC |
78 | |
79 | static int do_modload(struct Client *, const char *); | |
80 | static int do_modunload(struct Client *, const char *); | |
81 | static int do_modreload(struct Client *, const char *); | |
82 | static int do_modlist(struct Client *, const char *); | |
83 | static int do_modrestart(struct Client *); | |
84 | ||
212380e3 | 85 | struct Message modload_msgtab = { |
7baa37a9 | 86 | "MODLOAD", 0, 0, 0, 0, |
15feac53 | 87 | {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modload, 2}, {mo_modload, 2}} |
212380e3 AC |
88 | }; |
89 | ||
90 | struct Message modunload_msgtab = { | |
7baa37a9 | 91 | "MODUNLOAD", 0, 0, 0, 0, |
15feac53 | 92 | {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modunload, 2}, {mo_modunload, 2}} |
212380e3 AC |
93 | }; |
94 | ||
95 | struct Message modreload_msgtab = { | |
7baa37a9 | 96 | "MODRELOAD", 0, 0, 0, 0, |
15feac53 | 97 | {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modreload, 2}, {mo_modreload, 2}} |
212380e3 AC |
98 | }; |
99 | ||
100 | struct Message modlist_msgtab = { | |
7baa37a9 | 101 | "MODLIST", 0, 0, 0, 0, |
15feac53 | 102 | {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modlist, 0}, {mo_modlist, 0}} |
212380e3 AC |
103 | }; |
104 | ||
105 | struct Message modrestart_msgtab = { | |
7baa37a9 | 106 | "MODRESTART", 0, 0, 0, 0, |
15feac53 | 107 | {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modrestart, 0}, {mo_modrestart, 0}} |
212380e3 AC |
108 | }; |
109 | ||
212380e3 AC |
110 | void |
111 | modules_init(void) | |
112 | { | |
f272e7ab AC |
113 | if(lt_dlinit()) |
114 | { | |
115 | ilog(L_MAIN, "lt_dlinit failed"); | |
116 | exit(0); | |
117 | } | |
118 | ||
212380e3 AC |
119 | mod_add_cmd(&modload_msgtab); |
120 | mod_add_cmd(&modunload_msgtab); | |
121 | mod_add_cmd(&modreload_msgtab); | |
122 | mod_add_cmd(&modlist_msgtab); | |
123 | mod_add_cmd(&modrestart_msgtab); | |
124 | ||
125 | /* Add the default paths we look in to the module system --nenolod */ | |
126 | mod_add_path(MODPATH); | |
127 | mod_add_path(AUTOMODPATH); | |
128 | } | |
129 | ||
130 | /* mod_find_path() | |
131 | * | |
132 | * input - path | |
133 | * output - none | |
134 | * side effects - returns a module path from path | |
135 | */ | |
26c6ac3d | 136 | static char * |
212380e3 AC |
137 | mod_find_path(const char *path) |
138 | { | |
330fc5c1 | 139 | rb_dlink_node *ptr; |
26c6ac3d | 140 | char *mpath; |
212380e3 | 141 | |
5cefa1d6 | 142 | RB_DLINK_FOREACH(ptr, mod_paths.head) |
212380e3 AC |
143 | { |
144 | mpath = ptr->data; | |
145 | ||
26c6ac3d | 146 | if(!strcmp(path, mpath)) |
212380e3 AC |
147 | return mpath; |
148 | } | |
149 | ||
150 | return NULL; | |
151 | } | |
152 | ||
153 | /* mod_add_path | |
154 | * | |
155 | * input - path | |
55abcbb2 | 156 | * ouput - |
212380e3 AC |
157 | * side effects - adds path to list |
158 | */ | |
159 | void | |
160 | mod_add_path(const char *path) | |
161 | { | |
26c6ac3d | 162 | char *pathst; |
212380e3 AC |
163 | |
164 | if(mod_find_path(path)) | |
165 | return; | |
166 | ||
26c6ac3d | 167 | pathst = rb_strdup(path); |
330fc5c1 | 168 | rb_dlinkAddAlloc(pathst, &mod_paths); |
212380e3 AC |
169 | } |
170 | ||
171 | /* mod_clear_paths() | |
172 | * | |
173 | * input - | |
174 | * output - | |
175 | * side effects - clear the lists of paths | |
176 | */ | |
177 | void | |
178 | mod_clear_paths(void) | |
179 | { | |
637c4932 | 180 | rb_dlink_node *ptr, *next_ptr; |
212380e3 | 181 | |
637c4932 | 182 | RB_DLINK_FOREACH_SAFE(ptr, next_ptr, mod_paths.head) |
212380e3 | 183 | { |
637c4932 | 184 | rb_free(ptr->data); |
30106156 | 185 | rb_free_rb_dlink_node(ptr); |
212380e3 AC |
186 | } |
187 | ||
188 | mod_paths.head = mod_paths.tail = NULL; | |
189 | mod_paths.length = 0; | |
190 | } | |
191 | ||
212380e3 AC |
192 | /* findmodule_byname |
193 | * | |
194 | * input - | |
195 | * output - | |
196 | * side effects - | |
197 | */ | |
198 | ||
199 | int | |
200 | findmodule_byname(const char *name) | |
201 | { | |
202 | int i; | |
203 | ||
204 | for (i = 0; i < num_mods; i++) | |
205 | { | |
206 | if(!irccmp(modlist[i]->name, name)) | |
207 | return i; | |
208 | } | |
209 | return -1; | |
210 | } | |
211 | ||
212 | /* load_all_modules() | |
213 | * | |
214 | * input - | |
215 | * output - | |
216 | * side effects - | |
217 | */ | |
218 | void | |
219 | load_all_modules(int warn) | |
220 | { | |
221 | DIR *system_module_dir = NULL; | |
222 | struct dirent *ldirent = NULL; | |
223 | char module_fq_name[PATH_MAX + 1]; | |
224 | int len; | |
225 | ||
226 | modules_init(); | |
227 | ||
1e170010 | 228 | modlist = (struct module **) rb_malloc(sizeof(struct module *) * (MODS_INCREMENT)); |
212380e3 AC |
229 | |
230 | max_mods = MODS_INCREMENT; | |
231 | ||
232 | system_module_dir = opendir(AUTOMODPATH); | |
233 | ||
234 | if(system_module_dir == NULL) | |
235 | { | |
236 | ilog(L_MAIN, "Could not load modules from %s: %s", AUTOMODPATH, strerror(errno)); | |
237 | return; | |
238 | } | |
239 | ||
240 | while ((ldirent = readdir(system_module_dir)) != NULL) | |
241 | { | |
5203cba5 | 242 | len = strlen(ldirent->d_name); |
2a19fc3f | 243 | if((len > 3) && !strcmp(ldirent->d_name+len-3, ".la")) |
5203cba5 VI |
244 | { |
245 | (void) snprintf(module_fq_name, sizeof(module_fq_name), "%s/%s", AUTOMODPATH, ldirent->d_name); | |
216d70e9 | 246 | (void) load_a_module(module_fq_name, warn, MAPI_ORIGIN_CORE, 0); |
5203cba5 | 247 | } |
212380e3 AC |
248 | |
249 | } | |
250 | (void) closedir(system_module_dir); | |
251 | } | |
252 | ||
253 | /* load_core_modules() | |
254 | * | |
255 | * input - | |
256 | * output - | |
257 | * side effects - core modules are loaded, if any fail, kill ircd | |
258 | */ | |
259 | void | |
260 | load_core_modules(int warn) | |
261 | { | |
d74fa5b5 | 262 | char module_name[PATH_MAX]; |
212380e3 AC |
263 | int i; |
264 | ||
265 | ||
266 | for (i = 0; core_module_table[i]; i++) | |
267 | { | |
5203cba5 | 268 | snprintf(module_name, sizeof(module_name), "%s/%s%s", MODPATH, |
2a19fc3f | 269 | core_module_table[i], ".la"); |
212380e3 | 270 | |
216d70e9 | 271 | if(load_a_module(module_name, warn, MAPI_ORIGIN_CORE, 1) == -1) |
212380e3 AC |
272 | { |
273 | ilog(L_MAIN, | |
274 | "Error loading core module %s%s: terminating ircd", | |
2a19fc3f | 275 | core_module_table[i], ".la"); |
212380e3 AC |
276 | exit(0); |
277 | } | |
278 | } | |
279 | } | |
280 | ||
281 | /* load_one_module() | |
282 | * | |
283 | * input - | |
284 | * output - | |
285 | * side effects - | |
286 | */ | |
287 | int | |
216d70e9 | 288 | load_one_module(const char *path, int origin, int coremodule) |
212380e3 | 289 | { |
d74fa5b5 | 290 | char modpath[PATH_MAX]; |
330fc5c1 | 291 | rb_dlink_node *pathst; |
26c6ac3d | 292 | const char *mpath; |
212380e3 AC |
293 | |
294 | struct stat statbuf; | |
295 | ||
296 | if (server_state_foreground == 1) | |
55abcbb2 | 297 | inotice("loading module %s ...", path); |
212380e3 | 298 | |
216d70e9 EM |
299 | if(coremodule != 0) |
300 | { | |
301 | coremodule = 1; | |
302 | origin = MAPI_ORIGIN_CORE; | |
303 | } | |
304 | ||
5cefa1d6 | 305 | RB_DLINK_FOREACH(pathst, mod_paths.head) |
212380e3 AC |
306 | { |
307 | mpath = pathst->data; | |
308 | ||
5203cba5 | 309 | snprintf(modpath, sizeof(modpath), "%s/%s", mpath, path); |
212380e3 AC |
310 | if((strstr(modpath, "../") == NULL) && (strstr(modpath, "/..") == NULL)) |
311 | { | |
312 | if(stat(modpath, &statbuf) == 0) | |
313 | { | |
314 | if(S_ISREG(statbuf.st_mode)) | |
315 | { | |
316 | /* Regular files only please */ | |
216d70e9 | 317 | return load_a_module(modpath, 1, origin, coremodule); |
212380e3 AC |
318 | } |
319 | } | |
320 | ||
321 | } | |
322 | } | |
323 | ||
324 | sendto_realops_snomask(SNO_GENERAL, L_ALL, "Cannot locate module %s", path); | |
325 | return -1; | |
326 | } | |
327 | ||
328 | ||
329 | /* load a module .. */ | |
330 | static int | |
4a84a763 | 331 | mo_modload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
212380e3 | 332 | { |
212380e3 AC |
333 | if(!IsOperAdmin(source_p)) |
334 | { | |
335 | sendto_one(source_p, form_str(ERR_NOPRIVS), | |
336 | me.name, source_p->name, "admin"); | |
337 | return 0; | |
338 | } | |
339 | ||
15feac53 AC |
340 | if(parc > 2) |
341 | { | |
342 | sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS, | |
343 | "ENCAP %s MODLOAD %s", parv[2], parv[1]); | |
344 | if (match(parv[2], me.name) == 0) | |
345 | return 0; | |
346 | } | |
347 | ||
348 | return do_modload(source_p, parv[1]); | |
349 | } | |
350 | ||
351 | static int | |
4a84a763 | 352 | me_modload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
15feac53 AC |
353 | { |
354 | if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE)) | |
355 | { | |
356 | sendto_one_notice(source_p, ":*** You do not have an appropriate shared block " | |
357 | "to load modules on this server."); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | return do_modload(source_p, parv[1]); | |
362 | } | |
363 | ||
364 | static int | |
365 | do_modload(struct Client *source_p, const char *module) | |
366 | { | |
367 | char *m_bn = rb_basename(module); | |
216d70e9 | 368 | int origin; |
212380e3 AC |
369 | |
370 | if(findmodule_byname(m_bn) != -1) | |
371 | { | |
5366977b | 372 | sendto_one_notice(source_p, ":Module %s is already loaded", m_bn); |
637c4932 | 373 | rb_free(m_bn); |
212380e3 AC |
374 | return 0; |
375 | } | |
376 | ||
216d70e9 EM |
377 | origin = strcmp(module, m_bn) == 0 ? MAPI_ORIGIN_CORE : MAPI_ORIGIN_EXTENSION; |
378 | load_one_module(module, origin, 0); | |
212380e3 | 379 | |
637c4932 | 380 | rb_free(m_bn); |
212380e3 AC |
381 | |
382 | return 0; | |
383 | } | |
384 | ||
385 | ||
386 | /* unload a module .. */ | |
387 | static int | |
4a84a763 | 388 | mo_modunload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
212380e3 | 389 | { |
212380e3 AC |
390 | if(!IsOperAdmin(source_p)) |
391 | { | |
392 | sendto_one(source_p, form_str(ERR_NOPRIVS), | |
393 | me.name, source_p->name, "admin"); | |
394 | return 0; | |
395 | } | |
396 | ||
15feac53 AC |
397 | if(parc > 2) |
398 | { | |
399 | sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS, | |
400 | "ENCAP %s MODUNLOAD %s", parv[2], parv[1]); | |
401 | if (match(parv[2], me.name) == 0) | |
402 | return 0; | |
403 | } | |
404 | ||
405 | return do_modunload(source_p, parv[1]); | |
406 | } | |
407 | ||
408 | static int | |
4a84a763 | 409 | me_modunload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
15feac53 AC |
410 | { |
411 | if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE)) | |
412 | { | |
413 | sendto_one_notice(source_p, ":*** You do not have an appropriate shared block " | |
414 | "to load modules on this server."); | |
415 | return 0; | |
416 | } | |
417 | ||
418 | return do_modunload(source_p, parv[1]); | |
419 | } | |
420 | ||
421 | static int | |
422 | do_modunload(struct Client *source_p, const char *module) | |
423 | { | |
424 | int modindex; | |
425 | char *m_bn = rb_basename(module); | |
212380e3 AC |
426 | |
427 | if((modindex = findmodule_byname(m_bn)) == -1) | |
428 | { | |
5366977b | 429 | sendto_one_notice(source_p, ":Module %s is not loaded", m_bn); |
637c4932 | 430 | rb_free(m_bn); |
212380e3 AC |
431 | return 0; |
432 | } | |
433 | ||
434 | if(modlist[modindex]->core == 1) | |
435 | { | |
5366977b | 436 | sendto_one_notice(source_p, ":Module %s is a core module and may not be unloaded", m_bn); |
637c4932 | 437 | rb_free(m_bn); |
212380e3 AC |
438 | return 0; |
439 | } | |
440 | ||
441 | if(unload_one_module(m_bn, 1) == -1) | |
442 | { | |
5366977b | 443 | sendto_one_notice(source_p, ":Module %s is not loaded", m_bn); |
212380e3 | 444 | } |
5366977b | 445 | |
637c4932 | 446 | rb_free(m_bn); |
212380e3 AC |
447 | return 0; |
448 | } | |
449 | ||
450 | /* unload and load in one! */ | |
451 | static int | |
4a84a763 | 452 | mo_modreload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
212380e3 | 453 | { |
212380e3 AC |
454 | if(!IsOperAdmin(source_p)) |
455 | { | |
456 | sendto_one(source_p, form_str(ERR_NOPRIVS), | |
457 | me.name, source_p->name, "admin"); | |
458 | return 0; | |
459 | } | |
460 | ||
15feac53 AC |
461 | if(parc > 2) |
462 | { | |
463 | sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS, | |
464 | "ENCAP %s MODRELOAD %s", parv[2], parv[1]); | |
465 | if (match(parv[2], me.name) == 0) | |
466 | return 0; | |
467 | } | |
468 | ||
469 | return do_modreload(source_p, parv[1]); | |
470 | } | |
471 | ||
472 | static int | |
4a84a763 | 473 | me_modreload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
15feac53 AC |
474 | { |
475 | if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE)) | |
476 | { | |
477 | sendto_one_notice(source_p, ":*** You do not have an appropriate shared block " | |
478 | "to load modules on this server."); | |
479 | return 0; | |
480 | } | |
481 | ||
482 | return do_modreload(source_p, parv[1]); | |
483 | } | |
484 | ||
485 | static int | |
486 | do_modreload(struct Client *source_p, const char *module) | |
487 | { | |
488 | int modindex; | |
489 | int check_core; | |
490 | char *m_bn = rb_basename(module); | |
212380e3 AC |
491 | |
492 | if((modindex = findmodule_byname(m_bn)) == -1) | |
493 | { | |
5366977b | 494 | sendto_one_notice(source_p, ":Module %s is not loaded", m_bn); |
637c4932 | 495 | rb_free(m_bn); |
212380e3 AC |
496 | return 0; |
497 | } | |
498 | ||
499 | check_core = modlist[modindex]->core; | |
500 | ||
501 | if(unload_one_module(m_bn, 1) == -1) | |
502 | { | |
5366977b | 503 | sendto_one_notice(source_p, ":Module %s is not loaded", m_bn); |
637c4932 | 504 | rb_free(m_bn); |
212380e3 AC |
505 | return 0; |
506 | } | |
507 | ||
216d70e9 | 508 | if((load_one_module(m_bn, modlist[modindex]->origin, check_core) == -1) && check_core) |
212380e3 | 509 | { |
15feac53 | 510 | sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, |
7d778d51 AC |
511 | "Error reloading core module: %s: terminating ircd", m_bn); |
512 | ilog(L_MAIN, "Error loading core module %s: terminating ircd", m_bn); | |
212380e3 AC |
513 | exit(0); |
514 | } | |
515 | ||
637c4932 | 516 | rb_free(m_bn); |
212380e3 AC |
517 | return 0; |
518 | } | |
519 | ||
520 | /* list modules .. */ | |
521 | static int | |
4a84a763 | 522 | mo_modlist(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
212380e3 | 523 | { |
212380e3 AC |
524 | if(!IsOperAdmin(source_p)) |
525 | { | |
526 | sendto_one(source_p, form_str(ERR_NOPRIVS), | |
527 | me.name, source_p->name, "admin"); | |
528 | return 0; | |
529 | } | |
530 | ||
15feac53 AC |
531 | if(parc > 2) |
532 | { | |
533 | sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS, | |
534 | "ENCAP %s MODLIST %s", parv[2], parv[1]); | |
535 | if (match(parv[2], me.name) == 0) | |
536 | return 0; | |
537 | } | |
538 | ||
539 | return do_modlist(source_p, parc > 1 ? parv[1] : 0); | |
540 | } | |
541 | ||
542 | static int | |
4a84a763 | 543 | me_modlist(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
15feac53 AC |
544 | { |
545 | if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE)) | |
546 | { | |
547 | sendto_one_notice(source_p, ":*** You do not have an appropriate shared block " | |
548 | "to load modules on this server."); | |
549 | return 0; | |
550 | } | |
551 | ||
552 | return do_modlist(source_p, parv[1]); | |
553 | } | |
554 | ||
555 | static int | |
556 | do_modlist(struct Client *source_p, const char *pattern) | |
557 | { | |
558 | int i; | |
559 | ||
212380e3 AC |
560 | for (i = 0; i < num_mods; i++) |
561 | { | |
15feac53 | 562 | if(pattern) |
212380e3 | 563 | { |
15feac53 | 564 | if(match(pattern, modlist[i]->name)) |
212380e3 AC |
565 | { |
566 | sendto_one(source_p, form_str(RPL_MODLIST), | |
567 | me.name, source_p->name, | |
568 | modlist[i]->name, | |
a9f12814 | 569 | (unsigned long)(uintptr_t)modlist[i]->address, |
2ab24be6 | 570 | modlist[i]->version, modlist[i]->description, modlist[i]->core ? " (core)" : ""); |
212380e3 AC |
571 | } |
572 | } | |
573 | else | |
574 | { | |
575 | sendto_one(source_p, form_str(RPL_MODLIST), | |
576 | me.name, source_p->name, modlist[i]->name, | |
2ab24be6 EM |
577 | (unsigned long)(uintptr_t)modlist[i]->address, |
578 | modlist[i]->version, modlist[i]->description, modlist[i]->core ? " (core)" : ""); | |
212380e3 AC |
579 | } |
580 | } | |
581 | ||
582 | sendto_one(source_p, form_str(RPL_ENDOFMODLIST), me.name, source_p->name); | |
583 | return 0; | |
584 | } | |
585 | ||
586 | /* unload and reload all modules */ | |
587 | static int | |
4a84a763 | 588 | mo_modrestart(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
212380e3 | 589 | { |
212380e3 AC |
590 | if(!IsOperAdmin(source_p)) |
591 | { | |
592 | sendto_one(source_p, form_str(ERR_NOPRIVS), | |
593 | me.name, source_p->name, "admin"); | |
594 | return 0; | |
595 | } | |
596 | ||
15feac53 AC |
597 | if(parc > 1) |
598 | { | |
599 | sendto_match_servs(source_p, parv[1], CAP_ENCAP, NOCAPS, | |
600 | "ENCAP %s MODRESTART", parv[1]); | |
601 | if (match(parv[1], me.name) == 0) | |
602 | return 0; | |
603 | } | |
604 | ||
605 | return do_modrestart(source_p); | |
606 | } | |
607 | ||
608 | static int | |
4a84a763 | 609 | me_modrestart(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv) |
15feac53 AC |
610 | { |
611 | if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE)) | |
612 | { | |
613 | sendto_one_notice(source_p, ":*** You do not have an appropriate shared block " | |
614 | "to load modules on this server."); | |
615 | return 0; | |
616 | } | |
617 | ||
618 | return do_modrestart(source_p); | |
619 | } | |
620 | ||
621 | static int | |
622 | do_modrestart(struct Client *source_p) | |
623 | { | |
624 | int modnum; | |
625 | ||
5366977b | 626 | sendto_one_notice(source_p, ":Reloading all modules"); |
212380e3 AC |
627 | |
628 | modnum = num_mods; | |
629 | while (num_mods) | |
630 | unload_one_module(modlist[0]->name, 0); | |
631 | ||
632 | load_all_modules(0); | |
633 | load_core_modules(0); | |
634 | rehash(0); | |
635 | ||
15feac53 | 636 | sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, |
212380e3 AC |
637 | "Module Restart: %d modules unloaded, %d modules loaded", |
638 | modnum, num_mods); | |
639 | ilog(L_MAIN, "Module Restart: %d modules unloaded, %d modules loaded", modnum, num_mods); | |
640 | return 0; | |
641 | } | |
642 | ||
643 | ||
212380e3 AC |
644 | static void increase_modlist(void); |
645 | ||
646 | #define MODS_INCREMENT 10 | |
647 | ||
648 | static char unknown_ver[] = "<unknown>"; | |
8e9c6a75 | 649 | static char unknown_description[] = "<none>"; |
212380e3 | 650 | |
212380e3 AC |
651 | /* unload_one_module() |
652 | * | |
653 | * inputs - name of module to unload | |
654 | * - 1 to say modules unloaded, 0 to not | |
655 | * output - 0 if successful, -1 if error | |
656 | * side effects - module is unloaded | |
657 | */ | |
658 | int | |
659 | unload_one_module(const char *name, int warn) | |
660 | { | |
661 | int modindex; | |
662 | ||
663 | if((modindex = findmodule_byname(name)) == -1) | |
664 | return -1; | |
665 | ||
666 | /* | |
667 | ** XXX - The type system in C does not allow direct conversion between | |
668 | ** data and function pointers, but as it happens, most C compilers will | |
55abcbb2 KB |
669 | ** safely do this, however it is a theoretical overlow to cast as we |
670 | ** must do here. I have library functions to take care of this, but | |
671 | ** despite being more "correct" for the C language, this is more | |
212380e3 AC |
672 | ** practical. Removing the abuse of the ability to cast ANY pointer |
673 | ** to and from an integer value here will break some compilers. | |
674 | ** -jmallett | |
675 | */ | |
676 | /* Left the comment in but the code isn't here any more -larne */ | |
677 | switch (modlist[modindex]->mapi_version) | |
678 | { | |
679 | case 1: | |
680 | { | |
681 | struct mapi_mheader_av1 *mheader = modlist[modindex]->mapi_header; | |
682 | if(mheader->mapi_command_list) | |
683 | { | |
684 | struct Message **m; | |
685 | for (m = mheader->mapi_command_list; *m; ++m) | |
686 | mod_del_cmd(*m); | |
687 | } | |
688 | ||
689 | /* hook events are never removed, we simply lose the | |
690 | * ability to call them --fl | |
691 | */ | |
692 | if(mheader->mapi_hfn_list) | |
693 | { | |
694 | mapi_hfn_list_av1 *m; | |
695 | for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) | |
696 | remove_hook(m->hapi_name, m->fn); | |
697 | } | |
698 | ||
699 | if(mheader->mapi_unregister) | |
700 | mheader->mapi_unregister(); | |
701 | break; | |
702 | } | |
8e9c6a75 EM |
703 | case 2: |
704 | { | |
705 | struct mapi_mheader_av2 *mheader = modlist[modindex]->mapi_header; | |
706 | ||
707 | /* XXX duplicate code :( */ | |
708 | if(mheader->mapi_command_list) | |
709 | { | |
710 | struct Message **m; | |
711 | for (m = mheader->mapi_command_list; *m; ++m) | |
712 | mod_del_cmd(*m); | |
713 | } | |
714 | ||
715 | /* hook events are never removed, we simply lose the | |
716 | * ability to call them --fl | |
717 | */ | |
718 | if(mheader->mapi_hfn_list) | |
719 | { | |
720 | mapi_hfn_list_av1 *m; | |
721 | for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) | |
722 | remove_hook(m->hapi_name, m->fn); | |
723 | } | |
724 | ||
725 | if(mheader->mapi_unregister) | |
726 | mheader->mapi_unregister(); | |
727 | ||
728 | if(mheader->mapi_cap_list) | |
729 | { | |
730 | mapi_cap_list_av2 *m; | |
731 | for (m = mheader->mapi_cap_list; m->cap_name; ++m) | |
732 | { | |
733 | struct CapabilityIndex *idx; | |
734 | ||
735 | switch(m->cap_index) | |
736 | { | |
737 | case MAPI_CAP_CLIENT: | |
738 | idx = cli_capindex; | |
739 | break; | |
740 | case MAPI_CAP_SERVER: | |
741 | idx = serv_capindex; | |
742 | break; | |
743 | default: | |
744 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
745 | "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", | |
746 | m->cap_index, m->cap_name, modlist[modindex]->name); | |
747 | ilog(L_MAIN, | |
748 | "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", | |
749 | m->cap_index, m->cap_name, modlist[modindex]->name); | |
750 | continue; | |
751 | } | |
752 | ||
753 | capability_orphan(idx, m->cap_name); | |
754 | } | |
755 | } | |
756 | } | |
212380e3 AC |
757 | default: |
758 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
759 | "Unknown/unsupported MAPI version %d when unloading %s!", | |
760 | modlist[modindex]->mapi_version, modlist[modindex]->name); | |
761 | ilog(L_MAIN, "Unknown/unsupported MAPI version %d when unloading %s!", | |
762 | modlist[modindex]->mapi_version, modlist[modindex]->name); | |
763 | break; | |
764 | } | |
765 | ||
f272e7ab | 766 | lt_dlclose(modlist[modindex]->address); |
212380e3 | 767 | |
637c4932 | 768 | rb_free(modlist[modindex]->name); |
55d5f797 | 769 | rb_free(modlist[modindex]); |
f54e1a8f | 770 | memmove(&modlist[modindex], &modlist[modindex + 1], |
1e170010 | 771 | sizeof(struct module *) * ((num_mods - 1) - modindex)); |
212380e3 AC |
772 | |
773 | if(num_mods != 0) | |
774 | num_mods--; | |
775 | ||
776 | if(warn == 1) | |
777 | { | |
778 | ilog(L_MAIN, "Module %s unloaded", name); | |
779 | sendto_realops_snomask(SNO_GENERAL, L_ALL, "Module %s unloaded", name); | |
780 | } | |
781 | ||
782 | return 0; | |
783 | } | |
784 | ||
785 | ||
786 | /* | |
787 | * load_a_module() | |
788 | * | |
216d70e9 | 789 | * inputs - path name of module, int to notice, int of origin, int of core |
212380e3 AC |
790 | * output - -1 if error 0 if success |
791 | * side effects - loads a module if successful | |
792 | */ | |
793 | int | |
216d70e9 | 794 | load_a_module(const char *path, int warn, int origin, int core) |
212380e3 | 795 | { |
f272e7ab | 796 | lt_dlhandle tmpptr; |
212380e3 | 797 | char *mod_basename; |
8e9c6a75 | 798 | const char *ver, *description = NULL; |
212380e3 AC |
799 | |
800 | int *mapi_version; | |
801 | ||
b7a689d1 | 802 | mod_basename = rb_basename(path); |
212380e3 | 803 | |
d76258f5 | 804 | tmpptr = lt_dlopen(path); |
212380e3 AC |
805 | |
806 | if(tmpptr == NULL) | |
807 | { | |
f272e7ab | 808 | const char *err = lt_dlerror(); |
212380e3 AC |
809 | |
810 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
811 | "Error loading module %s: %s", mod_basename, err); | |
812 | ilog(L_MAIN, "Error loading module %s: %s", mod_basename, err); | |
637c4932 | 813 | rb_free(mod_basename); |
212380e3 AC |
814 | return -1; |
815 | } | |
816 | ||
817 | ||
818 | /* | |
819 | * _mheader is actually a struct mapi_mheader_*, but mapi_version | |
820 | * is always the first member of this structure, so we treate it | |
821 | * as a single int in order to determine the API version. | |
822 | * -larne. | |
823 | */ | |
f272e7ab | 824 | mapi_version = (int *) (uintptr_t) lt_dlsym(tmpptr, "_mheader"); |
212380e3 | 825 | if((mapi_version == NULL |
f272e7ab | 826 | && (mapi_version = (int *) (uintptr_t) lt_dlsym(tmpptr, "__mheader")) == NULL) |
212380e3 AC |
827 | || MAPI_MAGIC(*mapi_version) != MAPI_MAGIC_HDR) |
828 | { | |
829 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
830 | "Data format error: module %s has no MAPI header.", | |
831 | mod_basename); | |
832 | ilog(L_MAIN, "Data format error: module %s has no MAPI header.", mod_basename); | |
f272e7ab | 833 | (void) lt_dlclose(tmpptr); |
637c4932 | 834 | rb_free(mod_basename); |
212380e3 AC |
835 | return -1; |
836 | } | |
837 | ||
838 | switch (MAPI_VERSION(*mapi_version)) | |
839 | { | |
840 | case 1: | |
841 | { | |
29c92cf9 | 842 | struct mapi_mheader_av1 *mheader = (struct mapi_mheader_av1 *)(void *)mapi_version; /* see above */ |
212380e3 AC |
843 | if(mheader->mapi_register && (mheader->mapi_register() == -1)) |
844 | { | |
845 | ilog(L_MAIN, "Module %s indicated failure during load.", | |
846 | mod_basename); | |
847 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
848 | "Module %s indicated failure during load.", | |
849 | mod_basename); | |
f272e7ab | 850 | lt_dlclose(tmpptr); |
637c4932 | 851 | rb_free(mod_basename); |
212380e3 AC |
852 | return -1; |
853 | } | |
854 | if(mheader->mapi_command_list) | |
855 | { | |
856 | struct Message **m; | |
857 | for (m = mheader->mapi_command_list; *m; ++m) | |
858 | mod_add_cmd(*m); | |
859 | } | |
860 | ||
861 | if(mheader->mapi_hook_list) | |
862 | { | |
863 | mapi_hlist_av1 *m; | |
864 | for (m = mheader->mapi_hook_list; m->hapi_name; ++m) | |
865 | *m->hapi_id = register_hook(m->hapi_name); | |
866 | } | |
867 | ||
868 | if(mheader->mapi_hfn_list) | |
869 | { | |
870 | mapi_hfn_list_av1 *m; | |
871 | for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) | |
872 | add_hook(m->hapi_name, m->fn); | |
873 | } | |
874 | ||
875 | ver = mheader->mapi_module_version; | |
876 | break; | |
877 | } | |
8e9c6a75 EM |
878 | case 2: |
879 | { | |
880 | struct mapi_mheader_av2 *mheader = (struct mapi_mheader_av2 *)(void *)mapi_version; /* see above */ | |
212380e3 | 881 | |
8e9c6a75 EM |
882 | /* XXX duplicated code :( */ |
883 | if(mheader->mapi_register && (mheader->mapi_register() == -1)) | |
884 | { | |
885 | ilog(L_MAIN, "Module %s indicated failure during load.", | |
886 | mod_basename); | |
887 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
888 | "Module %s indicated failure during load.", | |
889 | mod_basename); | |
890 | lt_dlclose(tmpptr); | |
891 | rb_free(mod_basename); | |
892 | return -1; | |
893 | } | |
894 | if(mheader->mapi_command_list) | |
895 | { | |
896 | struct Message **m; | |
897 | for (m = mheader->mapi_command_list; *m; ++m) | |
898 | mod_add_cmd(*m); | |
899 | } | |
900 | ||
901 | if(mheader->mapi_hook_list) | |
902 | { | |
903 | mapi_hlist_av1 *m; | |
904 | for (m = mheader->mapi_hook_list; m->hapi_name; ++m) | |
905 | *m->hapi_id = register_hook(m->hapi_name); | |
906 | } | |
907 | ||
908 | if(mheader->mapi_hfn_list) | |
909 | { | |
910 | mapi_hfn_list_av1 *m; | |
911 | for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) | |
912 | add_hook(m->hapi_name, m->fn); | |
913 | } | |
914 | ||
915 | /* New in MAPI v2 - version replacement */ | |
916 | ver = mheader->mapi_module_version ? mheader->mapi_module_version : ircd_version; | |
917 | description = mheader->mapi_module_description; | |
918 | ||
919 | if(mheader->mapi_cap_list) | |
920 | { | |
921 | mapi_cap_list_av2 *m; | |
922 | for (m = mheader->mapi_cap_list; m->cap_name; ++m) | |
923 | { | |
924 | struct CapabilityIndex *idx; | |
925 | int result; | |
926 | ||
927 | switch(m->cap_index) | |
928 | { | |
929 | case MAPI_CAP_CLIENT: | |
930 | idx = cli_capindex; | |
931 | break; | |
932 | case MAPI_CAP_SERVER: | |
933 | idx = serv_capindex; | |
934 | break; | |
935 | default: | |
936 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
937 | "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", | |
938 | m->cap_index, m->cap_name, mod_basename); | |
939 | ilog(L_MAIN, | |
940 | "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", | |
941 | m->cap_index, m->cap_name, mod_basename); | |
942 | continue; | |
943 | } | |
944 | ||
945 | result = capability_put(idx, m->cap_name, m->cap_ownerdata); | |
946 | if (m->cap_id != NULL) | |
947 | *(m->cap_id) = result; | |
948 | } | |
949 | } | |
950 | } | |
0e5bf029 EM |
951 | |
952 | break; | |
212380e3 AC |
953 | default: |
954 | ilog(L_MAIN, "Module %s has unknown/unsupported MAPI version %d.", | |
955 | mod_basename, MAPI_VERSION(*mapi_version)); | |
956 | sendto_realops_snomask(SNO_GENERAL, L_ALL, | |
957 | "Module %s has unknown/unsupported MAPI version %d.", | |
958 | mod_basename, *mapi_version); | |
f272e7ab | 959 | lt_dlclose(tmpptr); |
637c4932 | 960 | rb_free(mod_basename); |
212380e3 AC |
961 | return -1; |
962 | } | |
963 | ||
964 | if(ver == NULL) | |
965 | ver = unknown_ver; | |
966 | ||
8e9c6a75 EM |
967 | if(description == NULL) |
968 | description = unknown_description; | |
969 | ||
212380e3 AC |
970 | increase_modlist(); |
971 | ||
eddc2ab6 | 972 | modlist[num_mods] = rb_malloc(sizeof(struct module)); |
212380e3 AC |
973 | modlist[num_mods]->address = tmpptr; |
974 | modlist[num_mods]->version = ver; | |
0eb7d9c0 | 975 | modlist[num_mods]->description = description; |
212380e3 | 976 | modlist[num_mods]->core = core; |
47a03750 | 977 | modlist[num_mods]->name = rb_strdup(mod_basename); |
212380e3 AC |
978 | modlist[num_mods]->mapi_header = mapi_version; |
979 | modlist[num_mods]->mapi_version = MAPI_VERSION(*mapi_version); | |
c63aeb44 | 980 | modlist[num_mods]->origin = origin; |
212380e3 AC |
981 | num_mods++; |
982 | ||
983 | if(warn == 1) | |
984 | { | |
c63aeb44 EM |
985 | const char *o; |
986 | ||
987 | switch(origin) | |
988 | { | |
c63aeb44 EM |
989 | case MAPI_ORIGIN_EXTENSION: |
990 | o = "extension"; | |
991 | break; | |
992 | case MAPI_ORIGIN_CORE: | |
993 | o = "core"; | |
994 | break; | |
995 | default: | |
996 | o = "unknown"; | |
997 | break; | |
998 | } | |
999 | ||
212380e3 | 1000 | sendto_realops_snomask(SNO_GENERAL, L_ALL, |
c63aeb44 EM |
1001 | "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded at 0x%lx", |
1002 | mod_basename, ver, MAPI_VERSION(*mapi_version), o, description, | |
212380e3 | 1003 | (unsigned long) tmpptr); |
c63aeb44 EM |
1004 | ilog(L_MAIN, "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded at 0x%lx", |
1005 | mod_basename, ver, MAPI_VERSION(*mapi_version), o, description, (unsigned long) tmpptr); | |
212380e3 | 1006 | } |
637c4932 | 1007 | rb_free(mod_basename); |
212380e3 AC |
1008 | return 0; |
1009 | } | |
1010 | ||
1011 | /* | |
1012 | * increase_modlist | |
1013 | * | |
1014 | * inputs - NONE | |
1015 | * output - NONE | |
1016 | * side effects - expand the size of modlist if necessary | |
1017 | */ | |
1018 | static void | |
1019 | increase_modlist(void) | |
1020 | { | |
1021 | struct module **new_modlist = NULL; | |
1022 | ||
1023 | if((num_mods + 1) < max_mods) | |
1024 | return; | |
1025 | ||
1e170010 | 1026 | new_modlist = (struct module **) rb_malloc(sizeof(struct module *) * |
212380e3 | 1027 | (max_mods + MODS_INCREMENT)); |
1e170010 | 1028 | memcpy((void *) new_modlist, (void *) modlist, sizeof(struct module *) * num_mods); |
212380e3 | 1029 | |
637c4932 | 1030 | rb_free(modlist); |
212380e3 AC |
1031 | modlist = new_modlist; |
1032 | max_mods += MODS_INCREMENT; | |
1033 | } |