/src/modules/genders.c
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 */