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