]> jfr.im git - solanum.git/blob - modules/core/m_modules.c
Merge pull request #277 from edk0/helpops
[solanum.git] / modules / core / m_modules.c
1 /* modules/m_modules.c - module for module loading
2 * Copyright (c) 2016 Elizabeth Myers <elizabeth@interlinked.me>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice is present in all copies.
7 *
8 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
12 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
14 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
16 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
17 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
18 * POSSIBILITY OF SUCH DAMAGE.
19 */
20
21 #include "stdinc.h"
22 #include "client.h"
23 #include "parse.h"
24 #include "msg.h"
25 #include "modules.h"
26 #include "s_newconf.h"
27 #include "s_conf.h"
28 #include "s_serv.h"
29 #include "hash.h"
30 #include "ircd.h"
31 #include "match.h"
32 #include "numeric.h"
33 #include "send.h"
34 #include "packet.h"
35 #include "logger.h"
36
37 static const char modules_desc[] = "Provides module management commands";
38
39 static void mo_modload(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
40 static void mo_modlist(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
41 static void mo_modreload(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
42 static void mo_modunload(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
43 static void mo_modrestart(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
44
45 static void me_modload(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
46 static void me_modlist(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
47 static void me_modreload(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
48 static void me_modunload(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
49 static void me_modrestart(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
50
51 static void do_modload(struct Client *, const char *);
52 static void do_modunload(struct Client *, const char *);
53 static void do_modreload(struct Client *, const char *);
54 static void do_modlist(struct Client *, const char *);
55 static void do_modrestart(struct Client *);
56
57 extern void modules_do_restart(void *); /* end of ircd/modules.c */
58
59 struct Message modload_msgtab = {
60 "MODLOAD", 0, 0, 0, 0,
61 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modload, 2}, {mo_modload, 2}}
62 };
63
64 struct Message modunload_msgtab = {
65 "MODUNLOAD", 0, 0, 0, 0,
66 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modunload, 2}, {mo_modunload, 2}}
67 };
68
69 struct Message modreload_msgtab = {
70 "MODRELOAD", 0, 0, 0, 0,
71 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modreload, 2}, {mo_modreload, 2}}
72 };
73
74 struct Message modlist_msgtab = {
75 "MODLIST", 0, 0, 0, 0,
76 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modlist, 0}, {mo_modlist, 0}}
77 };
78
79 struct Message modrestart_msgtab = {
80 "MODRESTART", 0, 0, 0, 0,
81 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_modrestart, 0}, {mo_modrestart, 0}}
82 };
83
84 mapi_clist_av1 modules_clist[] = { &modload_msgtab, &modunload_msgtab, &modreload_msgtab, &modlist_msgtab, &modrestart_msgtab, NULL };
85
86 DECLARE_MODULE_AV2(modules, NULL, NULL, modules_clist, NULL, NULL, NULL, NULL, modules_desc);
87
88 /* load a module .. */
89 static void
90 mo_modload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
91 {
92 if(!IsOperAdmin(source_p))
93 {
94 sendto_one(source_p, form_str(ERR_NOPRIVS),
95 me.name, source_p->name, "admin");
96 return;
97 }
98
99 if(parc > 2)
100 {
101 sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS,
102 "ENCAP %s MODLOAD %s", parv[2], parv[1]);
103 if(match(parv[2], me.name) == 0)
104 return;
105 }
106
107 do_modload(source_p, parv[1]);
108 }
109
110 static void
111 me_modload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
112 {
113 if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE))
114 {
115 sendto_one_notice(source_p, ":*** You do not have an appropriate shared block "
116 "to load modules on this server.");
117 return;
118 }
119
120 do_modload(source_p, parv[1]);
121 }
122
123
124 /* unload a module .. */
125 static void
126 mo_modunload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
127 {
128 if(!IsOperAdmin(source_p))
129 {
130 sendto_one(source_p, form_str(ERR_NOPRIVS),
131 me.name, source_p->name, "admin");
132 return;
133 }
134
135 if(parc > 2)
136 {
137 sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS,
138 "ENCAP %s MODUNLOAD %s", parv[2], parv[1]);
139 if(match(parv[2], me.name) == 0)
140 return;
141 }
142
143 do_modunload(source_p, parv[1]);
144 }
145
146 static void
147 me_modunload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
148 {
149 if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE))
150 {
151 sendto_one_notice(source_p, ":*** You do not have an appropriate shared block "
152 "to load modules on this server.");
153 return;
154 }
155
156 do_modunload(source_p, parv[1]);
157 }
158
159 /* unload and load in one! */
160 static void
161 mo_modreload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
162 {
163 if(!IsOperAdmin(source_p))
164 {
165 sendto_one(source_p, form_str(ERR_NOPRIVS),
166 me.name, source_p->name, "admin");
167 return;
168 }
169
170 if(parc > 2)
171 {
172 sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS,
173 "ENCAP %s MODRELOAD %s", parv[2], parv[1]);
174 if(match(parv[2], me.name) == 0)
175 return;
176 }
177
178 do_modreload(source_p, parv[1]);
179 }
180
181 static void
182 me_modreload(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
183 {
184 if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE))
185 {
186 sendto_one_notice(source_p, ":*** You do not have an appropriate shared block "
187 "to load modules on this server.");
188 return;
189 }
190
191 do_modreload(source_p, parv[1]);
192 }
193
194 /* list modules .. */
195 static void
196 mo_modlist(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
197 {
198 if(!IsOperAdmin(source_p))
199 {
200 sendto_one(source_p, form_str(ERR_NOPRIVS),
201 me.name, source_p->name, "admin");
202 return;
203 }
204
205 if(parc > 2)
206 {
207 sendto_match_servs(source_p, parv[2], CAP_ENCAP, NOCAPS,
208 "ENCAP %s MODLIST %s", parv[2], parv[1]);
209 if(match(parv[2], me.name) == 0)
210 return;
211 }
212
213 do_modlist(source_p, parc > 1 ? parv[1] : 0);
214 }
215
216 static void
217 me_modlist(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
218 {
219 if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE))
220 {
221 sendto_one_notice(source_p, ":*** You do not have an appropriate shared block "
222 "to load modules on this server.");
223 return;
224 }
225
226 do_modlist(source_p, parv[1]);
227 }
228
229 /* unload and reload all modules */
230 static void
231 mo_modrestart(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
232 {
233 if(!IsOperAdmin(source_p))
234 {
235 sendto_one(source_p, form_str(ERR_NOPRIVS),
236 me.name, source_p->name, "admin");
237 return;
238 }
239
240 if(parc > 1)
241 {
242 sendto_match_servs(source_p, parv[1], CAP_ENCAP, NOCAPS,
243 "ENCAP %s MODRESTART", parv[1]);
244 if(match(parv[1], me.name) == 0)
245 return;
246 }
247
248 do_modrestart(source_p);
249 }
250
251 static void
252 me_modrestart(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char **parv)
253 {
254 if(!find_shared_conf(source_p->username, source_p->host, source_p->servptr->name, SHARED_MODULE))
255 {
256 sendto_one_notice(source_p, ":*** You do not have an appropriate shared block "
257 "to load modules on this server.");
258 return;
259 }
260
261 do_modrestart(source_p);
262 }
263
264 static void
265 do_modload(struct Client *source_p, const char *module)
266 {
267 char *m_bn = rb_basename(module);
268 int origin;
269
270 if(findmodule_byname(m_bn) != NULL)
271 {
272 sendto_one_notice(source_p, ":Module %s is already loaded", m_bn);
273 rb_free(m_bn);
274 return;
275 }
276
277 mod_remember_clicaps();
278
279 origin = strcmp(module, m_bn) == 0 ? MAPI_ORIGIN_CORE : MAPI_ORIGIN_EXTENSION;
280 load_one_module(module, origin, false);
281
282 mod_notify_clicaps();
283
284 rb_free(m_bn);
285 }
286
287 static void
288 do_modunload(struct Client *source_p, const char *module)
289 {
290 struct module *mod;
291 char *m_bn = rb_basename(module);
292
293 if((mod = findmodule_byname(m_bn)) == NULL)
294 {
295 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
296 rb_free(m_bn);
297 return;
298 }
299
300 if(mod->core)
301 {
302 sendto_one_notice(source_p, ":Module %s is a core module and may not be unloaded", m_bn);
303 rb_free(m_bn);
304 return;
305 }
306
307 mod_remember_clicaps();
308
309 if(unload_one_module(m_bn, true) == false)
310 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
311
312 mod_notify_clicaps();
313
314 rb_free(m_bn);
315 }
316
317 static void
318 do_modreload(struct Client *source_p, const char *module)
319 {
320 struct module *mod;
321 int check_core;
322 char *m_bn = rb_basename(module);
323
324 if((mod = findmodule_byname(m_bn)) == NULL)
325 {
326 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
327 rb_free(m_bn);
328 return;
329 }
330
331 check_core = mod->core;
332
333 mod_remember_clicaps();
334
335 if(unload_one_module(m_bn, true) == false)
336 {
337 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
338 rb_free(m_bn);
339 return;
340 }
341
342 if((load_one_module(m_bn, mod->origin, check_core) == false) && check_core)
343 {
344 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
345 "Error reloading core module: %s: terminating ircd", m_bn);
346 ilog(L_MAIN, "Error loading core module %s: terminating ircd", m_bn);
347 exit(0);
348 }
349
350 mod_notify_clicaps();
351
352 rb_free(m_bn);
353 }
354
355 static void
356 do_modrestart(struct Client *source_p)
357 {
358 sendto_one_notice(source_p, ":Reloading all modules");
359
360 /*
361 * If a remote MODRESTART is received, m_encap.so will be reloaded,
362 * but ms_encap is in the call stack (it indirectly calls this
363 * function). Also, this function is itself in a module.
364 *
365 * This will go horribly wrong if either module is reloaded to a
366 * different address.
367 *
368 * So, defer the restart to the event loop and return now.
369 */
370 rb_event_addonce("modules_do_restart", modules_do_restart, NULL, 1);
371 }
372
373 static void
374 do_modlist(struct Client *source_p, const char *pattern)
375 {
376 rb_dlink_node *ptr;
377
378 RB_DLINK_FOREACH(ptr, module_list.head)
379 {
380 struct module *mod = ptr->data;
381 const char *origin;
382 switch (mod->origin)
383 {
384 case MAPI_ORIGIN_EXTENSION:
385 origin = "extension";
386 break;
387 case MAPI_ORIGIN_CORE:
388 origin = "builtin";
389 break;
390 default:
391 origin = "unknown";
392 break;
393 }
394
395 if(pattern)
396 {
397 if(match(pattern, mod->name))
398 {
399 sendto_one(source_p, form_str(RPL_MODLIST),
400 me.name, source_p->name,
401 mod->name,
402 (unsigned long)(uintptr_t)mod->address, origin,
403 mod->core ? " (core)" : "", mod->version, mod->description);
404 }
405 }
406 else
407 {
408 sendto_one(source_p, form_str(RPL_MODLIST),
409 me.name, source_p->name, mod->name,
410 (unsigned long)(uintptr_t)mod->address, origin,
411 mod->core ? " (core)" : "", mod->version, mod->description);
412 }
413 }
414
415 sendto_one(source_p, form_str(RPL_ENDOFMODLIST), me.name, source_p->name);
416 }