]> jfr.im git - solanum.git/blob - modules/core/m_modules.c
When a remote MODRESTART command is received, it will pass through the
[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 origin = strcmp(module, m_bn) == 0 ? MAPI_ORIGIN_CORE : MAPI_ORIGIN_EXTENSION;
278 load_one_module(module, origin, false);
279
280 rb_free(m_bn);
281 }
282
283 static void
284 do_modunload(struct Client *source_p, const char *module)
285 {
286 struct module *mod;
287 char *m_bn = rb_basename(module);
288
289 if((mod = findmodule_byname(m_bn)) == NULL)
290 {
291 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
292 rb_free(m_bn);
293 return;
294 }
295
296 if(mod->core)
297 {
298 sendto_one_notice(source_p, ":Module %s is a core module and may not be unloaded", m_bn);
299 rb_free(m_bn);
300 return;
301 }
302
303 if(unload_one_module(m_bn, true) == false)
304 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
305
306 rb_free(m_bn);
307 }
308
309 static void
310 do_modreload(struct Client *source_p, const char *module)
311 {
312 struct module *mod;
313 int check_core;
314 char *m_bn = rb_basename(module);
315
316 if((mod = findmodule_byname(m_bn)) == NULL)
317 {
318 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
319 rb_free(m_bn);
320 return;
321 }
322
323 check_core = mod->core;
324
325 if(unload_one_module(m_bn, true) == false)
326 {
327 sendto_one_notice(source_p, ":Module %s is not loaded", m_bn);
328 rb_free(m_bn);
329 return;
330 }
331
332 if((load_one_module(m_bn, mod->origin, check_core) == false) && check_core)
333 {
334 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
335 "Error reloading core module: %s: terminating ircd", m_bn);
336 ilog(L_MAIN, "Error loading core module %s: terminating ircd", m_bn);
337 exit(0);
338 }
339
340 rb_free(m_bn);
341 }
342
343 static void
344 do_modrestart(struct Client *source_p)
345 {
346 sendto_one_notice(source_p, ":Reloading all modules");
347
348 /*
349 * If a remote MODRESTART is received, m_encap.so will be reloaded,
350 * but ms_encap is in the call stack (it indirectly calls this
351 * function). Also, this function is itself in a module.
352 *
353 * This will go horribly wrong if either module is reloaded to a
354 * different address.
355 *
356 * So, defer the restart to the event loop and return now.
357 */
358 rb_event_addonce("modules_do_restart", modules_do_restart, NULL, 1);
359 }
360
361 static void
362 do_modlist(struct Client *source_p, const char *pattern)
363 {
364 rb_dlink_node *ptr;
365
366 RB_DLINK_FOREACH(ptr, module_list.head)
367 {
368 struct module *mod = ptr->data;
369 const char *origin;
370 switch (mod->origin)
371 {
372 case MAPI_ORIGIN_EXTENSION:
373 origin = "extension";
374 break;
375 case MAPI_ORIGIN_CORE:
376 origin = "builtin";
377 break;
378 default:
379 origin = "unknown";
380 break;
381 }
382
383 if(pattern)
384 {
385 if(match(pattern, mod->name))
386 {
387 sendto_one(source_p, form_str(RPL_MODLIST),
388 me.name, source_p->name,
389 mod->name,
390 (unsigned long)(uintptr_t)mod->address, origin,
391 mod->core ? " (core)" : "", mod->version, mod->description);
392 }
393 }
394 else
395 {
396 sendto_one(source_p, form_str(RPL_MODLIST),
397 me.name, source_p->name, mod->name,
398 (unsigned long)(uintptr_t)mod->address, origin,
399 mod->core ? " (core)" : "", mod->version, mod->description);
400 }
401 }
402
403 sendto_one(source_p, form_str(RPL_ENDOFMODLIST), me.name, source_p->name);
404 }