1 /*
2  * ProFTPD: mod_dso -- support for loading/unloading modules at run-time
3  * Copyright (c) 2004-2020 TJ Saunders <tj@castaglia.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  *
24  * This is mod_dso, contrib software for proftpd 1.3.x.
25  * For more information contact TJ Saunders <tj@castaglia.org>.
26  */
27 
28 #include "conf.h"
29 #include "mod_ctrls.h"
30 #include "ltdl.h"
31 
32 #define MOD_DSO_VERSION		"mod_dso/0.5"
33 
34 /* From modules/module_glue.c */
35 extern module *static_modules[];
36 extern module *loaded_modules;
37 
38 module dso_module;
39 static const char *dso_module_path = PR_LIBEXEC_DIR;
40 static pool *dso_pool = NULL;
41 
42 static const char *trace_channel = "dso";
43 
44 #ifdef PR_USE_CTRLS
45 static ctrls_acttab_t dso_acttab[];
46 #endif
47 
dso_load_file(char * path)48 static int dso_load_file(char *path) {
49   if (path == NULL) {
50     errno = EINVAL;
51     return -1;
52   }
53 
54   pr_trace_msg(trace_channel, 5, "loading file '%s'", path);
55 
56   /* XXX Is this sufficient for loading an external library? */
57   if (lt_dlopenext(path) == NULL) {
58     pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION ": unable to open '%s': %s",
59       path, lt_dlerror());
60     errno = EPERM;
61     return -1;
62   }
63 
64   pr_trace_msg(trace_channel, 8, "file '%s' successfully loaded", path);
65   return 0;
66 }
67 
dso_load_module(pool * p,char * name)68 static int dso_load_module(pool *p, char *name) {
69   int module_load_errno = 0, res;
70   char *symbol_name, *path, *ptr;
71   size_t namelen;
72   module *m;
73   lt_ptr mh = NULL;
74   lt_dladvise advise;
75   const lt_dlinfo *info = NULL;
76 
77   if (name == NULL) {
78     errno = EINVAL;
79     return -1;
80   }
81 
82   namelen = strlen(name);
83 
84   if (namelen < 5 ||
85       strncmp(name, "mod_", 4) != 0) {
86     errno = EINVAL;
87     return -1;
88   }
89 
90   /* Handle ".c" and ".cpp" extensions. */
91   if (pr_strnrstr(name, namelen, ".c", 2, 0) != TRUE &&
92       pr_strnrstr(name, namelen, ".cpp", 4, 0) != TRUE) {
93     errno = EINVAL;
94     return -1;
95   }
96 
97   pr_log_debug(DEBUG7, MOD_DSO_VERSION ": loading '%s'", name);
98 
99   ptr = strrchr(name, '.');
100   if (ptr == NULL) {
101     errno = EINVAL;
102     return -1;
103   }
104 
105   if (lt_dladvise_init(&advise) < 0) {
106     pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
107       ": unable to initialise advise: %s", lt_dlerror());
108     errno = EPERM;
109     return -1;
110   }
111 
112   if (lt_dladvise_ext(&advise) < 0) {
113     pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
114       ": unable to setting 'ext' advise hint: %s", lt_dlerror());
115     lt_dladvise_destroy(&advise);
116     errno = EPERM;
117     return -1;
118   }
119 
120   if (lt_dladvise_global(&advise) < 0) {
121     pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
122       ": unable to setting 'global' advise hint: %s", lt_dlerror());
123     lt_dladvise_destroy(&advise);
124     errno = EPERM;
125     return -1;
126   }
127 
128   *ptr = '\0';
129 
130   /* Load file: $prefix/libexec/<module> */
131   path = pdircat(dso_pool, dso_module_path, name, NULL);
132 
133   pr_trace_msg(trace_channel, 5, "loading module '%s'", path);
134 
135   mh = lt_dlopenadvise(path, advise);
136   if (mh == NULL) {
137     int xerrno = errno;
138 
139     *ptr = '.';
140 
141     /* Remember this errno value, for reporting later if we cannot resolve
142      * the symbol from the main executable.
143      */
144     module_load_errno = errno;
145 
146     pr_log_debug(DEBUG3, MOD_DSO_VERSION ": unable to dlopen '%s': %s (%s)",
147       name, lt_dlerror(), strerror(xerrno));
148 
149     if (xerrno == ENOENT) {
150       pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
151         ": unable to load '%s'; check to see if '%s.la' exists", name, path);
152     }
153 
154     pr_log_debug(DEBUG3, MOD_DSO_VERSION
155       ": defaulting to 'self' for symbol resolution");
156 
157     lt_dladvise_destroy(&advise);
158 
159     mh = lt_dlopen(NULL);
160     if (mh == NULL) {
161       pr_log_debug(DEBUG0, MOD_DSO_VERSION ": error loading 'self': %s",
162         lt_dlerror());
163 
164       errno = xerrno;
165       return -1;
166     }
167   }
168 
169   lt_dladvise_destroy(&advise);
170 
171   info = lt_dlgetinfo(mh);
172   if (info != NULL) {
173     struct stat st;
174 
175     res = stat(info->filename, &st);
176     if (res == 0) {
177       pr_log_debug(DEBUG7, MOD_DSO_VERSION
178         ": loaded module '%s' (from '%s', last modified on %s)", info->name,
179         info->filename, pr_strtime3(p, st.st_mtime, FALSE));
180     }
181   }
182 
183   /* Tease name of the module structure out of the given name:
184    *  <module>.<ext> --> <module>_module
185    */
186 
187   *ptr = '\0';
188   symbol_name = pstrcat(dso_pool, name+4, "_module", NULL);
189 
190   /* Lookup module structure symbol by name. */
191 
192   pr_trace_msg(trace_channel, 7, "looking for symbol '%s' in loaded module",
193     symbol_name);
194 
195   m = (module *) lt_dlsym(mh, symbol_name);
196   if (m == NULL) {
197     int xerrno = errno;
198 
199     *ptr = '.';
200     pr_log_debug(DEBUG1, MOD_DSO_VERSION
201       ": unable to find module symbol '%s' in 'self'", symbol_name);
202     pr_trace_msg(trace_channel, 1,
203       "unable to find module symbol '%s' in 'self'", symbol_name);
204 
205     lt_dlclose(mh);
206     mh = NULL;
207 
208     if (xerrno == ENOENT) {
209       pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
210         ": check to see if '%s.la' exists", path);
211     }
212 
213     if (module_load_errno != 0) {
214       /* If we had an error loading the original module, AND we had an error
215        * resolving the symbol in the main executable, then return the original
216        * errno from loading the module, rather than the symbol resolution
217        * error.
218        */
219       errno = module_load_errno;
220 
221     } else {
222       errno = xerrno;
223     }
224 
225     return -1;
226   }
227   *ptr = '.';
228 
229   m->handle = mh;
230 
231   /* Add the module to the core structures */
232   res = pr_module_load(m);
233   if (res < 0) {
234     int xerrno = errno;
235 
236     if (xerrno == EEXIST) {
237       pr_log_pri(PR_LOG_INFO, MOD_DSO_VERSION
238         ": module 'mod_%s.c' already loaded", m->name);
239       pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' already loaded",
240         m->name);
241 
242     } else if (xerrno == EACCES) {
243       pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION
244         ": module 'mod_%s.c' has wrong API version (0x%x), must be 0x%x",
245         m->name, m->api_version, PR_MODULE_API_VERSION);
246       pr_trace_msg(trace_channel, 1,
247         "module 'mod_%s.c' has wrong API version (0x%x), must be 0x%x",
248         m->name, m->api_version, PR_MODULE_API_VERSION);
249 
250     } else if (xerrno == EPERM) {
251       pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION
252         ": module 'mod_%s.c' failed to initialize", m->name);
253       pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' failed to initialize",
254         m->name);
255     }
256 
257     lt_dlclose(mh);
258     mh = NULL;
259 
260     errno = xerrno;
261     return -1;
262   }
263 
264   pr_trace_msg(trace_channel, 8, "module '%s' successfully loaded", path);
265   return 0;
266 }
267 
dso_unload_module(module * m)268 static int dso_unload_module(module *m) {
269   int res;
270   char *name;
271 
272   /* Some modules cannot be unloaded. */
273   if (strncmp(m->name, "dso", 4) == 0) {
274     errno = EPERM;
275     return -1;
276   }
277 
278   name = pstrdup(dso_pool, m->name);
279 
280   pr_trace_msg(trace_channel, 5, "unloading module 'mod_%s.c'", name);
281 
282   res = pr_module_unload(m);
283   if (res < 0) {
284     int xerrno = errno;
285 
286     pr_log_debug(DEBUG1, MOD_DSO_VERSION
287       ": error unloading module 'mod_%s.c': %s", m->name, strerror(xerrno));
288     pr_trace_msg(trace_channel, 1,
289       "error unloading module 'mod_%s.c': %s", m->name, strerror(xerrno));
290   }
291 
292   if (lt_dlclose(m->handle) < 0) {
293     int xerrno = errno;
294 
295     pr_log_debug(DEBUG1, MOD_DSO_VERSION ": error closing '%s': %s",
296       name, lt_dlerror());
297 
298     errno = xerrno;
299     return -1;
300   }
301 
302   pr_trace_msg(trace_channel, 8, "module 'mod_%s.c' successfully unloaded",
303     name);
304   return 0;
305 }
306 
307 #ifdef PR_USE_CTRLS
dso_unload_module_by_name(const char * name)308 static int dso_unload_module_by_name(const char *name) {
309   module *m;
310 
311   if (strncmp(name, "mod_", 4) != 0 ||
312       name[strlen(name)-2] != '.' ||
313       name[strlen(name)-1] != 'c') {
314     errno = EINVAL;
315     return -1;
316   }
317 
318   /* Lookup the module pointer for the given module name. */
319   m = pr_module_get(name);
320   if (m == NULL) {
321     errno = ENOENT;
322     return -1;
323   }
324 
325   return dso_unload_module(m);
326 }
327 #endif /* PR_USE_CTRLS */
328 
329 #ifdef PR_USE_CTRLS
330 /* Controls handlers
331  */
332 
dso_handle_insmod(pr_ctrls_t * ctrl,int reqargc,char ** reqargv)333 static int dso_handle_insmod(pr_ctrls_t *ctrl, int reqargc,
334     char **reqargv) {
335   register int i;
336 
337   /* Check the ACL. */
338   if (!pr_ctrls_check_acl(ctrl, dso_acttab, "insmod")) {
339 
340     /* Access denied. */
341     pr_ctrls_add_response(ctrl, "access denied");
342     return -1;
343   }
344 
345   /* Sanity check */
346   if (reqargc == 0 ||
347       reqargv == NULL) {
348     pr_ctrls_add_response(ctrl, "missing required parameters");
349     return -1;
350   }
351 
352   for (i = 0; i < reqargc; i++) {
353     if (dso_load_module(ctrl->ctrls_tmp_pool, reqargv[i]) < 0) {
354       int xerrno = errno;
355 
356       /* Make the error messages a little more relevant. */
357       switch (xerrno) {
358         case EINVAL:
359           pr_ctrls_add_response(ctrl, "error loading '%s': Bad module name",
360             reqargv[i]);
361           break;
362 
363         case EEXIST:
364           pr_ctrls_add_response(ctrl, "error loading '%s': Already loaded",
365             reqargv[i]);
366           break;
367 
368         default:
369           pr_ctrls_add_response(ctrl, "error loading '%s': %s", reqargv[i],
370             strerror(xerrno));
371           break;
372       }
373 
374     } else
375       pr_ctrls_add_response(ctrl, "'%s' loaded", reqargv[i]);
376   }
377 
378   return 0;
379 }
380 
dso_handle_lsmod(pr_ctrls_t * ctrl,int reqargc,char ** reqargv)381 static int dso_handle_lsmod(pr_ctrls_t *ctrl, int reqargc,
382     char **reqargv) {
383   module *m;
384 
385   /* Check the ACL. */
386   if (!pr_ctrls_check_acl(ctrl, dso_acttab, "lsmod")) {
387 
388     /* Access denied. */
389     pr_ctrls_add_response(ctrl, "access denied");
390     return -1;
391   }
392 
393   if (reqargc != 0) {
394     pr_ctrls_add_response(ctrl, "wrong number of parameters");
395     return -1;
396   }
397 
398   /* We want to show the modules as `proftpd -l` shows them, in module
399    * load order.  So first we find the end of the loaded_modules list,
400    * then walk it backwards.
401    */
402   for (m = loaded_modules; m && m->next; m = m->next);
403 
404   pr_ctrls_add_response(ctrl, "Loaded Modules:");
405   for (; m; m = m->prev)
406     pr_ctrls_add_response(ctrl, "  mod_%s.c", m->name);
407 
408   return 0;
409 }
410 
dso_handle_rmmod(pr_ctrls_t * ctrl,int reqargc,char ** reqargv)411 static int dso_handle_rmmod(pr_ctrls_t *ctrl, int reqargc,
412     char **reqargv) {
413   register int i;
414 
415   /* Check the ACL. */
416   if (!pr_ctrls_check_acl(ctrl, dso_acttab, "rmmod")) {
417 
418     /* Access denied. */
419     pr_ctrls_add_response(ctrl, "access denied");
420     return -1;
421   }
422 
423   /* Sanity check */
424   if (reqargc == 0 || reqargv == NULL) {
425     pr_ctrls_add_response(ctrl, "missing required parameters");
426     return -1;
427   }
428 
429   for (i = 0; i < reqargc; i++) {
430     if (dso_unload_module_by_name(reqargv[i]) < 0) {
431       int xerrno = errno;
432 
433       switch (xerrno) {
434         case EINVAL:
435           pr_ctrls_add_response(ctrl, "error unloading '%s': Bad module name",
436             reqargv[i]);
437           break;
438 
439         case ENOENT:
440           pr_ctrls_add_response(ctrl, "error unloading '%s': Module not loaded",
441             reqargv[i]);
442           break;
443 
444         default:
445           pr_ctrls_add_response(ctrl, "error unloading '%s': %s",
446             reqargv[i], strerror(errno));
447           break;
448       }
449 
450     } else {
451       pr_ctrls_add_response(ctrl, "'%s' unloaded", reqargv[i]);
452     }
453   }
454 
455   return 0;
456 }
457 #endif /* PR_USE_CTRLS */
458 
459 /* Configuration handlers
460  */
461 
462 /* usage: LoadFile path */
set_loadfile(cmd_rec * cmd)463 MODRET set_loadfile(cmd_rec *cmd) {
464   CHECK_ARGS(cmd, 1);
465   CHECK_CONF(cmd, CONF_ROOT);
466 
467   if (pr_fs_valid_path(cmd->argv[1]) < 0) {
468     CONF_ERROR(cmd, "must be an absolute path");
469   }
470 
471   if (dso_load_file(cmd->argv[1]) < 0) {
472     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error loading '", cmd->argv[1],
473       "': ", strerror(errno), NULL));
474   }
475 
476   return PR_HANDLED(cmd);
477 }
478 
479 /* usage: LoadModule module */
set_loadmodule(cmd_rec * cmd)480 MODRET set_loadmodule(cmd_rec *cmd) {
481   CHECK_ARGS(cmd, 1);
482   CHECK_CONF(cmd, CONF_ROOT);
483 
484   if (dso_load_module(cmd->tmp_pool, cmd->argv[1]) < 0) {
485     int xerrno = errno;
486 
487     if (xerrno != EEXIST) {
488       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error loading module '",
489         cmd->argv[1], "': ", strerror(xerrno), NULL));
490     }
491   }
492 
493   return PR_HANDLED(cmd);
494 }
495 
496 /* usage: ModuleControlsACLs actions|all allow|deny user|group list */
set_modulectrlsacls(cmd_rec * cmd)497 MODRET set_modulectrlsacls(cmd_rec *cmd) {
498 #ifdef PR_USE_CTRLS
499   char *bad_action = NULL, **actions = NULL;
500 
501   CHECK_ARGS(cmd, 4);
502   CHECK_CONF(cmd, CONF_ROOT);
503 
504   actions = ctrls_parse_acl(cmd->tmp_pool, cmd->argv[1]);
505 
506   if (strcmp(cmd->argv[2], "allow") != 0 &&
507       strcmp(cmd->argv[2], "deny") != 0)
508     CONF_ERROR(cmd, "second parameter must be 'allow' or 'deny'");
509 
510   if (strcmp(cmd->argv[3], "user") != 0 &&
511       strcmp(cmd->argv[3], "group") != 0)
512     CONF_ERROR(cmd, "third parameter must be 'user' or 'group'");
513 
514   bad_action = pr_ctrls_set_module_acls(dso_acttab, dso_pool, actions,
515     cmd->argv[2], cmd->argv[3], cmd->argv[4]);
516   if (bad_action != NULL)
517     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown action: '",
518       bad_action, "'", NULL));
519 
520   return PR_HANDLED(cmd);
521 #else
522   CONF_ERROR(cmd, "requires Controls support (--enable-ctrls)");
523 #endif
524 }
525 
526 /* usage: ModuleOrder mod1 mod2 ... modN */
set_moduleorder(cmd_rec * cmd)527 MODRET set_moduleorder(cmd_rec *cmd) {
528   register unsigned int i;
529   module *m, *mn, *module_list = NULL;
530 
531   if (cmd->argc-1 < 1)
532     CONF_ERROR(cmd, "wrong number of parameters");
533 
534   CHECK_CONF(cmd, CONF_ROOT);
535 
536   /* What about duplicate names in the list?
537    *
538    * What if the given list is longer than the one already in loaded_modules?
539    * This will be caught by the existence check.  Otherwise, the only way for
540    * the list to be longer is if there are duplicates, which will be caught
541    * by the duplicate check.
542    */
543 
544   /* Make sure the given module names exist. */
545   for (i = 1; i < cmd->argc; i++) {
546     if (pr_module_get(cmd->argv[i]) == NULL) {
547       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "no such module '", cmd->argv[i],
548         "' loaded", NULL));
549     }
550   }
551 
552   /* Make sure there are no duplicate module names in the list. */
553   for (i = 1; i < cmd->argc; i++) {
554     register unsigned int j;
555 
556     for (j = i + 1; j < cmd->argc; j++) {
557       if (strcmp(cmd->argv[i], cmd->argv[j]) == 0) {
558         char ibuf[4], jbuf[4];
559 
560         pr_snprintf(ibuf, sizeof(ibuf), "%u", i);
561         ibuf[sizeof(ibuf)-1] = '\0';
562 
563         pr_snprintf(jbuf, sizeof(jbuf), "%u", j);
564         jbuf[sizeof(jbuf)-1] = '\0';
565 
566         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
567           "duplicate module name '", cmd->argv[i], "' as parameters ",
568           ibuf, " and ", jbuf, NULL));
569       }
570     }
571   }
572 
573   pr_log_debug(DEBUG4, "%s: reordering modules", (char *) cmd->argv[0]);
574   for (i = 1; i < cmd->argc; i++) {
575     m = pr_module_get(cmd->argv[i]);
576 
577     if (module_list) {
578       m->next = module_list;
579       module_list->prev = m;
580       module_list = m;
581 
582     } else {
583       module_list = m;
584     }
585   }
586 
587   /* Now, unload all the modules in the loaded_modules list, then load
588    * the modules in our module_list.
589    */
590   for (m = loaded_modules; m;) {
591     mn = m->next;
592 
593     if (pr_module_unload(m) < 0) {
594       pr_log_debug(DEBUG0, "%s: error unloading module 'mod_%s.c': %s",
595         (char *) cmd->argv[0], m->name, strerror(errno));
596     }
597 
598     m = mn;
599   }
600 
601   for (m = module_list; m; m = m->next) {
602     if (pr_module_load(m) < 0) {
603       pr_log_debug(DEBUG0, "%s: error loading module 'mod_%s.c': %s",
604         (char *) cmd->argv[0], m->name, strerror(errno));
605       exit(1);
606     }
607   }
608 
609   pr_log_pri(PR_LOG_NOTICE, "%s: module order is now:", (char *) cmd->argv[0]);
610   for (m = loaded_modules; m; m = m->next) {
611     pr_log_pri(PR_LOG_NOTICE, " mod_%s.c", m->name);
612   }
613 
614   return PR_HANDLED(cmd);
615 }
616 
617 /* usage: ModulePath path */
set_modulepath(cmd_rec * cmd)618 MODRET set_modulepath(cmd_rec *cmd) {
619   int res;
620   struct stat st;
621 
622   CHECK_ARGS(cmd, 1);
623   CHECK_CONF(cmd, CONF_ROOT);
624 
625   if (pr_fs_valid_path(cmd->argv[1]) < 0) {
626     CONF_ERROR(cmd, "must be an absolute path");
627   }
628 
629   /* Make sure that the configured path is not world-writable. */
630   res = pr_fsio_stat(cmd->argv[1], &st);
631   if (res < 0) {
632     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error checking '",
633       cmd->argv[1], "': ", strerror(errno), NULL));
634   }
635 
636   if (!S_ISDIR(st.st_mode)) {
637     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, cmd->argv[1], " is not a directory",
638       NULL));
639   }
640 
641   if (st.st_mode & S_IWOTH) {
642     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, cmd->argv[1], " is world-writable",
643       NULL));
644   }
645 
646   if (lt_dlsetsearchpath(cmd->argv[1]) < 0) {
647     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error setting module path: ",
648       lt_dlerror(), NULL));
649   }
650 
651   dso_module_path = pstrdup(dso_pool, cmd->argv[1]);
652   return PR_HANDLED(cmd);
653 }
654 
655 /* Event handlers
656  */
657 
dso_restart_ev(const void * event_data,void * user_data)658 static void dso_restart_ev(const void *event_data, void *user_data) {
659   module *m, *mi;
660 #ifdef PR_USE_CTRLS
661   register unsigned int i = 0;
662 #endif /* PR_USE_CTRLS */
663 
664   if (dso_pool)
665     destroy_pool(dso_pool);
666 
667   dso_pool = make_sub_pool(permanent_pool);
668   pr_pool_tag(dso_pool, MOD_DSO_VERSION);
669 
670 #ifdef PR_USE_CTRLS
671   /* Re-register the control handlers */
672   for (i = 0; dso_acttab[i].act_action; i++) {
673     pool *sub_pool = make_sub_pool(dso_pool);
674     pr_pool_tag(sub_pool, "DSO control action pool");
675 
676     /* Allocate and initialize the ACL for this control. */
677     dso_acttab[i].act_acl = pcalloc(sub_pool, sizeof(ctrls_acl_t));
678     dso_acttab[i].act_acl->acl_pool = sub_pool;
679     pr_ctrls_init_acl(dso_acttab[i].act_acl);
680   }
681 #endif /* PR_USE_CTRLS */
682 
683   /* Unload all shared modules. */
684   for (mi = loaded_modules; mi; mi = m) {
685 #ifndef PR_USE_CTRLS
686     register unsigned int i;
687 #endif /* PR_USE_CTRLS */
688     int is_static = FALSE;
689 
690     m = mi->next;
691 
692     for (i = 0; static_modules[i]; i++) {
693       if (strcmp(mi->name, static_modules[i]->name) == 0) {
694         is_static = TRUE;
695         break;
696       }
697     }
698 
699     if (!is_static) {
700       pr_log_debug(DEBUG7, MOD_DSO_VERSION ": unloading 'mod_%s.c'", mi->name);
701       if (dso_unload_module(mi) < 0) {
702         pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
703           ": error unloading 'mod_%s.c': %s", mi->name, strerror(errno));
704       }
705     }
706   }
707 
708   return;
709 }
710 
711 /* Initialization routines
712  */
713 
714 /* We should be using the LTDL_SET_PRELOADED_SYMBOLS macro provided by
715  * the ltdl.h header.  However, doing so resulted in compiler warnings
716  * about "nested extern declaraction of lt_preloaded_symbols".  To
717  * work around these warnings, we will use the expanded version of the
718  * macro directly.
719  *
720  * By the way, it appears that this lt_preloaded_symbols list is defined
721  * at link-time by the libtool script.
722  */
723 extern const lt_dlsymlist lt_preloaded_symbols[];
724 
dso_init(void)725 static int dso_init(void) {
726 #ifdef PR_USE_CTRLS
727   register unsigned int i = 0;
728 #endif /* PR_USE_CTRLS */
729 
730   /* Allocate the pool for this module's use. */
731   dso_pool = make_sub_pool(permanent_pool);
732   pr_pool_tag(dso_pool, MOD_DSO_VERSION);
733 
734   lt_dlpreload_default(lt_preloaded_symbols);
735 
736   /* Initialize libltdl. */
737   if (lt_dlinit() < 0) {
738     pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION ": error initializing libltdl: %s",
739       lt_dlerror());
740     return -1;
741   }
742 
743   /* Explicitly set the search path used for opening modules. */
744   if (lt_dlsetsearchpath(dso_module_path) < 0) {
745     pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION ": error setting module path: %s",
746       lt_dlerror());
747     return -1;
748   }
749 
750 #ifdef PR_USE_CTRLS
751   /* Register ctrls handlers. */
752   for (i = 0; dso_acttab[i].act_action; i++) {
753     pool *sub_pool = make_sub_pool(dso_pool);
754     pr_pool_tag(sub_pool, "DSO control action pool");
755 
756     /* Allocate and initialize the ACL for this control. */
757     dso_acttab[i].act_acl = pcalloc(sub_pool, sizeof(ctrls_acl_t));
758     dso_acttab[i].act_acl->acl_pool = sub_pool;
759     pr_ctrls_init_acl(dso_acttab[i].act_acl);
760 
761     if (pr_ctrls_register(&dso_module, dso_acttab[i].act_action,
762         dso_acttab[i].act_desc, dso_acttab[i].act_cb) < 0) {
763       pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
764         ": error registering '%s' control: %s", dso_acttab[i].act_action,
765         strerror(errno));
766     }
767   }
768 #endif /* PR_USE_CTRLS */
769 
770   /* Ideally, we'd call register a listener for the 'core.exit' event
771    * and call lt_dlexit() there, politely freeing up any resources allocated
772    * by the ltdl library.  However, it's possible that other modules, later in
773    * the dispatch cycles, may need to use pointers to memory in shared modules
774    * that would become invalid by such finalization.  So we skip it, for now.
775    *
776    * If there was a way to schedule this handler, to happen after all other
777    * exit handlers, that'd be best.
778    */
779   pr_event_register(&dso_module, "core.restart", dso_restart_ev, NULL);
780 
781   return 0;
782 }
783 
dso_sess_init(void)784 static int dso_sess_init(void) {
785   pr_event_unregister(&dso_module, "core.restart", dso_restart_ev);
786   return 0;
787 }
788 
789 #ifdef PR_USE_CTRLS
790 static ctrls_acttab_t dso_acttab[] = {
791   { "insmod",	"load modules",		NULL,	dso_handle_insmod },
792   { "lsmod",	"list modules",		NULL, 	dso_handle_lsmod },
793   { "rmmod",	"unload modules",	NULL,	dso_handle_rmmod },
794   { NULL, NULL, NULL, NULL }
795 };
796 #endif /* PR_USE_CTRLS */
797 
798 /* Module API tables
799  */
800 
801 static conftable dso_conftab[] = {
802   { "LoadFile",			set_loadfile,		NULL },
803   { "LoadModule",		set_loadmodule,		NULL },
804   { "ModuleControlsACLs",	set_modulectrlsacls,	NULL },
805   { "ModuleOrder",		set_moduleorder,	NULL },
806   { "ModulePath",		set_modulepath,		NULL },
807   { NULL }
808 };
809 
810 module dso_module = {
811   /* Always NULL */
812   NULL, NULL,
813 
814   /* Module API version 2.0 */
815   0x20,
816 
817   /* Module name */
818   "dso",
819 
820   /* Module configuration handler table */
821   dso_conftab,
822 
823   /* Module command handler table */
824   NULL,
825 
826   /* Module authentication handler table */
827   NULL,
828 
829   /* Module initialization function */
830   dso_init,
831 
832   /* Session initialization function */
833   dso_sess_init,
834 
835   /* Module version */
836   MOD_DSO_VERSION
837 };
838 
839