/src/pdsh/mod.c

https://code.google.com/ · C · 1100 lines · 726 code · 221 blank · 153 comment · 174 complexity · 3a1282cef33a63ce5a75b60a915dfd24 MD5 · raw file

  1. /*****************************************************************************\
  2. * $Id$
  3. *****************************************************************************
  4. * Copyright (C) 2001-2006 The Regents of the University of California.
  5. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  6. * Written by Jim Garlick <garlick@llnl.gov>.
  7. * UCRL-CODE-2003-005.
  8. *
  9. * This file is part of Pdsh, a parallel remote shell program.
  10. * For details, see <http://www.llnl.gov/linux/pdsh/>.
  11. *
  12. * Pdsh is free software; you can redistribute it and/or modify it under
  13. * the terms of the GNU General Public License as published by the Free
  14. * Software Foundation; either version 2 of the License, or (at your option)
  15. * any later version.
  16. *
  17. * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
  18. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  19. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  20. * details.
  21. *
  22. * You should have received a copy of the GNU General Public License along
  23. * with Pdsh; if not, write to the Free Software Foundation, Inc.,
  24. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  25. \*****************************************************************************/
  26. #if HAVE_CONFIG_H
  27. # include <config.h>
  28. #endif
  29. #if HAVE_FEATURES_H
  30. # include <features.h>
  31. #endif
  32. #include <sys/param.h>
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #include <unistd.h>
  36. #include <dirent.h>
  37. #include <assert.h>
  38. #include <string.h>
  39. #if STATIC_MODULES
  40. #include "static_modules.h"
  41. #else
  42. #include "ltdl.h"
  43. #endif
  44. #include "src/common/err.h"
  45. #include "src/common/xmalloc.h"
  46. #include "src/common/xstring.h"
  47. #include "src/common/hostlist.h"
  48. #include "src/common/list.h"
  49. #include "src/common/split.h"
  50. #include "mod.h"
  51. /*
  52. * pdsh/322: Workaround apparent bug in glibc 2.2.4 which
  53. * occaisionally causes LinuxThreads manager thread to
  54. * segfault at exit. (Disable dlclose() and lt_dlexit()
  55. * in these versions of glibc)
  56. */
  57. #if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3)
  58. # define PREVENT_DLCLOSE_BUG 1
  59. #endif
  60. /*
  61. * Components of a module.
  62. */
  63. struct module_components {
  64. #ifndef NDEBUG
  65. #define MOD_MAGIC 0xc0c0b0b
  66. int magic;
  67. #endif
  68. #if !STATIC_MODULES
  69. lt_dlhandle handle;
  70. #endif
  71. char *filename;
  72. int priority;
  73. int initialized;
  74. struct pdsh_module *pmod;
  75. };
  76. typedef enum permission_error {
  77. DIR_OK,
  78. DIR_NOT_DIRECTORY,
  79. DIR_BAD_OWNER,
  80. DIR_WORLD_WRITABLE
  81. } perm_error_t;
  82. /*
  83. * Static function prototypes:
  84. */
  85. #if STATIC_MODULES
  86. static int _mod_load_static_modules(void);
  87. static int _mod_load_static(int);
  88. #else
  89. static int _mod_load_dynamic_modules(const char *, opt_t *);
  90. static int _mod_load_dynamic(const char *);
  91. static int _cmp_filenames(mod_t, char *);
  92. static int _is_loaded(char *filename);
  93. static bool _path_permissions_ok(const char *dir, uid_t pdsh_owner);
  94. static perm_error_t _dir_permission_error(struct stat *, uid_t alt_uid);
  95. #endif
  96. static int _mod_initialize(mod_t mod);
  97. static int _mod_init_list_safe(mod_t mod, void *arg);
  98. static void _mod_destroy(mod_t mod);
  99. static bool _mod_opts_ok(mod_t mod);
  100. static int _mod_print_info(mod_t mod);
  101. static void _print_option_help(struct pdsh_module_option *p, int col);
  102. static struct pdsh_module_option * _mod_find_opt(mod_t mod, int opt);
  103. /*
  104. * Static list of loaded modules
  105. */
  106. static List module_list;
  107. static bool initialized = false;
  108. int
  109. mod_init(void)
  110. {
  111. if (!initialized) {
  112. if (!(module_list = list_create((ListDelF) _mod_destroy))) {
  113. err("Unable to create module list\n");
  114. return -1;
  115. }
  116. initialized = true;
  117. #if STATIC_MODULES
  118. return 0;
  119. #else
  120. return lt_dlinit();
  121. #endif
  122. } else
  123. return 0;
  124. }
  125. int
  126. mod_exit(void)
  127. {
  128. if (!initialized)
  129. return 0;
  130. /*
  131. * list_destroy() will call module destructor on each
  132. * element in list
  133. */
  134. list_destroy(module_list);
  135. #if STATIC_MODULES || PREVENT_DLCLOSE_BUG
  136. return 0;
  137. #else
  138. return lt_dlexit();
  139. #endif
  140. }
  141. hostlist_t
  142. _mod_read_wcoll(mod_t mod, opt_t *pdsh_opts)
  143. {
  144. if (mod->pmod->mod_ops && mod->pmod->mod_ops->read_wcoll)
  145. return (*mod->pmod->mod_ops->read_wcoll) (pdsh_opts);
  146. return 0;
  147. }
  148. int
  149. _mod_postop(mod_t mod, opt_t *pdsh_opts)
  150. {
  151. if (mod->pmod->mod_ops && mod->pmod->mod_ops->postop)
  152. return (*mod->pmod->mod_ops->postop) (pdsh_opts);
  153. return 0;
  154. }
  155. /*
  156. * Like list_next (i), but skip over inactive modules
  157. */
  158. static mod_t _mod_next_active (ListIterator i)
  159. {
  160. mod_t mod;
  161. while ((mod = list_next (i))) {
  162. if (mod->initialized)
  163. return (mod);
  164. }
  165. return (NULL);
  166. }
  167. /*
  168. * Call any "read wcoll" functions exported by modules. The module
  169. * is responsible for deciding when to generate a new wcoll,
  170. * append to existing wcoll, etc. (presumably based on the contents
  171. * of opt)
  172. *
  173. * This function appends to wcoll any new hosts returned from the
  174. * module specific read_wcoll() functions.
  175. *
  176. * Returns -1 on error, 0 for success.
  177. */
  178. int
  179. mod_read_wcoll(opt_t *opt)
  180. {
  181. mod_t mod;
  182. ListIterator module_itr;
  183. if (!initialized)
  184. mod_init();
  185. if (!(module_itr = list_iterator_create(module_list))) {
  186. err("Unable to create module list iterator\n");
  187. return -1;
  188. }
  189. while ((mod = _mod_next_active (module_itr))) {
  190. hostlist_t hl = NULL;
  191. if (!(hl = _mod_read_wcoll(mod, opt)))
  192. continue;
  193. if (opt->wcoll != NULL) {
  194. hostlist_push_list(opt->wcoll, hl);
  195. hostlist_destroy(hl);
  196. } else
  197. opt->wcoll = hl;
  198. }
  199. list_iterator_destroy(module_itr);
  200. return 0;
  201. }
  202. int
  203. mod_postop(opt_t *pdsh_opts)
  204. {
  205. mod_t mod;
  206. int errors = 0;
  207. ListIterator module_itr;
  208. if (!initialized)
  209. mod_init();
  210. if (!(module_itr = list_iterator_create(module_list))) {
  211. err("Unable to create module list iterator\n");
  212. return 1;
  213. }
  214. while ((mod = _mod_next_active (module_itr)))
  215. errors += _mod_postop(mod, pdsh_opts);
  216. list_iterator_destroy(module_itr);
  217. return errors;
  218. }
  219. mod_t
  220. mod_create(void)
  221. {
  222. mod_t mod = Malloc(sizeof(*mod));
  223. #if !STATIC_MODULES
  224. mod->handle = NULL;
  225. #endif
  226. mod->filename = NULL;
  227. mod->priority = DEFAULT_MODULE_PRIORITY;
  228. mod->initialized = 0;
  229. assert(mod->magic = MOD_MAGIC);
  230. return mod;
  231. }
  232. static void
  233. _mod_destroy(mod_t mod)
  234. {
  235. assert(mod->magic == MOD_MAGIC);
  236. /* must assert mod->mod, loading of module may have failed */
  237. if (mod->pmod) {
  238. mod->pmod->type = NULL;
  239. mod->pmod->name = NULL;
  240. /*
  241. * Only run exit function if module was initialized
  242. */
  243. if (mod->initialized &&
  244. mod->pmod->mod_ops && mod->pmod->mod_ops->exit)
  245. (*mod->pmod->mod_ops->exit)();
  246. }
  247. if (mod->filename)
  248. Free((void **) &mod->filename);
  249. #if !STATIC_MODULES
  250. # if !PREVENT_DLCLOSE_BUG
  251. if (mod->handle)
  252. lt_dlclose(mod->handle);
  253. # endif
  254. #endif
  255. assert(mod->magic = ~MOD_MAGIC);
  256. Free((void **) &mod);
  257. return;
  258. }
  259. static bool
  260. _mod_opts_ok(mod_t mod)
  261. {
  262. if (!opt_register(mod->pmod->opt_table))
  263. return false;
  264. return true;
  265. }
  266. static int
  267. _cmp_f (mod_t x, mod_t y)
  268. {
  269. if (x->priority == y->priority)
  270. return strcmp (x->pmod->name, y->pmod->name);
  271. return (y->priority - x->priority);
  272. }
  273. static int
  274. _mod_find_misc (mod_t mod, const char *name)
  275. {
  276. if (strcmp (mod->pmod->type, "misc") != 0)
  277. return 0;
  278. if (strcmp (mod->pmod->name, name) != 0)
  279. return 0;
  280. return 1;
  281. }
  282. static int
  283. _mod_initialize_by_name (char *name, List l)
  284. {
  285. mod_t mod = list_find_first (l, (ListFindF) _mod_find_misc, name);
  286. if (mod != NULL && _mod_initialize (mod) < 0)
  287. err("%p: Warning: Failed to initialize requested module \"%s/%s\"\n",
  288. mod->pmod->type, mod->pmod->name);
  289. return (0);
  290. }
  291. static int _mod_initialize_modules_by_name (char *names, List m)
  292. {
  293. List l;
  294. if (names == NULL)
  295. return (0);
  296. l = list_split (",", names);
  297. list_for_each (l, (ListForF) _mod_initialize_by_name, m);
  298. return (0);
  299. }
  300. int mod_load_modules(const char *dir, opt_t *opt)
  301. {
  302. int rc = 0;
  303. #if STATIC_MODULES
  304. rc = _mod_load_static_modules();
  305. #else
  306. rc = _mod_load_dynamic_modules(dir, opt);
  307. #endif
  308. list_sort(module_list, (ListCmpF) _cmp_f);
  309. /*
  310. * Initialize misc modules by name
  311. */
  312. _mod_initialize_modules_by_name (opt->misc_modules, module_list);
  313. /*
  314. * Initialize remaining modules in modules_list:
  315. */
  316. list_for_each (module_list, (ListForF) _mod_init_list_safe, NULL);
  317. return(rc);
  318. }
  319. /*
  320. * Print all options from module option table 'p,' aligning description
  321. * with column 'col'
  322. */
  323. static void
  324. _print_option_help(struct pdsh_module_option *p, int col)
  325. {
  326. char buf[81];
  327. assert(p != NULL);
  328. snprintf(buf, 81, "-%c %-*s %s\n", p->opt, col - 4,
  329. (p->arginfo ? p->arginfo : ""), p->descr);
  330. err("%s", buf);
  331. }
  332. void
  333. mod_print_options(mod_t mod, int col)
  334. {
  335. struct pdsh_module_option *p;
  336. assert(mod != NULL);
  337. assert(mod->pmod != NULL);
  338. p = mod->pmod->opt_table;
  339. if (!p || !p->opt)
  340. return;
  341. /*
  342. * out("%s/%s Options:\n", mod->pmod->type, mod->pmod->name);
  343. */
  344. for (p = mod->pmod->opt_table; p && (p->opt != 0); p++)
  345. _print_option_help(p, col);
  346. }
  347. /*
  348. * Print to stdout information stanza for module "mod"
  349. */
  350. static int
  351. _mod_print_info(mod_t mod)
  352. {
  353. if (mod == NULL)
  354. return 0;
  355. out("Module: %s/%s\n", mod->pmod->type, mod->pmod->name);
  356. out("Author: %s\n", mod->pmod->author ? mod->pmod->author : "???");
  357. out("Descr: %s\n", mod->pmod->descr ? mod->pmod->descr : "???");
  358. out("Active: %s\n", mod->initialized ? "yes" : "no");
  359. if (mod->pmod->opt_table && mod->pmod->opt_table->opt) {
  360. out("Options:\n");
  361. mod_print_options(mod, 18);
  362. }
  363. out("\n");
  364. return 0;
  365. }
  366. static int _opt_print(mod_t mod, int *col)
  367. {
  368. if (mod->initialized)
  369. mod_print_options(mod, *col);
  370. return 0;
  371. }
  372. void mod_print_all_options(int col)
  373. {
  374. list_for_each(module_list, (ListForF) _opt_print, &col);
  375. }
  376. static int
  377. _cmp_type(mod_t mod, char *type)
  378. {
  379. return (strcmp(mod->pmod->type, type) == 0);
  380. }
  381. int
  382. mod_count(char *type)
  383. {
  384. int i = 0;
  385. ListIterator module_itr;
  386. assert(module_list != NULL);
  387. if (type == NULL)
  388. return list_count(module_list);
  389. if (!(module_itr = list_iterator_create(module_list))) {
  390. err("Unable to create module list iterator\n");
  391. return -1;
  392. }
  393. while (list_find(module_itr, (ListFindF) _cmp_type, type))
  394. i++;
  395. list_iterator_destroy(module_itr);
  396. return i;
  397. }
  398. static List
  399. _mod_get_module_names(char *type, int get_active)
  400. {
  401. List l;
  402. mod_t mod;
  403. ListIterator module_itr;
  404. assert(module_list != NULL);
  405. l = list_create(NULL);
  406. if (!(module_itr = list_iterator_create(module_list))) {
  407. err("Unable to create module list iterator\n");
  408. list_destroy(l);
  409. return NULL;
  410. }
  411. while ((mod = (type ? list_find(module_itr, (ListFindF) _cmp_type, type)
  412. : list_next(module_itr)))) {
  413. /*
  414. * Push active (initialized) modules onto list if get_active
  415. * is true, otherwise push inactive (!initialized) modules:
  416. */
  417. if (!get_active == !mod->initialized)
  418. list_push(l, mod->pmod->name);
  419. }
  420. list_iterator_destroy(module_itr);
  421. return l;
  422. }
  423. List mod_get_module_names (char *type)
  424. {
  425. return _mod_get_module_names (type, 1);
  426. }
  427. List mod_get_uninitialized_module_names (char *type)
  428. {
  429. return _mod_get_module_names (type, 0);
  430. }
  431. void
  432. mod_list_module_info(void)
  433. {
  434. int nmodules = list_count(module_list);
  435. out("%d module%s loaded:\n\n", nmodules, (nmodules > 1 ? "s" : ""));
  436. if (nmodules == 0)
  437. return;
  438. list_for_each(module_list, (ListForF) _mod_print_info, NULL);
  439. }
  440. static int
  441. _mod_description_match (mod_t m, const char *type, const char *name)
  442. {
  443. if ( (strcmp(m->pmod->type, type) == 0)
  444. && (strcmp(m->pmod->name, name) == 0) )
  445. return (1);
  446. return (0);
  447. }
  448. mod_t
  449. mod_get_module(const char *type, const char *name)
  450. {
  451. mod_t mod;
  452. ListIterator module_itr;
  453. assert(type != NULL);
  454. assert(name != NULL);
  455. if (!(module_itr = list_iterator_create(module_list))) {
  456. err("Unable to create module list iterator\n");
  457. return NULL;
  458. }
  459. while ((mod = list_next(module_itr))) {
  460. if (_mod_description_match (mod, type, name))
  461. break;
  462. }
  463. list_iterator_destroy(module_itr);
  464. return mod;
  465. }
  466. char *
  467. mod_get_name(mod_t mod)
  468. {
  469. assert(mod != NULL);
  470. assert(mod->pmod != NULL);
  471. return mod->pmod->name;
  472. }
  473. char *
  474. mod_get_type(mod_t mod)
  475. {
  476. assert(mod != NULL);
  477. assert(mod->pmod != NULL);
  478. return mod->pmod->type;
  479. }
  480. RcmdInitF
  481. mod_get_rcmd_init(mod_t mod) {
  482. assert(mod != NULL);
  483. assert(mod->pmod != NULL);
  484. if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_init)
  485. return mod->pmod->rcmd_ops->rcmd_init;
  486. else
  487. return NULL;
  488. }
  489. RcmdSigF
  490. mod_get_rcmd_signal(mod_t mod) {
  491. assert(mod != NULL);
  492. assert(mod->pmod != NULL);
  493. if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_signal)
  494. return mod->pmod->rcmd_ops->rcmd_signal;
  495. else
  496. return NULL;
  497. }
  498. RcmdF
  499. mod_get_rcmd(mod_t mod) {
  500. assert(mod != NULL);
  501. assert(mod->pmod != NULL);
  502. if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd)
  503. return mod->pmod->rcmd_ops->rcmd;
  504. else
  505. return NULL;
  506. }
  507. RcmdDestroyF
  508. mod_get_rcmd_destroy (mod_t mod)
  509. {
  510. assert (mod != NULL);
  511. assert (mod->pmod != NULL);
  512. if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_destroy)
  513. return mod->pmod->rcmd_ops->rcmd_destroy;
  514. else
  515. return NULL;
  516. }
  517. int
  518. mod_process_opt(opt_t *opt, int c, char *optarg)
  519. {
  520. mod_t mod;
  521. struct pdsh_module_option *p = NULL;
  522. ListIterator module_itr;
  523. if (!(module_itr = list_iterator_create(module_list))) {
  524. err("Unable to create module list iterator\n");
  525. return -1;
  526. }
  527. while ((mod = _mod_next_active (module_itr))) {
  528. if ((p = _mod_find_opt(mod, c))) {
  529. list_iterator_destroy(module_itr);
  530. return p->f(opt, c, optarg);
  531. }
  532. }
  533. list_iterator_destroy(module_itr);
  534. return -1;
  535. }
  536. /*
  537. * Return pointer to pdsh_module_option struct for option `opt'
  538. * or NULL if no loaded module provides that option.
  539. */
  540. static struct pdsh_module_option *
  541. _mod_find_opt(mod_t mod, int opt)
  542. {
  543. struct pdsh_module_option *p = mod->pmod->opt_table;
  544. for (p = mod->pmod->opt_table; p && (p->opt != 0); p++)
  545. if (p->opt == opt) return p;
  546. return NULL;
  547. }
  548. static int
  549. _mod_delete (const char *type, const char *name)
  550. {
  551. ListIterator i = list_iterator_create (module_list);
  552. mod_t m;
  553. while ((m = list_next (i))) {
  554. if (_mod_description_match (m, type, name))
  555. list_delete (i);
  556. }
  557. return (0);
  558. }
  559. static int _mod_register (mod_t mod, const char *name)
  560. {
  561. mod_t prev;
  562. /*
  563. * Must have atleast a name and type
  564. */
  565. if (!mod->pmod->type || !mod->pmod->name) {
  566. err("%p:[%s] type or name not specified in module\n", name);
  567. return -1;
  568. }
  569. /*
  570. * Check for existing module of the same type and name
  571. * Delete previous module if its priority is higher.
  572. */
  573. if ((prev = mod_get_module (mod->pmod->type, mod->pmod->name))) {
  574. err("%p: %s: [%s/%s] already loaded from [%s]\n",
  575. mod->filename, mod->pmod->type, mod->pmod->name,
  576. prev->filename);
  577. if (mod->priority > prev->priority)
  578. _mod_delete (mod->pmod->type, mod->pmod->name);
  579. else
  580. return (-1);
  581. }
  582. /*
  583. * Continue with module loading only if personality acceptable
  584. */
  585. if (!(mod->pmod->personality & pdsh_personality()))
  586. return -1;
  587. list_prepend(module_list, mod);
  588. return 0;
  589. }
  590. static int _mod_initialize (mod_t mod)
  591. {
  592. if (!_mod_opts_ok(mod))
  593. return -1;
  594. if (mod->pmod->mod_ops &&
  595. mod->pmod->mod_ops->init &&
  596. ((*mod->pmod->mod_ops->init)() < 0)) {
  597. err("%p: error: %s/%s failed to initialize.\n",
  598. mod->pmod->type, mod->pmod->name);
  599. return -1;
  600. }
  601. mod->initialized = 1;
  602. return 0;
  603. }
  604. /*
  605. * Version of _mod_initialize that always returns zero
  606. * for use with list_for_each.
  607. */
  608. static int _mod_init_list_safe (mod_t mod, void *arg)
  609. {
  610. _mod_initialize (mod);
  611. return 0;
  612. }
  613. #if STATIC_MODULES
  614. /*
  615. * Set pdsh module pointer (pmod) to point to address from
  616. * statically defined external static_mods array.
  617. */
  618. static int
  619. _mod_load_static(int idx)
  620. {
  621. mod_t mod = mod_create();
  622. mod->pmod = static_mods[idx];
  623. mod->priority = *priority[idx];
  624. mod->filename = Strdup("static");
  625. _mod_register(mod, static_mod_names[idx]);
  626. return 0;
  627. }
  628. /*
  629. * Load all statically defined modules from internal static_mods array
  630. */
  631. static int
  632. _mod_load_static_modules(void)
  633. {
  634. int i = 0;
  635. while (static_mods[i] != NULL) {
  636. if (_mod_load_static(i++) < 0)
  637. continue;
  638. }
  639. return 0;
  640. }
  641. #else /* !STATIC_MODULES */
  642. /*
  643. * Load a single module from file `fq_path' and append to module_list.
  644. */
  645. static int
  646. _mod_load_dynamic(const char *fq_path)
  647. {
  648. mod_t mod = NULL;
  649. const lt_dlinfo *info;
  650. int *priority;
  651. assert(fq_path != NULL);
  652. mod = mod_create();
  653. if (!(mod->handle = lt_dlopen(fq_path)))
  654. goto fail;
  655. if (!(info = lt_dlgetinfo(mod->handle)))
  656. goto fail_libtool_broken;
  657. if (info->filename == NULL)
  658. goto fail_libtool_broken;
  659. mod->filename = Strdup(info->filename);
  660. if (_is_loaded(mod->filename)) {
  661. /* Module already loaded. This is OK, no need for
  662. * error message. (Could have already opened a .la and
  663. * we are now opening the corresponding .so
  664. */
  665. goto fail;
  666. }
  667. /* load all module info from the pdsh_module structure */
  668. if (!(mod->pmod = lt_dlsym(mod->handle, "pdsh_module_info"))) {
  669. err("%p:[%s] can't resolve pdsh module\n", mod->filename);
  670. goto fail;
  671. }
  672. if ((priority = lt_dlsym(mod->handle, "pdsh_module_priority")))
  673. mod->priority = *priority;
  674. if (_mod_register(mod, mod->filename) < 0)
  675. goto fail;
  676. return 0;
  677. fail_libtool_broken:
  678. /*
  679. * Avoid dlclose() of invalid handle
  680. */
  681. mod->handle = NULL;
  682. fail:
  683. _mod_destroy(mod);
  684. return -1;
  685. }
  686. static int
  687. _pdsh_owner(const char *pdsh_path, uid_t *pdsh_uid)
  688. {
  689. struct stat st;
  690. if (stat (pdsh_path, &st) < 0) {
  691. err ("%p: Unable to determine ownership of pdsh binary: %m\n");
  692. return -1;
  693. }
  694. *pdsh_uid = st.st_uid;
  695. return 0;
  696. }
  697. static int
  698. _mod_load_dynamic_modules(const char *dir, opt_t *pdsh_opts)
  699. {
  700. DIR *dirp = NULL;
  701. struct dirent *entry = NULL;
  702. char path[MAXPATHLEN + 1];
  703. char *p;
  704. int count = 0;
  705. uid_t pdsh_owner = 0;
  706. assert(dir != NULL);
  707. assert(*dir != '\0');
  708. if (!initialized)
  709. mod_init();
  710. if (_pdsh_owner(pdsh_opts->local_program_path, &pdsh_owner) < 0)
  711. return -1;
  712. if (!_path_permissions_ok(dir, pdsh_owner))
  713. return -1;
  714. if (!(dirp = opendir(dir)))
  715. return -1;
  716. strncpy(path, dir, MAXPATHLEN);
  717. p = path + strlen(dir);
  718. *(p++) = '/';
  719. while ((entry = readdir(dirp))) {
  720. struct stat st;
  721. strcpy(p, entry->d_name);
  722. if (stat(path, &st) < 0)
  723. continue;
  724. if (!S_ISREG(st.st_mode))
  725. continue;
  726. /*
  727. * Do not load modules that could have been altered by
  728. * a user other than root or the current user or the user
  729. * owning the pdsh executable. Otherwise pdsh could execute
  730. * arbitrary code.
  731. */
  732. if ( (st.st_uid != 0) && (st.st_uid != getuid())
  733. && (st.st_uid != pdsh_owner)) {
  734. err ("%p: skipping insecure module \"%s\" (check owner)\n", path);
  735. continue;
  736. }
  737. if (st.st_mode & S_IWOTH) {
  738. err ("%p: skipping insecure module \"%s\" (check perms)\n", path);
  739. continue;
  740. }
  741. if (_mod_load_dynamic(path) < 0)
  742. continue;
  743. count++;
  744. }
  745. if (closedir(dirp) < 0)
  746. err("%p: error closing %s: %m", dir);
  747. if (count == 0)
  748. errx("%p: no modules found\n");
  749. return 0;
  750. }
  751. static int _cmp_filenames(mod_t mod, char *filename)
  752. {
  753. return (strcmp(mod->filename, filename) == 0);
  754. }
  755. static int
  756. _is_loaded(char *filename)
  757. {
  758. if (list_find_first(module_list, (ListFindF) _cmp_filenames, filename))
  759. return 1;
  760. return 0;
  761. }
  762. static char *
  763. _perm_error_string (perm_error_t error)
  764. {
  765. switch (error) {
  766. case DIR_OK:
  767. return ("Permissions are valid");
  768. case DIR_NOT_DIRECTORY:
  769. return ("Not a directory");
  770. case DIR_BAD_OWNER:
  771. return ("Owner not root, current uid, or pdsh executable owner");
  772. case DIR_WORLD_WRITABLE:
  773. return ("World writable and sticky bit is not set");
  774. default:
  775. break;
  776. }
  777. return ("Unspecified error");
  778. }
  779. /*
  780. * Return permissions error if stat buffer shows any of the below
  781. * 1. This is not a directory.
  782. * 2. Ownership something other than root, the current uid, or the
  783. * same ownership as the pdsh executable.
  784. * 3. Permissions are world writable and sticky bit is not set.
  785. */
  786. static perm_error_t
  787. _dir_permission_error(struct stat *st, uid_t alt_uid)
  788. {
  789. if (!S_ISDIR(st->st_mode))
  790. return DIR_NOT_DIRECTORY;
  791. if ( (st->st_uid != 0) && (st->st_uid != getuid())
  792. && (st->st_uid != alt_uid))
  793. return DIR_BAD_OWNER;
  794. if ((st->st_mode & S_IWOTH) && !(st->st_mode & S_ISVTX))
  795. return DIR_WORLD_WRITABLE;
  796. return DIR_OK;
  797. }
  798. /*
  799. * Temprarily chdir() to path and use getcwd to return real patch
  800. * to caller.
  801. */
  802. static char *
  803. _get_dir_name (const char *path, char *buf, size_t len)
  804. {
  805. int pathlen = 256;
  806. char * orig_path = Malloc (pathlen * sizeof (char));
  807. while (!getcwd (orig_path, pathlen) && (pathlen < MAXPATHLEN*2))
  808. Realloc ((void **) &orig_path, pathlen*=2 * sizeof (char));
  809. if (chdir (path) < 0)
  810. errx ("Unable to chdir() to %s: %m", path);
  811. if (!getcwd (buf, len))
  812. errx ("Unable to get working directory for module path: %s\n",
  813. path);
  814. if (chdir (orig_path) < 0)
  815. err ("Unable to return to original working directory: %s: %m\n",
  816. orig_path);
  817. Free ((void **) &orig_path);
  818. return (buf);
  819. }
  820. /*
  821. * Returns true if, for the directory "dir" and all of its parent
  822. * directories, the following are true:
  823. * - ownership is root or the calling user (as returned by getuid())
  824. * or same ownership as the pdsh or pdcp binary.
  825. * - directory has user write permission only
  826. *
  827. * Returns false if one of the assertions above are false for any
  828. * directory in the tree.
  829. *
  830. */
  831. static bool
  832. _path_permissions_ok(const char *dir, uid_t pdsh_owner)
  833. {
  834. struct stat st;
  835. char dirbuf[MAXPATHLEN + 1];
  836. dev_t rootdev;
  837. ino_t rootino;
  838. perm_error_t error;
  839. int pos = 0;
  840. assert(dir != NULL);
  841. if (stat("/", &st) < 0) {
  842. err("%p: Can't stat root directory: %m\n");
  843. return false;
  844. }
  845. rootdev = st.st_dev;
  846. rootino = st.st_ino;
  847. strncpy(dirbuf, dir, MAXPATHLEN);
  848. dirbuf[MAXPATHLEN] = '\0';
  849. pos = strlen(dirbuf);
  850. do {
  851. if (stat(dirbuf, &st) < 0) {
  852. err("%p: Can't stat \"%s\": %m\n", dir);
  853. return false;
  854. }
  855. if ((error = _dir_permission_error(&st, pdsh_owner)) != DIR_OK) {
  856. char buf [MAXPATHLEN];
  857. err("%p: module path \"%s\" insecure.\n", dir);
  858. err("%p: \"%s\": %s\n",
  859. _get_dir_name (dirbuf, buf, MAXPATHLEN),
  860. _perm_error_string (error));
  861. return false;
  862. }
  863. /* Check for impending overflow
  864. */
  865. if (pos > MAXPATHLEN - 3) {
  866. err("%p :-( Path too long while checking permissions\n");
  867. return false;
  868. }
  869. /* Check parent
  870. */
  871. strncpy(&dirbuf[pos], "/..", 4);
  872. pos+=3;
  873. } while ( !((st.st_ino == rootino) && (st.st_dev == rootdev)) );
  874. return true;
  875. }
  876. #endif /* !STATIC_MODULES */
  877. /*
  878. * vi:tabstop=4 shiftwidth=4 expandtab
  879. */