PageRenderTime 91ms CodeModel.GetById 10ms app.highlight 72ms RepoModel.GetById 2ms app.codeStats 0ms

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