]> jfr.im git - irc/quakenet/newserv.git/blame - trusts/trusts_management.c
Another query fix.
[irc/quakenet/newserv.git] / trusts / trusts_management.c
CommitLineData
35449aa5
CP
1#include <stdio.h>
2#include <string.h>
3#include "../control/control.h"
4#include "../lib/irc_string.h"
5#include "../lib/strlfunc.h"
82a316e7 6#include "../core/config.h"
caf2d02a 7#include "../core/schedule.h"
2ab0a1e7 8#include "../lib/stringbuf.h"
35449aa5
CP
9#include "trusts.h"
10
11static void registercommands(int, void *);
12static void deregistercommands(int, void *);
13
2ab0a1e7
CP
14typedef int (*trustmodificationfn)(trustgroup *, char *arg);
15
16struct trustmodification {
1d9ccd69 17 char name[50];
2ab0a1e7
CP
18 trustmodificationfn fn;
19};
20
35449aa5
CP
21static int trusts_cmdtrustadd(void *source, int cargc, char **cargv) {
22 trustgroup *tg;
23 nick *sender = source;
24 char *host;
25 uint32_t ip, mask;
26 trusthost *th, *superset, *subset;
27
28 if(cargc < 2)
29 return CMD_USAGE;
30
31 tg = tg_strtotg(cargv[0]);
32 if(!tg) {
33 controlreply(sender, "Couldn't look up trustgroup.");
34 return CMD_ERROR;
35 }
36
37 host = cargv[1];
38 if(!trusts_str2cidr(host, &ip, &mask)) {
39 controlreply(sender, "Invalid host.");
40 return CMD_ERROR;
41 }
42
43 /* OKAY! Lots of checking here!
44 *
45 * Need to check:
46 * - host isn't already covered by given group (reject if it is)
47 * - host doesn't already exist exactly already (reject if it does)
48 * - host is more specific than an existing one (warn if it is, fix up later)
49 * - host is less specific than an existing one (warn if it is, don't need to do anything special)
50 */
51
52 for(th=tg->hosts;th;th=th->next) {
53 if(th->ip == (ip & th->mask)) {
54 controlreply(sender, "This host (or part of it) is already covered in the given group.");
55 return CMD_ERROR;
56 }
57 }
58
59 if(th_getbyhostandmask(ip, mask)) {
60 controlreply(sender, "This host already exists in another group with the same mask.");
61 return CMD_ERROR;
62 }
63
64 /* this function will set both to NULL if it's equal, hence the check above */
65 th_getsuperandsubsets(ip, mask, &superset, &subset);
66 if(superset) {
67 /* a superset exists for us, we will be more specific than one existing host */
68
69 controlreply(sender, "Warning: this host already exists in another group, but this new host will override it as it has a smaller prefix.");
70 }
71 if(subset) {
72 /* a subset of us exists, we will be less specific than some existing hosts */
73
74 controlreply(sender, "Warning: this host already exists in at least one other group, the new host has a larger prefix and therefore will not override those hosts.");
75 }
76 if(superset || subset)
77 controlreply(sender, "Adding anyway...");
78
79 th = th_new(tg, host);
80 if(!th) {
81 controlreply(sender, "An error occured adding the host to the group.");
82 return CMD_ERROR;
83 }
84
85 controlreply(sender, "Host added.");
82a316e7
CP
86 triggerhook(HOOK_TRUSTS_ADDHOST, th);
87
7e11a2c6 88 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTADD'ed host %s to group '%s'", controlid(sender), host, th->group->name->content);
1467e9a4 89 trustlog(tg, controlid(sender), "Added host '%s'.", host);
35449aa5
CP
90
91 return CMD_OK;
92}
93
94static int trusts_cmdtrustgroupadd(void *source, int cargc, char **cargv) {
95 nick *sender = source;
96 char *name, *contact, *comment, createdby[ACCOUNTLEN + 2];
97 unsigned int howmany, maxperident, enforceident;
1467e9a4 98 time_t howlong, expires;
82a316e7 99 trustgroup *tg, itg;
35449aa5
CP
100
101 if(cargc < 6)
102 return CMD_USAGE;
103
104 name = cargv[0];
105 howmany = strtoul(cargv[1], NULL, 10);
2ab0a1e7 106 if(!howmany || (howmany > MAXTRUSTEDFOR)) {
35449aa5
CP
107 controlreply(sender, "Bad value maximum number of clients.");
108 return CMD_ERROR;
109 }
110
111 howlong = durationtolong(cargv[2]);
ad3fd5ee 112 if((howlong < 0) || (howlong > MAXDURATION)) {
35449aa5
CP
113 controlreply(sender, "Invalid duration supplied.");
114 return CMD_ERROR;
115 }
116
1467e9a4
GB
117 if(howlong)
118 expires = howlong + time(NULL);
119 else
120 expires = 0;
121
35449aa5 122 maxperident = strtoul(cargv[3], NULL, 10);
2ab0a1e7 123 if(!howmany || (maxperident > MAXPERIDENT)) {
35449aa5
CP
124 controlreply(sender, "Bad value for max per ident.");
125 return CMD_ERROR;
126 }
127
128 if(cargv[4][0] != '1' && cargv[4][0] != '0') {
129 controlreply(sender, "Bad value for enforce ident (use 0 or 1).");
130 return CMD_ERROR;
131 }
132 enforceident = cargv[4][0] == '1';
133
134 contact = cargv[5];
135
136 if(cargc < 7) {
137 comment = "(no comment)";
138 } else {
139 comment = cargv[6];
140 }
141
142 /* don't allow #id or id forms */
143 if((name[0] == '#') || strtoul(name, NULL, 10)) {
144 controlreply(sender, "Invalid trustgroup name.");
145 return CMD_ERROR;
146 }
147
148 tg = tg_strtotg(name);
149 if(tg) {
150 controlreply(sender, "A group with that name already exists");
151 return CMD_ERROR;
152 }
153
154 snprintf(createdby, sizeof(createdby), "#%s", sender->authname);
155
82a316e7
CP
156 itg.trustedfor = howmany;
157 itg.mode = enforceident;
158 itg.maxperident = maxperident;
1467e9a4 159 itg.expires = expires;
1f685425 160 itg.createdby = getsstring(createdby, CREATEDBYLEN);
82a316e7
CP
161 itg.contact = getsstring(contact, CONTACTLEN);
162 itg.comment = getsstring(comment, COMMENTLEN);
163 itg.name = getsstring(name, TRUSTNAMELEN);
164
165 if(itg.createdby && itg.contact && itg.comment && itg.name) {
166 tg = tg_new(&itg);
167 } else {
168 tg = NULL;
169 }
170
171 freesstring(itg.createdby);
172 freesstring(itg.comment);
173 freesstring(itg.name);
174 freesstring(itg.contact);
175
35449aa5
CP
176 if(!tg) {
177 controlreply(sender, "An error occured adding the trustgroup.");
178 return CMD_ERROR;
179 }
180
181 controlreply(sender, "Group added.");
82a316e7
CP
182 triggerhook(HOOK_TRUSTS_ADDGROUP, tg);
183
7e11a2c6 184 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTGROUPADD'ed '%s'", controlid(sender), tg->name->content);
1467e9a4 185 trustlog(tg, controlid(sender), "Created trust group '%s' (ID #%d): howmany=%d, enforceident=%d, maxperident=%d, "
01bd21d3 186 "expires=%d, createdby=%s, contact=%s, comment=%s",
1467e9a4 187 tg->name->content, howmany, tg->id, enforceident, maxperident, expires, createdby, contact, comment);
35449aa5
CP
188
189 return CMD_OK;
190}
191
2ab0a1e7
CP
192static int trusts_cmdtrustgroupdel(void *source, int cargc, char **cargv) {
193 trustgroup *tg;
194 nick *sender = source;
195
196 if(cargc < 1)
197 return CMD_USAGE;
198
199 tg = tg_strtotg(cargv[0]);
200 if(!tg) {
201 controlreply(sender, "Couldn't look up trustgroup.");
202 return CMD_ERROR;
203 }
204
205 if(tg->hosts) {
206 controlreply(sender, "Delete all hosts before deleting the group.");
207 return CMD_ERROR;
208 }
209
7e11a2c6 210 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTGROUPDEL'ed '%s'.", controlid(sender), tg->name->content);
1467e9a4 211 trustlog(tg, controlid(sender), "Deleted group '%s'.", tg->name->content);
7e11a2c6 212
2ab0a1e7
CP
213 triggerhook(HOOK_TRUSTS_DELGROUP, tg);
214 tg_delete(tg);
215 controlreply(sender, "Group deleted.");
216
2ab0a1e7
CP
217 return CMD_OK;
218}
219
220static int trusts_cmdtrustdel(void *source, int cargc, char **cargv) {
221 trustgroup *tg;
222 trusthost *th;
223 uint32_t ip, mask;
224 nick *sender = source;
7e11a2c6 225 char *host;
2ab0a1e7
CP
226
227 if(cargc < 2)
228 return CMD_USAGE;
229
230 tg = tg_strtotg(cargv[0]);
231 if(!tg) {
232 controlreply(sender, "Couldn't look up trustgroup.");
233 return CMD_ERROR;
234 }
235
7e11a2c6
CP
236 host = cargv[1];
237 if(!trusts_str2cidr(host, &ip, &mask)) {
2ab0a1e7
CP
238 controlreply(sender, "Invalid IP/mask.");
239 return CMD_ERROR;
240 }
241
242 for(th=tg->hosts;th;th=th->next)
243 if((th->ip == ip) && (th->mask == mask))
244 break;
245
246 if(!th) {
247 controlreply(sender, "Couldn't find that host in that group.");
248 return CMD_ERROR;
249 }
250
251 triggerhook(HOOK_TRUSTS_DELHOST, th);
252 th_delete(th);
253 controlreply(sender, "Host deleted.");
254
7e11a2c6 255 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTDEL'ed %s from group '%s'.", controlid(sender), host, tg->name->content);
1467e9a4 256 trustlog(tg, controlid(sender), "Removed host '%s'.", host);
2ab0a1e7 257
7e11a2c6 258 return CMD_OK;
2ab0a1e7
CP
259}
260
261static int modifycomment(trustgroup *tg, char *comment) {
262 sstring *n = getsstring(comment, COMMENTLEN);
263 if(!n)
264 return 0;
265
266 freesstring(tg->comment);
267 tg->comment = n;
268
269 return 1;
270}
271
272static int modifycontact(trustgroup *tg, char *contact) {
273 sstring *n = getsstring(contact, CONTACTLEN);
274 if(!n)
275 return 0;
276
277 freesstring(tg->contact);
278 tg->contact = n;
279
280 return 1;
281}
282
283static int modifytrustedfor(trustgroup *tg, char *num) {
284 unsigned int trustedfor = strtoul(num, NULL, 10);
285
286 if(trustedfor > MAXTRUSTEDFOR)
287 return 0;
288
289 tg->trustedfor = trustedfor;
290
291 return 1;
292}
293
1f685425
CP
294static int modifymaxperident(trustgroup *tg, char *num) {
295 unsigned int maxperident = strtoul(num, NULL, 10);
2ab0a1e7 296
1f685425 297 if(maxperident > MAXPERIDENT)
2ab0a1e7
CP
298 return 0;
299
1f685425 300 tg->maxperident = maxperident;
2ab0a1e7
CP
301
302 return 1;
303}
304
305static int modifyenforceident(trustgroup *tg, char *num) {
306 if(num[0] == '1') {
307 tg->mode = 1;
308 } else if(num[0] == '0') {
309 tg->mode = 0;
310 } else {
311 return 0;
312 }
313
314 return 1;
315}
316
317static int modifyexpires(trustgroup *tg, char *expires) {
318 int howlong = durationtolong(expires);
319
ad3fd5ee 320 if((howlong < 0) || (howlong > MAXDURATION))
2ab0a1e7
CP
321 return 0;
322
ad3fd5ee
GB
323 if(howlong)
324 tg->expires = time(NULL) + howlong;
325 else
326 tg->expires = 0; /* never */
2ab0a1e7
CP
327
328 return 1;
329}
330
1d9ccd69
CP
331static array trustmods_a;
332static struct trustmodification *trustmods;
2ab0a1e7
CP
333
334static int trusts_cmdtrustgroupmodify(void *source, int cargc, char **cargv) {
335 trustgroup *tg;
336 nick *sender = source;
337 char *what, *to, validfields[512];
2ab0a1e7
CP
338 int i;
339 StringBuf b;
2ab0a1e7
CP
340
341 if(cargc < 3)
342 return CMD_USAGE;
343
344 tg = tg_strtotg(cargv[0]);
345 if(!tg) {
346 controlreply(sender, "Couldn't look up trustgroup.");
347 return CMD_ERROR;
348 }
349
350 what = cargv[1];
351 to = cargv[2];
352
353 sbinit(&b, validfields, sizeof(validfields));
1d9ccd69
CP
354 for(i=0;i<trustmods_a.cursi;i++) {
355 if(!strcmp(what, trustmods[i].name)) {
356 if(!(trustmods[i].fn)(tg, to)) {
2ab0a1e7
CP
357 controlreply(sender, "An error occured changing that property, check the syntax.");
358 return CMD_ERROR;
359 }
360 break;
361 }
362
363 if(i > 0)
364 sbaddstr(&b, ", ");
1d9ccd69 365 sbaddstr(&b, trustmods[i].name);
2ab0a1e7
CP
366 }
367
1d9ccd69 368 if(i == trustmods_a.cursi) {
2ab0a1e7
CP
369 sbterminate(&b);
370 controlreply(sender, "No such field, valid fields are: %s", validfields);
371 return CMD_ERROR;
372 }
373
374 triggerhook(HOOK_TRUSTS_MODIFYGROUP, tg);
375 tg_update(tg);
376 controlreply(sender, "Group modified.");
377
7e11a2c6 378 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTMODIFIED'ed group '%s' (field: %s, value: %s)", controlid(sender), tg->name->content, what, to);
1467e9a4 379 trustlog(tg, controlid(sender), "Modified %s: %s", what, to);
7e11a2c6 380
2ab0a1e7
CP
381 return CMD_OK;
382}
383
01bd21d3
GB
384static int trusts_cmdtrustlogspew(void *source, int cargc, char **cargv) {
385 nick *sender = source;
01bd21d3
GB
386 char *name;
387 int groupid;
388 int limit = 0;
389
390 if(cargc < 1)
391 return CMD_USAGE;
392
01bd21d3
GB
393 if(cargc>1)
394 limit = strtoul(cargv[1], NULL, 10);
395
396 if(limit==0)
397 limit = 100;
398
1467e9a4
GB
399 name = cargv[0];
400
401 if (name[0] == '#') {
402 groupid = strtoul(name + 1, NULL, 10);
403 trustlogspewid(sender, groupid, limit);
404 } else {
405 trustlogspewname(sender, name, limit);
406 }
01bd21d3
GB
407
408 return CMD_OK;
409}
410
411static int trusts_cmdtrustloggrep(void *source, int cargc, char **cargv) {
412 nick *sender = source;
413 char *pattern;
414 int limit = 0;
415
416 if(cargc < 1)
417 return CMD_USAGE;
418
419 pattern = cargv[0];
420
421 if(cargc>1)
422 limit = strtoul(cargv[1], NULL, 10);
423
424 if(limit==0)
425 limit = 100;
426
427 trustloggrep(sender, pattern, limit);
428
429 return CMD_OK;
430}
431
432static int trusts_cmdtrustcomment(void *source, int cargc, char **cargv) {
433 nick *sender = source;
434 trustgroup *tg = NULL;
435 char *name, *comment;
436
437 if(cargc < 2)
438 return CMD_USAGE;
439
440 name = cargv[0];
441 comment = cargv[1];
442
443 tg = tg_strtotg(name);
444
445 if(!tg) {
446 controlreply(sender, "Invalid trust group name or ID.");
447 return CMD_OK;
448 }
449
52644481 450 controlwall(NO_OPER, NL_TRUSTS, "%s TRUSTCOMMENT'ed group '%s': %s", controlid(sender), tg->name->content, comment);
1467e9a4 451 trustlog(tg, controlid(sender), "Comment: %s", comment);
01bd21d3
GB
452
453 return CMD_OK;
454}
455
caf2d02a
GB
456static void cleanuptrusts(void *arg);
457
458static int trusts_cmdtrustcleanup(void *source, int cargc, char **cargv) {
459 cleanuptrusts(source);
460
461 controlreply(source, "Done.");
462
463 return CMD_OK;
464}
465
01bd21d3 466
35449aa5
CP
467static int commandsregistered;
468
469static void registercommands(int hooknum, void *arg) {
470 if(commandsregistered)
471 return;
472 commandsregistered = 1;
473
56f4ec56 474 registercontrolhelpcmd("trustgroupadd", NO_OPER, 7, trusts_cmdtrustgroupadd, "Usage: trustgroupadd <name> <howmany> <howlong> <maxperident> <enforceident> <contact> ?comment?");
35449aa5 475 registercontrolhelpcmd("trustadd", NO_OPER, 2, trusts_cmdtrustadd, "Usage: trustadd <#id|name|id> <host>");
2ab0a1e7
CP
476 registercontrolhelpcmd("trustgroupdel", NO_OPER, 1, trusts_cmdtrustgroupdel, "Usage: trustgroupdel <#id|name|id>");
477 registercontrolhelpcmd("trustdel", NO_OPER, 2, trusts_cmdtrustdel, "Usage: trustdel <#id|name|id> <ip/mask>");
478 registercontrolhelpcmd("trustgroupmodify", NO_OPER, 3, trusts_cmdtrustgroupmodify, "Usage: trustgroupmodify <#id|name|id> <field> <new value>");
35c3513c
GB
479 registercontrolhelpcmd("trustlogspew", NO_OPER, 2, trusts_cmdtrustlogspew, "Usage: trustlogspew <#id|name> ?limit?\nShows log for the specified trust group.");
480 registercontrolhelpcmd("trustloggrep", NO_OPER, 2, trusts_cmdtrustloggrep, "Usage trustloggrep <pattern> ?limit?\nShows maching log entries.");
01bd21d3 481 registercontrolhelpcmd("trustcomment", NO_OPER, 2, trusts_cmdtrustcomment, "Usage: trustcomment <#id|name> <comment>\nLogs a comment for a trust.");
caf2d02a 482 registercontrolhelpcmd("trustcleanup", NO_DEVELOPER, 0, trusts_cmdtrustcleanup, "Usage: trustcleanup\nCleans up unused trusts.");
35449aa5
CP
483}
484
485static void deregistercommands(int hooknum, void *arg) {
486 if(!commandsregistered)
487 return;
488 commandsregistered = 0;
489
490 deregistercontrolcmd("trustgroupadd", trusts_cmdtrustgroupadd);
491 deregistercontrolcmd("trustadd", trusts_cmdtrustadd);
2ab0a1e7
CP
492 deregistercontrolcmd("trustgroupdel", trusts_cmdtrustgroupdel);
493 deregistercontrolcmd("trustdel", trusts_cmdtrustdel);
494 deregistercontrolcmd("trustgroupmodify", trusts_cmdtrustgroupmodify);
01bd21d3
GB
495 deregistercontrolcmd("trustlogspew", trusts_cmdtrustlogspew);
496 deregistercontrolcmd("trustloggrep", trusts_cmdtrustloggrep);
497 deregistercontrolcmd("trustcomment", trusts_cmdtrustcomment);
caf2d02a 498 deregistercontrolcmd("trustcleanup", trusts_cmdtrustcleanup);
35449aa5
CP
499}
500
82a316e7
CP
501static int loaded;
502
1d9ccd69
CP
503#define _ms_(x) (struct trustmodification){ .name = # x, .fn = modify ## x }
504#define MS(x) { int slot = array_getfreeslot(&trustmods_a); trustmods = (struct trustmodification *)trustmods_a.content; memcpy(&trustmods[slot], &_ms_(x), sizeof(struct trustmodification)); }
505
506static void setupmods(void) {
507 MS(expires);
508 MS(enforceident);
509 MS(maxperident);
510 MS(contact);
511 MS(comment);
512 MS(trustedfor);
513}
514
caf2d02a
GB
515static int cleanuptrusts_active;
516
517static void cleanuptrusts(void *arg) {
518 unsigned int now, to_age;
519 nick *np = (nick *)arg;
520 trustgroup *tg;
521 trusthost *th;
522 int thcount = 0, tgcount = 0;
523 int i;
524 array expiredths, expiredtgs;
525
526 now = time(NULL);
527 to_age = now - (CLEANUP_TH_INACTIVE * 3600 * 24);
528
529 if(np) {
530 controlwall(NO_OPER, NL_TRUSTS, "CLEANUPTRUSTS: Manually started by %s.", np->nick);
531 } else {
532 controlwall(NO_OPER, NL_TRUSTS, "CLEANUPTRUSTS: Automatically started.");
533 }
534
535 if (cleanuptrusts_active) {
536 controlwall(NO_OPER, NL_TRUSTS, "CLEANUPTRUSTS: ABORTED! Cleanup already in progress! BUG BUG BUG!");
537 return;
538 }
539
540 cleanuptrusts_active=1;
541
542 array_init(&expiredtgs, sizeof(trustgroup *));
543
544 for(tg=tglist;tg;tg=tg->next) {
545 array_init(&expiredths, sizeof(trusthost *));
546
547 for(th=tg->hosts;th;th=th->next) {
548 if((th->count == 0 && th->created < to_age && th->lastseen < to_age) || (tg->expires && tg->expires < now)) {
549 int pos = array_getfreeslot(&expiredths);
550 ((trusthost **)(expiredths.content))[pos] = th;
551 }
552 }
553
554 for(i=0;i<expiredths.cursi;i++) {
555 char *cidrstr;
556
557 th = ((trusthost **)(expiredths.content))[i];
558 triggerhook(HOOK_TRUSTS_DELHOST, th);
559 th_delete(th);
560
561 cidrstr = trusts_cidr2str(th->ip, th->mask);
562 trustlog(tg, "cleanuptrusts", "Removed host '%s' because it was unused for %d days.", cidrstr, CLEANUP_TH_INACTIVE);
563
564 thcount++;
565 }
566
567 if(!tg->hosts) {
568 int pos = array_getfreeslot(&expiredtgs);
569 ((trustgroup **)(expiredtgs.content))[pos] = tg;
570 }
571 }
572
573 for(i=0;i<expiredtgs.cursi;i++) {
574 tg = ((trustgroup **)(expiredtgs.content))[i];
575 triggerhook(HOOK_TRUSTS_DELGROUP, tg);
576 trustlog(tg, "cleanuptrusts", "Deleted group '%s' because it had no hosts left.", tg->name->content);
577 tg_delete(tg);
578 tgcount++;
579 }
580
581 controlwall(NO_OPER, NL_TRUSTS, "CLEANUPTRUSTS: Removed %d trust hosts (inactive for %d days) and %d trust groups.", thcount, CLEANUP_TH_INACTIVE, tgcount);
582
583 cleanuptrusts_active=0;
584}
585
586static void schedulecleanup(int hooknum, void *arg) {
587 /* run at 1am but only if we're more than 15m away from it, otherwise run tomorrow */
588
589 time_t t = time(NULL);
590 time_t next_run = ((t / 86400) * 86400 + 86400) + 3600;
591 if(next_run - t < 900)
592 next_run+=86400;
593
594 schedulerecurring(next_run,0,86400,cleanuptrusts,NULL);
595}
596
35449aa5 597void _init(void) {
82a316e7
CP
598 sstring *m;
599
1d9ccd69
CP
600 array_init(&trustmods_a, sizeof(struct trustmodification));
601 setupmods();
602
82a316e7
CP
603 m = getconfigitem("trusts", "master");
604 if(!m || (atoi(m->content) != 1)) {
605 Error("trusts_management", ERR_ERROR, "Not a master server, not loaded.");
606 return;
607 }
608
609 loaded = 1;
610
35449aa5 611 registerhook(HOOK_TRUSTS_DB_LOADED, registercommands);
caf2d02a 612 registerhook(HOOK_TRUSTS_DB_LOADED, schedulecleanup);
35449aa5
CP
613 registerhook(HOOK_TRUSTS_DB_CLOSED, deregistercommands);
614
615 if(trustsdbloaded)
616 registercommands(0, NULL);
617}
618
619void _fini(void) {
1d9ccd69
CP
620 array_free(&trustmods_a);
621
82a316e7
CP
622 if(!loaded)
623 return;
624
35449aa5
CP
625 deregisterhook(HOOK_TRUSTS_DB_LOADED, registercommands);
626 deregisterhook(HOOK_TRUSTS_DB_CLOSED, deregistercommands);
627
628 deregistercommands(0, NULL);
caf2d02a
GB
629
630 deleteallschedules(cleanuptrusts);
35449aa5 631}