PageRenderTime 99ms CodeModel.GetById 34ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 1ms

/src/modules/genders.c

https://code.google.com/
C | 682 lines | 450 code | 117 blank | 115 comment | 96 complexity | b86317f6ccff075c95d5d746bb32ea54 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
 30#include <stdio.h>
 31#include <stdlib.h>
 32#include <string.h>
 33
 34#include <genders.h>
 35
 36#include "src/common/hostlist.h"
 37#include "src/common/err.h"
 38#include "src/common/xmalloc.h"
 39#include "src/common/split.h"
 40#include "src/common/xstring.h"
 41#include "src/pdsh/mod.h"
 42#include "src/pdsh/rcmd.h"
 43
 44#define ALL_NODES NULL
 45
 46#ifndef GENDERS_ALTNAME_ATTRIBUTE
 47#  define GENDERS_ALTNAME_ATTRIBUTE   "altname"
 48#endif
 49
 50#if STATIC_MODULES
 51#  define pdsh_module_info genders_module_info
 52#  define pdsh_module_priority genders_module_priority
 53#endif    
 54
 55int pdsh_module_priority = DEFAULT_MODULE_PRIORITY;
 56
 57
 58/*
 59 *  Static genders module interface routines:
 60 */
 61static hostlist_t genders_wcoll(opt_t *pdsh_opts);
 62static int        genders_process_opt(opt_t *, int, char *);
 63static int        genders_init(void);
 64static int        genders_fini(void);
 65static int        genders_postop(opt_t *);
 66
 67
 68#if !GENDERS_G_ONLY
 69
 70static bool allnodes   = false;
 71static bool opt_i      = false;
 72#endif /* !GENDERS_G_ONLY */
 73static bool genders_opt_invoked = false;
 74static bool generate_altnames =   false;
 75
 76static genders_t gh    = NULL;
 77static char *gfile     = NULL;
 78static List attrlist   = NULL;
 79static List excllist   = NULL;
 80
 81/* 
 82 * Export pdsh module operations structure
 83 */
 84struct pdsh_module_operations genders_module_ops = {
 85    (ModInitF)       genders_init, 
 86    (ModExitF)       genders_fini, 
 87    (ModReadWcollF)  genders_wcoll, 
 88    (ModPostOpF)     genders_postop,
 89};
 90
 91/* 
 92 * Export rcmd module operations
 93 */
 94struct pdsh_rcmd_operations genders_rcmd_ops = {
 95    (RcmdInitF)  NULL,
 96    (RcmdSigF)   NULL,
 97    (RcmdF)      NULL,
 98};
 99
100/* 
101 * Export module options
102 */
103struct pdsh_module_option genders_module_options[] = 
104 { 
105   { 'g', "query,...", 
106     "target nodes using genders query",
107     DSH | PCP, (optFunc) genders_process_opt 
108   },
109   { 'X', "query,...", 
110     "exclude nodes using genders query",
111     DSH | PCP, (optFunc) genders_process_opt
112   },
113   { 'F', "file",          "use alternate genders file `file'",
114     DSH | PCP, (optFunc) genders_process_opt
115   },
116#if !GENDERS_G_ONLY
117   { 'i', NULL, "request alternate or canonical hostnames if applicable",
118     DSH | PCP, (optFunc) genders_process_opt
119   },
120   { 'a', NULL, "target all nodes except those with \"pdsh_all_skip\" attribute", 
121     DSH | PCP, (optFunc) genders_process_opt 
122   },
123   { 'A', NULL, "target all nodes listed in genders database",
124     DSH | PCP, (optFunc) genders_process_opt
125   },
126#endif /* !GENDERS_G_ONLY */
127   PDSH_OPT_TABLE_END
128 };
129
130/* 
131 * Genders module info 
132 */
133struct pdsh_module pdsh_module_info = {
134    "misc",
135#if GENDERS_G_ONLY
136    "genders-g",
137#else
138    "genders",
139#endif /* GENDERS_G_ONLY */
140    "Jim Garlick <garlick@llnl.gov>",
141    "target nodes using libgenders and genders attributes",
142    DSH | PCP, 
143
144    &genders_module_ops,
145    &genders_rcmd_ops,
146    &genders_module_options[0],
147};
148
149/*
150 * Static prototypes
151 */
152static genders_t  _handle_create();
153static hostlist_t _genders_to_altnames(genders_t g, hostlist_t hl);
154static hostlist_t _read_genders(List l);
155static void       _genders_opt_verify(opt_t *opt);
156static int        _delete_all (hostlist_t hl, hostlist_t dl);
157static int        register_genders_rcmd_types (opt_t *opt);
158
159
160/*
161 *  Functions:
162 */
163int
164genders_process_opt(opt_t *pdsh_opts, int opt, char *arg)
165{
166    switch (opt) {
167#if !GENDERS_G_ONLY
168    case 'a':  
169        /* For -a, exclude nodes with "pdsh_all_skip" */ 
170        excllist = list_split_append (excllist, ",", "pdsh_all_skip");
171    case 'A':
172        allnodes = true;
173        break;
174    case 'i':
175        opt_i    = true;
176        break;
177#endif /* !GENDERS_G_ONLY */
178    case 'g':
179        attrlist = list_split_append (attrlist, ",", arg);
180        break;
181    case 'X':
182        excllist = list_split_append (excllist, ",", arg);
183        break;
184    case 'F':
185        gfile = Strdup (arg);
186        break;
187    default:
188        err("%p: genders_process_opt: invalid option `%c'\n", opt);
189        return -1;
190        break;
191    }
192    genders_opt_invoked = true;
193    return 0;
194}
195
196static int
197genders_init(void)
198{
199    return 0;
200}
201
202static int
203genders_fini(void)
204{
205    if (attrlist)
206        list_destroy (attrlist);
207
208    if (excllist)
209        list_destroy (excllist);
210
211    if ((gh != NULL) && (genders_handle_destroy(gh) < 0))
212        errx("%p: Error destroying genders handle: %s\n", genders_errormsg(gh));
213
214    return 0;
215}
216
217static hostlist_t 
218genders_wcoll(opt_t *opt)
219{
220    _genders_opt_verify(opt);
221
222    if (opt->wcoll)
223        return (NULL);
224
225#if GENDERS_G_ONLY
226    if (!attrlist)
227        return NULL;
228#else
229    if (!allnodes && !attrlist)
230        return NULL;
231#endif /* !GENDERS_G_ONLY */
232
233    if (gh == NULL)
234        gh = _handle_create();
235
236    generate_altnames = true;
237    return _read_genders(attrlist);
238}
239
240/*
241 *  Return true if host returns true for any one genders query
242 *   in list iterator i.
243 */
244static int g_host_matches (const char *host, ListIterator i)
245{
246    char altname [1024];
247    int has_altname = 0;
248    size_t len = sizeof (altname);
249    const char altattr[] = GENDERS_ALTNAME_ATTRIBUTE;
250    char *query;
251
252    if (genders_testattr (gh, host, altattr, altname, len))
253        has_altname = 1;
254
255    list_iterator_reset (i);
256    while ((query = list_next (i))) {
257        if (genders_testquery (gh, host, query) == 1)
258            return (1);
259        if (has_altname && genders_testquery (gh, altname, query) == 1)
260            return (1);
261    }
262    return (0);
263}
264
265/*
266 *  Filter hostlist hl on a list of genders queries in query_list.
267 *   Multiple queries are ORed together, so a given host must only
268 *   match a single query.
269 */
270static void genders_filter (hostlist_t hl, List query_list)
271{
272    char *s;
273    hostlist_iterator_t i = hostlist_iterator_create (hl);
274    ListIterator qi = list_iterator_create (query_list);
275
276    if ((i == NULL) || (qi == NULL)) {
277        err ("%p: genders: failed to create list or hostlist iterator\n");
278        return;
279    }
280
281    while ((s = hostlist_next (i))) {
282        if (!g_host_matches (s, qi))
283            hostlist_remove (i);
284    }
285    hostlist_iterator_destroy (i);
286    list_iterator_destroy (qi);
287}
288
289static int
290genders_postop(opt_t *opt)
291{
292    hostlist_t hl = NULL;
293
294    if (!opt->wcoll)
295        return (0);
296
297    if (gh == NULL)
298        gh = _handle_create();
299
300    if (attrlist)
301        genders_filter (opt->wcoll, attrlist);
302
303    if (excllist && (hl = _read_genders (excllist))) {
304        hostlist_t altlist = _genders_to_altnames (gh, hl);
305        _delete_all (opt->wcoll, hl);
306        _delete_all (opt->wcoll, altlist);
307
308        hostlist_destroy (altlist);
309        hostlist_destroy (hl);
310    }
311
312#if !GENDERS_G_ONLY
313    /*
314     *  Genders module returns altnames by default, but only
315     *   when genders fills in wcoll or with -i, not when filtering via
316     *   -g or -X.
317     */
318    if ((generate_altnames && !opt_i) || (!generate_altnames && opt_i)) {
319        hostlist_t hl = opt->wcoll;
320        opt->wcoll = _genders_to_altnames(gh, hl);
321        hostlist_destroy(hl);
322    }
323#endif
324
325    /*
326     *  Apply any pdsh_rcmd_type setting from genders file
327     *   based on current opt->wcoll.
328     */
329    register_genders_rcmd_types (opt);
330
331    return (0);
332}
333
334
335/* 
336 * Verify options passed to this module
337 */
338static void
339_genders_opt_verify(opt_t *opt)
340{
341#if !GENDERS_G_ONLY
342/*    if (altnames && !allnodes && (gend_attr == NULL)) {
343 *       err("%p: Warning: Ignoring -i without -a or -g\n");
344 *       altnames = false;
345 *   }
346 */
347    if (allnodes && (attrlist != NULL))
348        errx("%p: Do not specify -a with -g\n");
349#endif /* !GENDERS_G_ONLY */
350    return;
351}
352
353static int
354_maxnamelen (genders_t g)
355{
356    int maxvallen, maxnodelen;
357    if ((maxvallen = genders_getmaxvallen(g)) < 0)
358        errx("%p: genders: getmaxvallen: %s\n", genders_errormsg(g));
359    if ((maxnodelen = genders_getmaxvallen(g)) < 0)
360        errx("%p: genders: getmaxnodelen: %s\n", genders_errormsg(g));
361
362    return (maxvallen > maxnodelen ? maxvallen : maxnodelen);
363}
364
365
366static hostlist_t
367_genders_to_altnames(genders_t g, hostlist_t hl)
368{
369    hostlist_t retlist = NULL;
370    hostlist_iterator_t i = NULL;
371    int  maxlen = 0;
372    char *altname = NULL;
373    char *altattr = GENDERS_ALTNAME_ATTRIBUTE;
374    char *host    = NULL;
375    int  rc;
376
377    if ((retlist = hostlist_create(NULL)) == NULL)
378        errx("%p: genders: hostlist_create: %m\n");
379
380    maxlen = _maxnamelen (g);
381    altname = Malloc (maxlen + 1);
382
383    if ((i = hostlist_iterator_create(hl)) == NULL)
384        errx("%p: genders: hostlist_iterator_create: %m");
385
386    while ((host = hostlist_next (i))) {
387        memset(altname, '\0', maxlen);
388
389        rc = genders_testattr(g, host, altattr, altname, maxlen + 1);
390
391        /*
392         *  If node not found, attempt to lookup canonical name via
393         *   altername name.
394         */
395        if ((rc < 0) && (genders_errnum(g) == GENDERS_ERR_NOTFOUND)) 
396            rc = genders_getnodes (g, &altname, 1, altattr, host);
397
398        if (hostlist_push_host(retlist, (rc > 0 ? altname : host)) <= 0)
399            err("%p: genders: warning: target `%s' not parsed: %m", host);
400
401        free(host);
402    }
403
404    hostlist_iterator_destroy(i);
405
406    Free((void **) &altname);
407
408    return (retlist);
409}
410
411static hostlist_t 
412_genders_to_hostlist(genders_t gh, char **nodes, int nnodes)
413{
414    hostlist_t hl = NULL;
415    int i;
416
417    if ((hl = hostlist_create(NULL)) == NULL)
418        errx("%p: genders: hostlist_create failed: %m");
419
420    for (i = 0; i < nnodes; i++) {
421        if (hostlist_push_host(hl, nodes[i]) <= 0)
422            err("%p: warning: target `%s' not parsed: %m\n", nodes[i]);
423    }
424
425    hostlist_uniq(hl);
426
427    return hl;
428}
429
430static char * genders_filename_create (char *file)
431{
432    char *genders_file;
433    const char *gdir = getenv ("PDSH_GENDERS_DIR");
434
435    /*
436     *  Return a copy of filename if user specified an
437     *   absolute or relative path:
438     */
439    if (file[0] == '/' || file[0] == '.')
440        return Strdup (file);
441
442    /*
443     *  Otherwise, append filename to
444     *   PDSH_GENDERS_DIR (or /etc by default)
445     */
446    genders_file = gdir ? Strdup (gdir) : Strdup ("/etc");
447    xstrcatchar (&genders_file, '/');
448    xstrcat (&genders_file, file);
449
450    return (genders_file);
451}
452
453static genders_t _handle_create()
454{
455    char *gfile_env;
456    char *genders_file = NULL;
457    genders_t gh = NULL;
458
459    if ((gh = genders_handle_create()) == NULL)
460        errx("%p: Unable to create genders handle: %m\n");
461
462    if (gfile)
463        genders_file = genders_filename_create (gfile);
464    else if ((gfile_env = getenv ("PDSH_GENDERS_FILE")))
465        genders_file = genders_filename_create (gfile_env);
466    else
467        genders_file = genders_filename_create ("genders");
468
469    /*
470     *  Only exit on error from genders_load_data() if an genders
471     *   module option was explicitly invoked:
472     */
473    if ((genders_load_data(gh, genders_file) < 0) && genders_opt_invoked)
474        errx("%p: %s: %s\n", genders_file, genders_errormsg(gh));
475
476    return gh;
477}
478
479/*
480 *  Search attr argument for an '=' char indicating an
481 *   attr=value pair. If found, nullify '=' and return
482 *   pointer to value part.
483 *
484 *  Returns NULL if no '=' found.
485 */
486#if !HAVE_GENDERS_QUERY
487static char *
488_get_val(char *attr)
489{
490    char *val = NULL;
491
492    if (attr == NULL)
493        return (NULL);
494
495    if ((val = strchr(attr, '='))) {
496        *val = '\0';
497        val++;
498    }
499
500    return (val);
501}
502#endif /* !HAVE_GENDERS_QUERY */
503
504static hostlist_t 
505_read_genders_attr(char *query)
506{
507    hostlist_t hl = NULL;
508    char **nodes;
509    int len, nnodes;
510
511    if ((len = genders_nodelist_create(gh, &nodes)) < 0)
512        errx("%p: genders: nodelist_create: %s\n", genders_errormsg(gh));
513
514#if HAVE_GENDERS_QUERY
515    if ((nnodes = genders_query (gh, nodes, len, query)) < 0) {
516        errx("%p: Error querying genders for query \"%s\": %s\n", 
517                query ? query : "(all)", genders_errormsg(gh));
518    }
519#else /* !HAVE_GENDERS_QUERY */
520    {
521        /* query defaults to just an attribute or attribute=value pair */
522        char *val;
523        val = _get_val(query);
524        if ((nnodes = genders_getnodes(gh, nodes, len, query, val)) < 0) {
525            errx("%p: Error querying genders for attr \"%s\": %s\n", 
526                 query ? query : "(all)", genders_errormsg(gh));
527        }
528    }
529#endif /* HAVE_GENDERS_QUERY */
530
531    hl = _genders_to_hostlist(gh, nodes, nnodes);
532
533    if (genders_nodelist_destroy(gh, nodes) < 0) {
534        errx("%p: Error destroying genders node list: %s\n",
535                genders_errormsg(gh));
536    }
537
538    return hl;
539}
540
541static hostlist_t 
542_read_genders (List attrs)
543{
544    ListIterator i  = NULL;
545    hostlist_t   hl = NULL;
546    char *    query = NULL;
547
548    if ((attrs == NULL) && (allnodes)) /* Special "all nodes" case */
549        return _read_genders_attr (ALL_NODES);
550
551    if ((attrs == NULL) || (list_count (attrs) == 0))
552        return NULL;
553
554    if ((i = list_iterator_create (attrs)) == NULL)
555        errx ("genders: unable to create list iterator: %m\n");
556
557    while ((query = list_next (i))) {
558        hostlist_t l = _read_genders_attr (query);
559
560        if (hl == NULL) {
561            hl = l;
562        } else {
563            hostlist_push_list (hl, l);
564            hostlist_destroy (l);
565        }
566    }
567
568    list_iterator_destroy (i);
569
570    hostlist_uniq (hl);
571
572    return (hl);
573}
574
575static int 
576attrval_by_altname (genders_t g, const char *host, const char *attr,
577                           char *val, int len)
578{   
579    char *altname = NULL;
580    char *altattr = GENDERS_ALTNAME_ATTRIBUTE;
581    int maxlen = _maxnamelen (g);
582    int rc = -1;
583
584    altname = Malloc (maxlen + 1);
585    memset (altname, 0, maxlen);
586
587    if ((rc = genders_getnodes (g, &altname, 1, altattr, host)) > 0)
588        rc = genders_testattr (g, altname, attr, val, sizeof (val));
589
590    Free ((void **) &altname);
591
592    return rc;
593}
594
595/*
596 *  Parse the value of "pdsh_rcmd_type" and split into user and rcmd
597 *   strings, passing rcmd name (if any) in *rp, and user name (if any)
598 *   in *up.
599 *   
600 *  Allows pdsh_rcmd_type to be set to [user@][rcmd], where user@ and 
601 *   rcmd are both optional. (i.e. you can set user or rcmd or both)
602 */
603static int rcmd_type_parse (char *val, char **rp, char **up)
604{
605    char *p;
606    *up = NULL;
607    *rp = NULL;
608
609    if ((p = strchr (val, '@'))) {
610        *(p)++ = '\0';
611        *up = val;
612        if (strlen (p) != 0)
613            *rp = p;
614    } else
615        *rp = val;
616
617    return (0);
618}
619
620static int
621register_genders_rcmd_types (opt_t *opt)
622{
623    char *host;
624    char *rcmd;
625    char *user;
626    char val[64];
627    char rcmd_attr[] = "pdsh_rcmd_type";
628    hostlist_iterator_t i = NULL;
629
630    if (!opt->wcoll) 
631        return (0);
632
633    /* 
634     *  Assume no nodes have "pdsh_rcmd_type" attr if index fails:
635     */
636    if (genders_index_attrvals (gh, rcmd_attr) < 0)
637        return (0);
638
639    i = hostlist_iterator_create (opt->wcoll);
640    while ((host = hostlist_next (i))) {
641        int rc;
642        memset (val, 0, sizeof (val));
643        rc = genders_testattr (gh, host, rcmd_attr, val, sizeof (val));
644
645        /*
646         *  If host wasn't found, try to see if "host" is the altname
647         *   for this node, then lookup with the real name
648         */
649        if (rc < 0 && (genders_errnum(gh) == GENDERS_ERR_NOTFOUND)) 
650            rc = attrval_by_altname (gh, host, rcmd_attr, val, sizeof (val));
651        
652        rcmd_type_parse (val, &rcmd, &user);
653
654        if (rc > 0) 
655            rcmd_register_defaults (host, rcmd, user);
656
657        free (host);
658    }
659
660    hostlist_iterator_destroy (i);
661            
662    return 0;
663}
664
665static int 
666_delete_all (hostlist_t hl, hostlist_t dl)
667{
668    int                 rc   = 0;
669    char *              host = NULL;
670    hostlist_iterator_t i    = hostlist_iterator_create (dl);
671
672    while ((host = hostlist_next (i))) {
673        rc += hostlist_delete_host (hl, host);
674        free (host);
675    }
676    hostlist_iterator_destroy (i);
677    return (rc);
678}
679
680/*
681 * vi: tabstop=4 shiftwidth=4 expandtab
682 */