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