jabberd2  2.2.17
mm.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 #include "sm.h"
22 
23 #ifdef _WIN32
24 # define LIBRARY_DIR "."
25 # include <windows.h>
26 #else
27 # include <dlfcn.h>
28 #endif /* _WIN32 */
29 
37 /* these functions implement a multiplexor to get calls to the correct module
38  * for the given type */
39 
40 /* Notes on dynamic modules (cedricv@) :
41  Modules are searched by name mod_[modulename].so or mod_[modulename].dll
42  depending platform.
43  You have to set <path>[full_path]</path> within <modules> in sm.xml config,
44  else it will only search in LD_LIBRARY_PATH or c:\windows\system32
45  */
46 
48  mm_t mm;
49  int celem, melem, attr, *nlist = NULL;
50  char id[13], name[32], mod_fullpath[PATH_MAX], arg[1024], *modules_path;
51  mod_chain_t chain = (mod_chain_t) NULL;
52  mod_instance_t **list = NULL, mi;
53  module_t mod;
54 
55  mm = (mm_t) calloc(1, sizeof(struct mm_st));
56 
57  mm->sm = sm;
58  mm->modules = xhash_new(101);
59 
60  if((celem = nad_find_elem(sm->config->nad, 0, -1, "modules", 1)) < 0)
61  return mm;
62 
63  modules_path = config_get_one(sm->config, "modules.path", 0);
64  if (modules_path != NULL)
65  log_write(sm->log, LOG_NOTICE, "modules search path: %s", modules_path);
66  else
67  log_write(sm->log, LOG_NOTICE, "modules search path undefined, using deafult: "LIBRARY_DIR);
68 
69  celem = nad_find_elem(sm->config->nad, celem, -1, "chain", 1);
70  while(celem >= 0) {
71  if((attr = nad_find_attr(sm->config->nad, celem, -1, "id", NULL)) < 0) {
72  celem = nad_find_elem(sm->config->nad, celem, -1, "chain", 0);
73  continue;
74  }
75 
76  snprintf(id, 13, "%.*s", NAD_AVAL_L(sm->config->nad, attr), NAD_AVAL(sm->config->nad, attr));
77  id[12] = '\0';
78 
79  log_debug(ZONE, "processing config for chain '%s'", id);
80 
81  list = NULL;
82  if(strcmp(id, "sess-start") == 0) {
83  chain = chain_SESS_START;
84  list = &mm->sess_start;
85  nlist = &mm->nsess_start;
86  }
87  else if(strcmp(id, "sess-end") == 0) {
88  chain = chain_SESS_END;
89  list = &mm->sess_end;
90  nlist = &mm->nsess_end;
91  }
92  else if(strcmp(id, "in-sess") == 0) {
93  chain = chain_IN_SESS;
94  list = &mm->in_sess;
95  nlist = &mm->nin_sess;
96  }
97  else if(strcmp(id, "in-router") == 0) {
98  chain = chain_IN_ROUTER;
99  list = &mm->in_router;
100  nlist = &mm->nin_router;
101  }
102  else if(strcmp(id, "out-sess") == 0) {
103  chain = chain_OUT_SESS;
104  list = &mm->out_sess;
105  nlist = &mm->nout_sess;
106  }
107  else if(strcmp(id, "out-router") == 0) {
108  chain = chain_OUT_ROUTER;
109  list = &mm->out_router;
110  nlist = &mm->nout_router;
111  }
112  else if(strcmp(id, "pkt-sm") == 0) {
113  chain = chain_PKT_SM;
114  list = &mm->pkt_sm;
115  nlist = &mm->npkt_sm;
116  }
117  else if(strcmp(id, "pkt-user") == 0) {
118  chain = chain_PKT_USER;
119  list = &mm->pkt_user;
120  nlist = &mm->npkt_user;
121  }
122  else if(strcmp(id, "pkt-router") == 0) {
123  chain = chain_PKT_ROUTER;
124  list = &mm->pkt_router;
125  nlist = &mm->npkt_router;
126  }
127  else if(strcmp(id, "user-load") == 0) {
128  chain = chain_USER_LOAD;
129  list = &mm->user_load;
130  nlist = &mm->nuser_load;
131  }
132  else if(strcmp(id, "user-unload") == 0) {
133  chain = chain_USER_UNLOAD;
134  list = &mm->user_unload;
135  nlist = &mm->nuser_unload;
136  }
137  else if(strcmp(id, "user-create") == 0) {
138  chain = chain_USER_CREATE;
139  list = &mm->user_create;
140  nlist = &mm->nuser_create;
141  }
142  else if(strcmp(id, "user-delete") == 0) {
143  chain = chain_USER_DELETE;
144  list = &mm->user_delete;
145  nlist = &mm->nuser_delete;
146  }
147  else if(strcmp(id, "disco-extend") == 0) {
148  chain = chain_DISCO_EXTEND;
149  list = &mm->disco_extend;
150  nlist = &mm->ndisco_extend;
151  }
152 
153  if(list == NULL) {
154  log_write(sm->log, LOG_ERR, "unknown chain type '%s'", id);
155 
156  celem = nad_find_elem(sm->config->nad, celem, -1, "chain", 0);
157  continue;
158  }
159 
160  melem = nad_find_elem(sm->config->nad, celem, -1, "module", 1);
161  while(melem >= 0) {
162  if(NAD_CDATA_L(sm->config->nad, melem) <= 0) {
163  melem = nad_find_elem(sm->config->nad, melem, -1, "module", 0);
164  continue;
165  }
166 
167  arg[0] = '\0';
168  attr = nad_find_attr(sm->config->nad, melem, -1, "arg", NULL);
169  if(attr >= 0) {
170  snprintf(arg, 1024, "%.*s", NAD_AVAL_L(sm->config->nad, attr), NAD_AVAL(sm->config->nad, attr));
171  log_debug(ZONE, "module arg: %s", arg);
172  }
173 
174  snprintf(name, 32, "%.*s", NAD_CDATA_L(sm->config->nad, melem), NAD_CDATA(sm->config->nad, melem));
175 
176  mod = xhash_get(mm->modules, name);
177  if(mod == NULL) {
178  mod = (module_t) calloc(1, sizeof(struct module_st));
179 
180  mod->mm = mm;
181  mod->index = mm->nindex;
182  mod->name = strdup(name);
183  #ifndef _WIN32
184  if (modules_path != NULL)
185  snprintf(mod_fullpath, PATH_MAX, "%s/mod_%s.so", modules_path, name);
186  else
187  snprintf(mod_fullpath, PATH_MAX, "%s/mod_%s.so", LIBRARY_DIR, name);
188  mod->handle = dlopen(mod_fullpath, RTLD_LAZY);
189  if (mod->handle != NULL)
190  mod->module_init_fn = dlsym(mod->handle, "module_init");
191  #else
192  if (modules_path != NULL)
193  snprintf(mod_fullpath, PATH_MAX, "%s\\mod_%s.dll", modules_path, name);
194  else
195  snprintf(mod_fullpath, PATH_MAX, "mod_%s.dll", name);
196  mod->handle = (void*) LoadLibrary(mod_fullpath);
197  if (mod->handle != NULL)
198  mod->module_init_fn = (int (*)(mod_instance_t))GetProcAddress((HMODULE) mod->handle, "module_init");
199  #endif
200 
201  if (mod->handle != NULL && mod->module_init_fn != NULL) {
202  log_debug(ZONE, "preloaded module '%s' to chain '%s' (not added yet)", name, id);
203  xhash_put(mm->modules, mod->name, (void *) mod);
204  mm->nindex++;
205  } else {
206  #ifndef _WIN32
207  log_write(sm->log, LOG_ERR, "failed loading module '%s' to chain '%s' (%s)", name, id, dlerror());
208  if (mod->handle != NULL)
209  dlclose(mod->handle);
210  #else
211  log_write(sm->log, LOG_ERR, "failed loading module '%s' to chain '%s' (errcode: %x)", name, id, GetLastError());
212  if (mod->handle != NULL)
213  FreeLibrary((HMODULE) mod->handle);
214  #endif
215 
216  melem = nad_find_elem(sm->config->nad, melem, -1, "module", 0);
217  continue;
218  }
219  }
220 
221  mi = (mod_instance_t) calloc(1, sizeof(struct mod_instance_st));
222 
223  mi->sm = sm;
224  mi->mod = mod;
225  mi->chain = chain;
226  mi->arg = (arg[0] == '\0') ? NULL : strdup(arg);
227  mi->seq = mod->init;
228 
229  if(mod->module_init_fn(mi) != 0) {
230  log_write(sm->log, LOG_ERR, "init for module '%s' (seq %d) failed", name, mi->seq);
231  free(mi);
232 
233  if(mod->init == 0) {
234  xhash_zap(mm->modules, mod->name);
235 
236  #ifndef _WIN32
237  if (mod->handle != NULL)
238  dlclose(mod->handle);
239  #else
240  if (mod->handle != NULL)
241  FreeLibrary((HMODULE) mod->handle);
242  #endif
243 
244  free(mod->name);
245  free(mod);
246 
247  mm->nindex--;
248 
249  melem = nad_find_elem(sm->config->nad, melem, -1, "module", 0);
250  continue;
251  }
252  }
253 
254  mod->init++;
255 
256  *list = (mod_instance_t *) realloc(*list, sizeof(mod_instance_t) * (*nlist + 1));
257  (*list)[*nlist] = mi;
258 
259  log_write(sm->log, LOG_NOTICE, "module '%s' added to chain '%s' (order %d index %d seq %d)", mod->name, id, *nlist, mod->index, mi->seq);
260 
261  (*nlist)++;
262 
263  melem = nad_find_elem(sm->config->nad, melem, -1, "module", 0);
264  }
265 
266  celem = nad_find_elem(sm->config->nad, celem, -1, "chain", 0);
267  }
268 
269  return mm;
270 }
271 
272 static void _mm_reaper(const char *module, int modulelen, void *val, void *arg) {
273  module_t mod = (module_t) val;
274 
275  if(mod->free != NULL)
276  (mod->free)(mod);
277 
278  #ifndef _WIN32
279  if (mod->handle != NULL)
280  dlclose(mod->handle);
281  #else
282  if (mod->handle != NULL)
283  FreeLibrary((HMODULE) mod->handle);
284  #endif
285 
286  free(mod->name);
287  free(mod);
288 }
289 
290 void mm_free(mm_t mm) {
291  int i, j, *nlist = NULL;
292  mod_instance_t **list = NULL, mi;
293 
294  /* close down modules */
295  xhash_walk(mm->modules, _mm_reaper, NULL);
296 
297  /* free instances */
298  for(i = 0; i < 13; i++) {
299  switch(i) {
300  case 0:
301  list = &mm->sess_start;
302  nlist = &mm->nsess_start;
303  break;
304  case 1:
305  list = &mm->sess_end;
306  nlist = &mm->nsess_end;
307  break;
308  case 2:
309  list = &mm->in_sess;
310  nlist = &mm->nin_sess;
311  break;
312  case 3:
313  list = &mm->in_router;
314  nlist = &mm->nin_router;
315  break;
316  case 4:
317  list = &mm->out_sess;
318  nlist = &mm->nout_sess;
319  break;
320  case 5:
321  list = &mm->out_router;
322  nlist = &mm->nout_router;
323  break;
324  case 6:
325  list = &mm->pkt_sm;
326  nlist = &mm->npkt_sm;
327  break;
328  case 7:
329  list = &mm->pkt_user;
330  nlist = &mm->npkt_user;
331  break;
332  case 8:
333  list = &mm->pkt_router;
334  nlist = &mm->npkt_router;
335  break;
336  case 9:
337  list = &mm->user_load;
338  nlist = &mm->nuser_load;
339  break;
340  case 10:
341  list = &mm->user_create;
342  nlist = &mm->nuser_create;
343  break;
344  case 11:
345  list = &mm->user_delete;
346  nlist = &mm->nuser_delete;
347  break;
348  case 12:
349  list = &mm->disco_extend;
350  nlist = &mm->ndisco_extend;
351  break;
352  }
353 
354  for(j = 0; j < *nlist; j++) {
355  mi = (*list)[j];
356  if(mi->arg != NULL)
357  free(mi->arg);
358  free(mi);
359  }
360  }
361 
362  /* free lists */
363  free(mm->sess_start);
364  free(mm->sess_end);
365  free(mm->in_sess);
366  free(mm->in_router);
367  free(mm->out_sess);
368  free(mm->out_router);
369  free(mm->pkt_sm);
370  free(mm->pkt_user);
371  free(mm->pkt_router);
372  free(mm->user_load);
373  free(mm->user_create);
374  free(mm->user_delete);
375  free(mm->disco_extend);
376 
377  xhash_free(mm->modules);
378 
379  free(mm);
380 }
381 
383 int mm_sess_start(mm_t mm, sess_t sess) {
384  int n, ret = 0;
385  mod_instance_t mi;
386 
387  log_debug(ZONE, "dispatching sess-start chain");
388 
389  ret = 0;
390  for(n = 0; n < mm->nsess_start; n++) {
391  mi = mm->sess_start[n];
392  if(mi == NULL) {
393  log_debug(ZONE, "module at index %d is not loaded yet", n);
394  continue;
395  }
396  if(mi->mod->sess_start == NULL) {
397  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
398  continue;
399  }
400 
401  log_debug(ZONE, "calling module %s", mi->mod->name);
402 
403  ret = (mi->mod->sess_start)(mi, sess);
404  if(ret != 0)
405  break;
406  }
407 
408  log_debug(ZONE, "sess-start chain returning %d", ret);
409 
410  return ret;
411 }
412 
414 void mm_sess_end(mm_t mm, sess_t sess) {
415  int n;
416  mod_instance_t mi;
417 
418  log_debug(ZONE, "dispatching sess-end chain");
419 
420  for(n = 0; n < mm->nsess_end; n++) {
421  mi = mm->sess_end[n];
422  if(mi == NULL) {
423  log_debug(ZONE, "module at index %d is not loaded yet", n);
424  continue;
425  }
426  if(mi->mod->sess_end == NULL) {
427  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
428  continue;
429  }
430 
431  log_debug(ZONE, "calling module %s", mi->mod->name);
432 
433  (mi->mod->sess_end)(mi, sess);
434  }
435 
436  log_debug(ZONE, "sess-end chain returning");
437 }
438 
441  int n;
442  mod_instance_t mi;
443  mod_ret_t ret = mod_PASS;
444 
445  log_debug(ZONE, "dispatching in-sess chain");
446 
447  ret = mod_PASS;
448  for(n = 0; n < mm->nin_sess; n++) {
449  mi = mm->in_sess[n];
450  if(mi == NULL) {
451  log_debug(ZONE, "module at index %d is not loaded yet", n);
452  continue;
453  }
454  if(mi->mod->in_sess == NULL) {
455  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
456  continue;
457  }
458 
459  log_debug(ZONE, "calling module %s", mi->mod->name);
460 
461  ret = (mi->mod->in_sess)(mi, sess, pkt);
462  if(ret != mod_PASS)
463  break;
464  }
465 
466  log_debug(ZONE, "in-sess chain returning %d", ret);
467 
468  return ret;
469 }
470 
473  int n;
474  mod_instance_t mi;
475  mod_ret_t ret = mod_PASS;
476 
477  log_debug(ZONE, "dispatching in-router chain");
478 
479  if (mm != NULL && pkt != NULL )
480  for(n = 0; n < mm->nin_router; n++) {
481  mi = mm->in_router[n];
482  if(mi == NULL) {
483  log_debug(ZONE, "module at index %d is not loaded yet", n);
484  continue;
485  }
486  if(mi->mod == NULL || mi->mod->in_router == NULL) {
487  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
488  continue;
489  }
490 
491  log_debug(ZONE, "calling module %s", mi->mod->name);
492 
493  ret = (mi->mod->in_router)(mi, pkt);
494  if(ret != mod_PASS)
495  break;
496  }
497 
498  log_debug(ZONE, "in-router chain returning %d", ret);
499 
500  return ret;
501 }
502 
505  int n;
506  mod_instance_t mi;
507  mod_ret_t ret = mod_PASS;
508 
509  log_debug(ZONE, "dispatching out-sess chain");
510 
511  for(n = 0; n < mm->nout_sess; n++) {
512  mi = mm->out_sess[n];
513  if(mi == NULL) {
514  log_debug(ZONE, "module at index %d is not loaded yet", n);
515  continue;
516  }
517  if(mi->mod->out_sess == NULL) {
518  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
519  continue;
520  }
521 
522  log_debug(ZONE, "calling module %s", mi->mod->name);
523 
524  ret = (mi->mod->out_sess)(mi, sess, pkt);
525  if(ret != mod_PASS)
526  break;
527  }
528 
529  log_debug(ZONE, "out-sess chain returning %d", ret);
530 
531  return ret;
532 }
533 
536  int n;
537  mod_instance_t mi;
538  mod_ret_t ret = mod_PASS;
539 
540  log_debug(ZONE, "dispatching out-router chain");
541 
542  for(n = 0; n < mm->nout_router; n++) {
543  mi = mm->out_router[n];
544  if(mi == NULL) {
545  log_debug(ZONE, "module at index %d is not loaded yet", n);
546  continue;
547  }
548  if(mi->mod->out_router == NULL) {
549  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
550  continue;
551  }
552 
553  log_debug(ZONE, "calling module %s", mi->mod->name);
554 
555  ret = (mi->mod->out_router)(mi, pkt);
556  if(ret != mod_PASS)
557  break;
558  }
559 
560  log_debug(ZONE, "out-router chain returning %d", ret);
561 
562  return ret;
563 }
564 
567  int n, ret = 0;
568  mod_instance_t mi;
569 
570  log_debug(ZONE, "dispatching pkt-sm chain");
571 
572  for(n = 0; n < mm->npkt_sm; n++) {
573  mi = mm->pkt_sm[n];
574  if(mi == NULL) {
575  log_debug(ZONE, "module at index %d is not loaded yet", n);
576  continue;
577  }
578  if(mi->mod->pkt_sm == NULL) {
579  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
580  continue;
581  }
582 
583  log_debug(ZONE, "calling module %s", mi->mod->name);
584 
585  ret = (mi->mod->pkt_sm)(mi, pkt);
586  if(ret != mod_PASS)
587  break;
588  }
589 
590  log_debug(ZONE, "pkt-sm chain returning %d", ret);
591 
592  return ret;
593 }
594 
597  int n;
598  mod_instance_t mi;
599  mod_ret_t ret = mod_PASS;
600 
601  log_debug(ZONE, "dispatching pkt-user chain");
602 
603  for(n = 0; n < mm->npkt_user; n++) {
604  mi = mm->pkt_user[n];
605  if(mi == NULL) {
606  log_debug(ZONE, "module at index %d is not loaded yet", n);
607  continue;
608  }
609  if(mi->mod->pkt_user == NULL) {
610  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
611  continue;
612  }
613 
614  log_debug(ZONE, "calling module %s", mi->mod->name);
615 
616  ret = (mi->mod->pkt_user)(mi, user, pkt);
617  if(ret != mod_PASS)
618  break;
619  }
620 
621  log_debug(ZONE, "pkt-user chain returning %d", ret);
622 
623  return ret;
624 }
625 
628  int n;
629  mod_instance_t mi;
630  mod_ret_t ret = mod_PASS;
631 
632  log_debug(ZONE, "dispatching pkt-router chain");
633 
634  for(n = 0; n < mm->npkt_router; n++) {
635  mi = mm->pkt_router[n];
636  if(mi == NULL) {
637  log_debug(ZONE, "module at index %d is not loaded yet", n);
638  continue;
639  }
640  if(mi->mod->pkt_router == NULL) {
641  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
642  continue;
643  }
644 
645  log_debug(ZONE, "calling module %s", mi->mod->name);
646 
647  ret = (mi->mod->pkt_router)(mi, pkt);
648  if(ret != mod_PASS)
649  break;
650  }
651 
652  log_debug(ZONE, "pkt-router chain returning %d", ret);
653 
654  return ret;
655 }
656 
658 int mm_user_load(mm_t mm, user_t user) {
659  int n;
660  mod_instance_t mi;
661  int ret = 0;
662 
663  log_debug(ZONE, "dispatching user-load chain");
664 
665  for(n = 0; n < mm->nuser_load; n++) {
666  mi = mm->user_load[n];
667  if(mi == NULL) {
668  log_debug(ZONE, "module at index %d is not loaded yet", n);
669  continue;
670  }
671  if(mi->mod->user_load == NULL) {
672  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
673  continue;
674  }
675 
676  log_debug(ZONE, "calling module %s", mi->mod->name);
677 
678  ret = (mi->mod->user_load)(mi, user);
679  if(ret != 0)
680  break;
681  }
682 
683  log_debug(ZONE, "user-load chain returning %d", ret);
684 
685  return ret;
686 }
687 
689 int mm_user_unload(mm_t mm, user_t user) {
690  int n;
691  mod_instance_t mi;
692  int ret = 0;
693 
694  log_debug(ZONE, "dispatching user-unload chain");
695 
696  for(n = 0; n < mm->nuser_unload; n++) {
697  mi = mm->user_unload[n];
698  if(mi == NULL) {
699  log_debug(ZONE, "module at index %d is not loaded yet", n);
700  continue;
701  }
702  if(mi->mod->user_unload == NULL) {
703  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
704  continue;
705  }
706 
707  log_debug(ZONE, "calling module %s", mi->mod->name);
708 
709  ret = (mi->mod->user_unload)(mi, user);
710  if(ret != 0)
711  break;
712  }
713 
714  log_debug(ZONE, "user-unload chain returning %d", ret);
715 
716  return ret;
717 }
718 
720 int mm_user_create(mm_t mm, jid_t jid) {
721  int n;
722  mod_instance_t mi;
723  int ret = 0;
724 
725  log_debug(ZONE, "dispatching user-create chain");
726 
727  for(n = 0; n < mm->nuser_create; n++) {
728  mi = mm->user_create[n];
729  if(mi == NULL) {
730  log_debug(ZONE, "module at index %d is not loaded yet", n);
731  continue;
732  }
733  if(mi->mod->user_create == NULL) {
734  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
735  continue;
736  }
737 
738  log_debug(ZONE, "calling module %s", mi->mod->name);
739 
740  ret = (mi->mod->user_create)(mi, jid);
741  if(ret != 0)
742  break;
743  }
744 
745  log_debug(ZONE, "user-create chain returning %d", ret);
746 
747  return ret;
748 }
749 
751 void mm_user_delete(mm_t mm, jid_t jid) {
752  int n;
753  mod_instance_t mi;
754 
755  log_debug(ZONE, "dispatching user-delete chain");
756 
757  for(n = 0; n < mm->nuser_delete; n++) {
758  mi = mm->user_delete[n];
759  if(mi == NULL) {
760  log_debug(ZONE, "module at index %d is not loaded yet", n);
761  continue;
762  }
763  if(mi->mod->user_delete == NULL) {
764  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
765  continue;
766  }
767 
768  log_debug(ZONE, "calling module %s", mi->mod->name);
769 
770  (mi->mod->user_delete)(mi, jid);
771  }
772 
773  log_debug(ZONE, "user-delete chain returning");
774 }
775 
777 void mm_disco_extend(mm_t mm, pkt_t pkt) {
778  int n;
779  mod_instance_t mi;
780 
781  log_debug(ZONE, "dispatching disco-extend chain");
782 
783  for(n = 0; n < mm->ndisco_extend; n++) {
784  mi = mm->disco_extend[n];
785  if(mi == NULL) {
786  log_debug(ZONE, "module at index %d is not loaded yet", n);
787  continue;
788  }
789  if(mi->mod->disco_extend == NULL) {
790  log_debug(ZONE, "module %s has no handler for this chain", mi->mod->name);
791  continue;
792  }
793 
794  log_debug(ZONE, "calling module %s", mi->mod->name);
795 
796  (mi->mod->disco_extend)(mi, pkt);
797  }
798 
799  log_debug(ZONE, "disco-extend chain returning");
800 }