PageRenderTime 36ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pdsh/opt.c

https://code.google.com/
C | 1413 lines | 974 code | 209 blank | 230 comment | 277 complexity | c6d02f5c6dd8d0da47f11488336cdc20 MD5 | raw file
Possible License(s): GPL-2.0
  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_STRING_H
  30. #include <string.h> /* strcpy */
  31. #endif
  32. #include <stdlib.h> /* getenv */
  33. #include <pwd.h> /* getpwuid */
  34. #include <sys/param.h> /* MAXPATHLEN */
  35. #include <sys/types.h>
  36. #include <sys/stat.h> /* stat */
  37. #if HAVE_UNISTD_H
  38. #include <unistd.h> /* getopt */
  39. #endif
  40. #include <errno.h>
  41. #include <regex.h>
  42. #include <ctype.h>
  43. #include "src/common/hostlist.h"
  44. #include "src/common/err.h"
  45. #include "src/common/list.h"
  46. #include "src/common/split.h"
  47. #include "src/common/xstring.h"
  48. #include "src/common/xmalloc.h"
  49. #include "dsh.h"
  50. #include "opt.h"
  51. #include "wcoll.h"
  52. #include "mod.h"
  53. #include "rcmd.h"
  54. /*
  55. * Fallback maximum username length if sysconf(_SC_LOGIN_NAME_MAX) not
  56. * available or fails. (buffers will be this size + 1 for NUL termination)
  57. */
  58. #define DEFAULT_MAX_USERNAME_LENGTH 16
  59. #define OPT_USAGE_DSH "\
  60. Usage: pdsh [-options] command ...\n\
  61. -S return largest of remote command return values\n"
  62. /* -s option only useful on AIX */
  63. #if HAVE_MAGIC_RSHELL_CLEANUP
  64. #define OPT_USAGE_STDERR "\
  65. -s separate stderr and stdout\n"
  66. #endif
  67. #define OPT_USAGE_PCP "\
  68. Usage: pdcp [-options] src [src2...] dest\n\
  69. -r recursively copy files\n\
  70. -p preserve modification time and modes\n\
  71. -e PATH specify the path to pdcp on the remote machine\n"
  72. /* undocumented "-y" target must be directory option */
  73. /* undocumented "-z" run pdcp server option */
  74. /* undocumented "-Z" run pdcp client option */
  75. #define OPT_USAGE_RPCP "\
  76. Usage: rpdcp [-options] src [src2...] dir\n\
  77. -r recursively copy files\n\
  78. -p preserve modification time and modes\n"
  79. /* undocumented "-y" target must be directory option */
  80. /* undocumented "-z" run pdcp server option */
  81. /* undocumented "-Z" run pdcp client option */
  82. #define OPT_USAGE_COMMON "\
  83. -h output usage menu and quit\n\
  84. -V output version information and quit\n\
  85. -q list the option settings and quit\n\
  86. -b disable ^C status feature (batch mode)\n\
  87. -d enable extra debug information from ^C status\n\
  88. -l user execute remote commands as user\n\
  89. -t seconds set connect timeout (default is 10 sec)\n\
  90. -u seconds set command timeout (no default)\n\
  91. -f n use fanout of n nodes\n\
  92. -w host,host,... set target node list on command line\n\
  93. -x host,host,... set node exclusion list on command line\n\
  94. -R name set rcmd module to name\n\
  95. -M name,... select one or more misc modules to initialize first\n\
  96. -N disable hostname: labels on output lines\n\
  97. -L list info on all loaded modules and exit\n"
  98. /* undocumented "-T testcase" option */
  99. /* undocumented "-Q" option */
  100. /* undocumented "-K" option - keep domain name in output */
  101. #if HAVE_MAGIC_RSHELL_CLEANUP
  102. #define DSH_ARGS "sS"
  103. #else
  104. #define DSH_ARGS "S"
  105. #endif
  106. #define PCP_ARGS "pryzZe:"
  107. #define GEN_ARGS "hLNKR:M:t:cqf:w:x:l:u:bI:dVT:Q"
  108. /*
  109. * Pdsh options string (for getopt) -- initialized
  110. * in _init_pdsh_options(), and appended to by modules that
  111. * register new pdsh options.
  112. */
  113. static char *pdsh_options = NULL;
  114. /*
  115. * Pdsh personality
  116. */
  117. static pers_t personality = DSH;
  118. /*
  119. * Return the current pdsh "personality"
  120. */
  121. pers_t pdsh_personality(void)
  122. {
  123. return personality;
  124. }
  125. /*
  126. * Remote argv array
  127. */
  128. static char **remote_argv;
  129. static int remote_argc;
  130. const char **pdsh_remote_argv (void)
  131. {
  132. return (const char **) remote_argv;
  133. }
  134. int pdsh_remote_argc (void)
  135. {
  136. return remote_argc;
  137. }
  138. /*
  139. * List of explicitly excluded hosts and regex filter options:
  140. */
  141. static List exclude_list = NULL;
  142. static List regex_list = NULL;
  143. static void _usage(opt_t * opt);
  144. static void _show_version(void);
  145. static int wcoll_args_process (opt_t *opt, char *args);
  146. static void wcoll_apply_regex (opt_t *opt, List regexs);
  147. static void wcoll_apply_excluded (opt_t *opt, List excludes);
  148. static void wcoll_expand (opt_t *opt);
  149. static void
  150. _init_pdsh_options()
  151. {
  152. pdsh_options = Strdup(GEN_ARGS);
  153. if (personality == DSH) {
  154. xstrcat(&pdsh_options, DSH_ARGS);
  155. } else
  156. xstrcat(&pdsh_options, PCP_ARGS);
  157. }
  158. /*
  159. * Check whether options in 'opt_table' are available for
  160. * provisioning. Register the option if the option's personality
  161. * allows it. 'opt_table' points to the first module_option listed
  162. * in the module.
  163. *
  164. * Returns false if option is already used by pdsh or a pdsh module.
  165. * Returns true if option was successfully registered.
  166. */
  167. bool opt_register(struct pdsh_module_option *opt_table)
  168. {
  169. struct pdsh_module_option *p;
  170. if (opt_table == NULL)
  171. return true;
  172. if (pdsh_options == NULL)
  173. _init_pdsh_options();
  174. /* Don't register any options if we can't register all the options
  175. * in this module
  176. */
  177. for (p = opt_table; p && (p->opt != 0); p++) {
  178. if ( (personality & p->personality)
  179. && (strchr(pdsh_options, p->opt) != NULL))
  180. return false;
  181. }
  182. for (p = opt_table; p && (p->opt != 0); p++) {
  183. /* register only if the personality allows this option */
  184. if (p->personality & personality) {
  185. xstrcatchar(&pdsh_options, p->opt);
  186. if (p->arginfo != NULL)
  187. xstrcatchar(&pdsh_options, ':');
  188. }
  189. }
  190. return true;
  191. }
  192. /*
  193. * Check current path dir, cwd, and argv0 for path to program
  194. */
  195. char * _check_path(char *dir, char *cwd, char *argv0)
  196. {
  197. char *abspath = NULL;
  198. if (*dir != '/') {
  199. abspath = Strdup(cwd);
  200. xstrcat(&abspath, "/");
  201. }
  202. xstrcat(&abspath, dir);
  203. xstrcat(&abspath, "/");
  204. xstrcat(&abspath, argv0);
  205. if (access(abspath, R_OK) == 0)
  206. return abspath;
  207. Free((void **) &abspath);
  208. return NULL;
  209. }
  210. /*
  211. * Determine absolute path to the program name based on argv0
  212. */
  213. char * _find_path(char *argv0)
  214. {
  215. char *abspath = NULL;
  216. char cwd[MAXPATHLEN];
  217. if (*argv0 == '/') {
  218. /* is absolute path */
  219. abspath = Strdup(argv0);
  220. goto done;
  221. }
  222. if (getcwd(cwd, MAXPATHLEN) == NULL)
  223. errx("%p: getcwd failed: %m\n");
  224. if (*argv0 == '.' || strchr(argv0, '/') != NULL) {
  225. /* path relative */
  226. abspath = Strdup(cwd);
  227. xstrcat(&abspath, "/");
  228. xstrcat(&abspath, argv0);
  229. }
  230. else {
  231. /* argv0 executed in PATH environment var */
  232. char *path, *dir, *p;
  233. if ((path = Strdup(getenv("PATH"))) == NULL)
  234. errx("%p: getenv PATH failed\n");
  235. dir = path;
  236. while ((p = strchr(dir,':')) != NULL) {
  237. *p = '\0';
  238. if (strlen(dir) > 0 && (abspath = _check_path(dir,cwd,argv0)) != NULL) {
  239. Free((void **) &path);
  240. goto done;
  241. }
  242. dir = ++p;
  243. }
  244. if (strlen(dir) > 0)
  245. abspath = _check_path(dir,cwd,argv0);
  246. Free((void **) &path);
  247. }
  248. done:
  249. return abspath;
  250. }
  251. /*
  252. * Return a value for the max login name length (username buffer length)
  253. */
  254. static int login_name_max_len (void)
  255. {
  256. static int maxnamelen = -1;
  257. if (maxnamelen < 0) {
  258. #ifdef _SC_LOGIN_NAME_MAX
  259. errno = 0;
  260. if ((maxnamelen = sysconf (_SC_LOGIN_NAME_MAX)) <= 0) {
  261. err ("%p: sysconf(LOGIN_NAME_MAX): %m\n");
  262. maxnamelen = DEFAULT_MAX_USERNAME_LENGTH;
  263. }
  264. #else
  265. maxnamelen = DEFAULT_MAX_USERNAME_LENGTH;
  266. #endif
  267. }
  268. return (maxnamelen);
  269. }
  270. static void copy_username (char *dst, const char *src)
  271. {
  272. int maxlen = login_name_max_len ();
  273. if (strlen (src) > maxlen)
  274. errx ("%p: Fatal: username '%s' exceeds max username length (%d)\n",
  275. src, maxlen);
  276. strcpy (dst, src);
  277. }
  278. /*
  279. * Set defaults for various options.
  280. * opt (IN/OUT) option struct
  281. */
  282. void opt_default(opt_t * opt, char *argv0)
  283. {
  284. struct passwd *pw;
  285. opt->progname = xbasename(argv0);
  286. opt->luser = Malloc (login_name_max_len () + 1);
  287. opt->ruser = Malloc (login_name_max_len () + 1);
  288. opt->reverse_copy = false;
  289. if (!strcmp(opt->progname, "pdsh") || !strcmp(opt->progname, "dsh"))
  290. personality = DSH;
  291. else if (!strcmp(opt->progname, "pdcp")
  292. || !strcmp(opt->progname, "dcp")
  293. || !strcmp(opt->progname, "pcp") )
  294. personality = PCP;
  295. else if (!strcmp(opt->progname, "rpdcp")) {
  296. personality = PCP;
  297. opt->reverse_copy = true;
  298. } else
  299. errx("%p: program must be named pdsh/dsh/pdcp/dcp/pcp/rpdcp\n");
  300. if (pdsh_options == NULL)
  301. _init_pdsh_options();
  302. if ((pw = getpwuid(getuid())) != NULL) {
  303. copy_username (opt->luser, pw->pw_name);
  304. copy_username (opt->ruser, pw->pw_name);
  305. opt->luid = pw->pw_uid;
  306. } else
  307. errx("%p: who are you?\n");
  308. opt->info_only = false;
  309. opt->test_range_expansion = false;
  310. opt->wcoll = NULL;
  311. opt->connect_timeout = CONNECT_TIMEOUT;
  312. opt->command_timeout = 0;
  313. opt->fanout = DFLT_FANOUT;
  314. opt->sigint_terminates = false;
  315. opt->infile_names = NULL;
  316. opt->altnames = false;
  317. opt->debug = false;
  318. opt->labels = true;
  319. opt->rcmd_name = NULL;
  320. opt->misc_modules = NULL;
  321. /*
  322. * Resolve hostnames by default
  323. */
  324. opt->resolve_hosts = true;
  325. /*
  326. * Do not kill all tasks on single failure by default
  327. */
  328. opt->kill_on_fail = false;
  329. /* DSH specific */
  330. opt->dshpath = NULL;
  331. opt->getstat = NULL;
  332. opt->ret_remote_rc = false;
  333. opt->cmd = NULL;
  334. opt->stdin_unavailable = false;
  335. #if HAVE_MAGIC_RSHELL_CLEANUP
  336. opt->separate_stderr = false; /* save a socket per connection on aix */
  337. #else
  338. opt->separate_stderr = true;
  339. #endif
  340. opt->local_program_path = _find_path(argv0);
  341. /*
  342. * By default assume remote path to pdsh/pdcp is the same
  343. * as local path: (overridden with pdpc -e or PDSH_REMOTE_PDCP_PATH).
  344. */
  345. opt->remote_program_path = Strdup(opt->local_program_path);
  346. /* PCP specific */
  347. opt->outfile_name = NULL;
  348. opt->recursive = false;
  349. opt->preserve = false;
  350. opt->pcp_server = false;
  351. opt->target_is_directory = false;
  352. opt->pcp_client = false;
  353. opt->pcp_client_host = NULL;
  354. return;
  355. }
  356. static int string_to_int (const char *val, int *p2int)
  357. {
  358. char *p;
  359. long n;
  360. errno = 0;
  361. n = strtoul (val, &p, 10);
  362. if (errno || (*p != '\0'))
  363. return (-1);
  364. *p2int = (int) n;
  365. return (0);
  366. }
  367. /*
  368. * Override default options with environment variables.
  369. * opt (IN/OUT) option struct
  370. */
  371. void opt_env(opt_t * opt)
  372. {
  373. char *rhs;
  374. if ((rhs = getenv("FANOUT")) != NULL)
  375. if (string_to_int (rhs, &opt->fanout) < 0)
  376. errx ("%p: Invalid environment variable FANOUT=%s\n", rhs);
  377. if ((rhs = getenv("PDSH_RCMD_TYPE")) != NULL)
  378. opt->rcmd_name = Strdup(rhs);
  379. if ((rhs = getenv("PDSH_MISC_MODULES")) != NULL)
  380. opt->misc_modules = Strdup(rhs);
  381. if ((rhs = getenv("DSHPATH")) != NULL) {
  382. struct passwd *pw = getpwnam(opt->luser);
  383. char *shell = "sh";
  384. if (pw && *pw->pw_shell)
  385. shell = xbasename(pw->pw_shell);
  386. /* c shell syntax */
  387. if (!strcmp(shell, "csh") || !strcmp(shell, "tcsh")) {
  388. opt->dshpath = Strdup("setenv PATH ");
  389. xstrcat(&opt->dshpath, rhs);
  390. xstrcat(&opt->dshpath, ";");
  391. } else { /* bourne shell syntax */
  392. opt->dshpath = Strdup("PATH=");
  393. xstrcat(&opt->dshpath, rhs);
  394. xstrcat(&opt->dshpath, ";");
  395. }
  396. }
  397. if (pdsh_personality() == PCP) {
  398. if ((rhs = getenv ("PDSH_REMOTE_PDCP_PATH")) != NULL) {
  399. Free ((void **) &opt->remote_program_path);
  400. opt->remote_program_path = Strdup (rhs);
  401. }
  402. }
  403. }
  404. /*
  405. * Process any options that need to be handled early, i.e.
  406. * before modules are loaded.
  407. */
  408. void opt_args_early (opt_t * opt, int argc, char *argv[])
  409. {
  410. int c;
  411. extern int optind;
  412. extern char *optarg;
  413. extern int opterr;
  414. /*
  415. * Disable error reporting from getopt during early processing,
  416. * since we won't have access to all the options provided by
  417. * dlopened modules.
  418. */
  419. opterr = 0;
  420. #ifdef __linux
  421. /* Tell glibc getopt to stop eating after the first non-option arg */
  422. putenv("POSIXLY_CORRECT=1");
  423. #endif
  424. while ((c = getopt(argc, argv, pdsh_options)) != EOF) {
  425. switch (c) {
  426. case 'M':
  427. if (opt->misc_modules)
  428. Free ((void **) &opt->misc_modules);
  429. opt->misc_modules = Strdup (optarg);
  430. break;
  431. }
  432. }
  433. }
  434. static void wcoll_append_excluded (opt_t *opt, char *exclude_args)
  435. {
  436. List l = list_split (",", exclude_args);
  437. ListIterator i = list_iterator_create (l);
  438. char *s;
  439. while ((s = list_next (i))) {
  440. char *p = NULL;
  441. xstrcatchar (&p, '-');
  442. xstrcat (&p, s);
  443. wcoll_args_process (opt, p);
  444. Free ((void **) &p);
  445. }
  446. list_iterator_destroy (i);
  447. list_destroy (l);
  448. }
  449. /*
  450. * Override default/environment options with command line arguments.
  451. * opt (IN/OUT) option struct
  452. * argc (IN) arg count passed in from main
  453. * argv (IN) arg vector passed in from main
  454. */
  455. void opt_args(opt_t * opt, int argc, char *argv[])
  456. {
  457. int c;
  458. extern int optind;
  459. extern char *optarg;
  460. extern int opterr;
  461. /*
  462. * Reset optind after opt_args_early.
  463. */
  464. optind = 1;
  465. /*
  466. * Reinstate getopt error reporting
  467. */
  468. opterr = 1;
  469. while ((c = getopt(argc, argv, pdsh_options)) != EOF) {
  470. switch (c) {
  471. /*
  472. * The following options were handled in opt_args_early() :
  473. */
  474. case 'M':
  475. break;
  476. /* Continue processing regular options...
  477. */
  478. case 'N':
  479. opt->labels = false;
  480. break;
  481. case 'L':
  482. mod_list_module_info();
  483. exit(0);
  484. break;
  485. case 'R':
  486. opt->rcmd_name = Strdup(optarg);
  487. break;
  488. case 'S': /* get remote command status */
  489. opt->ret_remote_rc = true;
  490. break;
  491. case 'd': /* debug */
  492. opt->debug = true;
  493. break;
  494. case 'f': /* fanout */
  495. if (string_to_int (optarg, &opt->fanout) < 0)
  496. errx ("%p: Invalid fanout `%s' passed to -f.\n", optarg);
  497. break;
  498. case 'w': /* target node list */
  499. if (strcmp(optarg, "-") == 0)
  500. wcoll_args_process (opt, "^-");
  501. else
  502. wcoll_args_process (opt, optarg);
  503. break;
  504. case 'x': /* exclude node list */
  505. wcoll_append_excluded (opt, optarg);
  506. break;
  507. case 'q': /* display fanout and wcoll then quit */
  508. opt->info_only = true;
  509. break;
  510. /* -s option only useful on AIX */
  511. #if HAVE_MAGIC_RSHELL_CLEANUP
  512. case 's': /* split stderr and stdout */
  513. opt->separate_stderr = true;
  514. break;
  515. #endif
  516. case 't': /* set connect timeout */
  517. opt->connect_timeout = atoi(optarg);
  518. break;
  519. case 'u': /* set command timeout */
  520. opt->command_timeout = atoi(optarg);
  521. break;
  522. case 'b': /* "batch" */
  523. opt->sigint_terminates = true;
  524. break;
  525. case 'l': /* specify remote username for rshd */
  526. copy_username (opt->ruser, optarg);
  527. break;
  528. case 'r': /* rcp: copy recursively */
  529. if (pdsh_personality() == PCP)
  530. opt->recursive = true;
  531. else
  532. goto test_module_option;
  533. break;
  534. case 'p': /* rcp: preserve permissions */
  535. if (pdsh_personality() == PCP)
  536. opt->preserve = true;
  537. else
  538. goto test_module_option;
  539. break;
  540. case 'e':
  541. if (pdsh_personality() == PCP) {
  542. Free ((void **) &opt->remote_program_path);
  543. opt->remote_program_path = Strdup(optarg);
  544. }
  545. else
  546. goto test_module_option;
  547. break;
  548. case 'V': /* show version */
  549. _show_version();
  550. break;
  551. case 'T': /* execute testcase */
  552. testcase(atoi(optarg));
  553. break;
  554. case 'Q': /* info only, expand host ranges */
  555. opt->info_only = true;
  556. opt->test_range_expansion = true;
  557. break;
  558. case 'h': /* display usage message */
  559. _usage(opt);
  560. break;
  561. case 'K': /* don't strip host domain in output */
  562. err_no_strip_domain ();
  563. break;
  564. case 'y':
  565. if (pdsh_personality() == PCP)
  566. opt->target_is_directory = true; /* is target a dir? */
  567. else
  568. goto test_module_option;
  569. break;
  570. case 'z':
  571. if (pdsh_personality() == PCP)
  572. opt->pcp_server = true; /* run PDCP server */
  573. else
  574. goto test_module_option;
  575. break;
  576. case 'Z':
  577. if (pdsh_personality() == PCP)
  578. opt->pcp_client = true; /* run PDCP client */
  579. else
  580. goto test_module_option;
  581. break;
  582. default: test_module_option:
  583. if (mod_process_opt(opt, c, optarg) < 0)
  584. _usage(opt);
  585. }
  586. }
  587. /*
  588. * Load default module for all hosts (unless overridden)
  589. */
  590. if (opt->rcmd_name == NULL)
  591. opt->rcmd_name = Strdup(rcmd_get_default_module ());
  592. if (opt->rcmd_name != NULL)
  593. if (rcmd_register_default_rcmd(opt->rcmd_name) < 0)
  594. exit(1);
  595. /*
  596. * Save beginning of remote argv in case something needs
  597. * to view the unadulterated version (after shell quoting
  598. * applied, etc.)
  599. */
  600. remote_argc = argc - optind;
  601. remote_argv = argv + optind;
  602. /* DSH: build command */
  603. if (personality == DSH) {
  604. for (; optind < argc; optind++) {
  605. if (opt->cmd != NULL)
  606. xstrcat(&opt->cmd, " ");
  607. xstrcat(&opt->cmd, argv[optind]);
  608. }
  609. /* PCP: build file list */
  610. } else {
  611. if (!opt->infile_names)
  612. opt->infile_names = list_create(NULL);
  613. for (; optind < argc - 1; optind++)
  614. list_append(opt->infile_names, argv[optind]);
  615. if (optind < argc) {
  616. /* If this is the initial pdcp call, the last argument is
  617. * the output file/dir. If this is the pcp_client, the
  618. * last argument is the hostname used for connecting.
  619. */
  620. if (opt->pcp_client)
  621. xstrcat(&opt->pcp_client_host, argv[optind]);
  622. else
  623. xstrcat(&opt->outfile_name, argv[optind]);
  624. }
  625. }
  626. /* ignore wcoll filtering when running pcp server */
  627. if (opt->pcp_server)
  628. return;
  629. /*
  630. * Give modules a chance to fill in wcoll if it hasn't been already:
  631. */
  632. if (mod_read_wcoll (opt) < 0)
  633. exit (1);
  634. /*
  635. * If wcoll is still empty, try WCOLL env variable:
  636. */
  637. if (opt->wcoll == NULL) {
  638. char *val = getenv ("WCOLL");
  639. if (val != NULL)
  640. opt->wcoll = read_wcoll (val, NULL);
  641. }
  642. if (opt->wcoll) {
  643. /*
  644. * Now apply wcoll filtering
  645. */
  646. if (exclude_list) {
  647. wcoll_apply_excluded (opt, exclude_list);
  648. list_destroy (exclude_list);
  649. }
  650. if (regex_list) {
  651. wcoll_apply_regex (opt, regex_list);
  652. list_destroy (regex_list);
  653. }
  654. /*
  655. * Finally, re-expand wcoll to allow two sets of brackets.
  656. * (For historical compatibility)
  657. */
  658. wcoll_expand (opt);
  659. }
  660. }
  661. static void wcoll_expand (opt_t *opt)
  662. {
  663. hostlist_t hl = opt->wcoll;
  664. const char *hosts;
  665. /*
  666. * Create new hostlist for wcoll
  667. */
  668. opt->wcoll = hostlist_create ("");
  669. while ((hosts = hostlist_shift (hl))) {
  670. hostlist_push (opt->wcoll, hosts);
  671. free (hosts);
  672. }
  673. hostlist_destroy (hl);
  674. }
  675. /*
  676. * Check if infile_names are legit.
  677. */
  678. static int
  679. _infile_names_check(opt_t * opt)
  680. {
  681. bool verified = true;
  682. ListIterator i;
  683. char *name;
  684. i = list_iterator_create(opt->infile_names);
  685. while ((name = list_next(i))) {
  686. struct stat sb;
  687. if (stat(name, &sb) < 0) {
  688. err("%p: can't stat %s\n", name);
  689. verified = false;
  690. continue;
  691. }
  692. if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) {
  693. err("%p: not a regular file or directory: %s\n", name);
  694. verified = false;
  695. break;
  696. }
  697. if (S_ISDIR(sb.st_mode) && !opt->recursive) {
  698. err("%p: use -r to copy directories: %s\n", name);
  699. verified = false;
  700. break;
  701. }
  702. }
  703. list_iterator_destroy(i);
  704. return verified;
  705. }
  706. /*
  707. * Trap a few option inconsitencies.
  708. * opt (IN) option struct
  709. */
  710. bool opt_verify(opt_t * opt)
  711. {
  712. bool verified = true;
  713. /*
  714. * Call all post option processing functions for modules
  715. */
  716. if (mod_postop(opt) > 0)
  717. verified = false;
  718. /* can't prompt for command if stdin was used for wcoll */
  719. if (personality == DSH && opt->stdin_unavailable && !opt->cmd) {
  720. _usage(opt);
  721. verified = false;
  722. }
  723. if (!opt->pcp_server && !opt->pcp_client) {
  724. /* wcoll is required */
  725. if (opt->wcoll == NULL || hostlist_count(opt->wcoll) == 0) {
  726. err("%p: no remote hosts specified\n");
  727. verified = false;
  728. }
  729. /* connect and command timeouts must be reasonable */
  730. if (opt->connect_timeout < 0) {
  731. err("%p: connect timeout must be >= 0\n");
  732. verified = false;
  733. }
  734. if (opt->command_timeout < 0) {
  735. err("%p: command timeout must be >= 0\n");
  736. verified = false;
  737. }
  738. }
  739. /* PCP: must have source and destination filename(s) */
  740. if (personality == PCP && !opt->pcp_server && !opt->pcp_client) {
  741. if (!opt->outfile_name || list_is_empty(opt->infile_names)) {
  742. err("%p: pcp requires source and dest filenames\n");
  743. verified = false;
  744. }
  745. if (opt->target_is_directory) {
  746. err("%p: target is directory can only be specified with pcp server\n");
  747. verified = false;
  748. }
  749. /* If reverse copy, the infiles need not exist locally */
  750. if (!opt->reverse_copy) {
  751. if (!_infile_names_check(opt))
  752. verified = false;
  753. }
  754. /* If reverse copy, the destination must be a directory */
  755. if (opt->reverse_copy && opt->outfile_name) {
  756. struct stat statbuf;
  757. if (stat(opt->outfile_name, &statbuf) < 0) {
  758. err("%p: can't stat %s\n", opt->outfile_name);
  759. verified = false;
  760. }
  761. if (!S_ISDIR(statbuf.st_mode)) {
  762. err("%p: reverse copy dest must be a directory\n");
  763. verified = false;
  764. }
  765. }
  766. }
  767. /* PCP: server and client sanity check */
  768. if (personality == PCP && opt->pcp_server && opt->pcp_client) {
  769. err("%p: pcp server and pcp client cannot both be set\n");
  770. verified = false;
  771. }
  772. /* PCP: verify options when -z option specified */
  773. if (personality == PCP && opt->pcp_server) {
  774. if (opt->infile_names && !list_is_empty(opt->infile_names)) {
  775. err("%p: do not list source files with pcp server\n");
  776. verified = false;
  777. }
  778. if (!opt->outfile_name) {
  779. err("%p: output file must be specified with pcp server\n");
  780. verified = false;
  781. }
  782. if (opt->pcp_client_host) {
  783. err("%p: pcp client host should not be specified with pcp server\n");
  784. verified = false;
  785. }
  786. if (opt->reverse_copy) {
  787. err("%p: reverse copy cannot be specified with pcp server\n");
  788. verified = false;
  789. }
  790. }
  791. /* PCP: verify options when -Z option specified */
  792. if (personality == PCP && opt->pcp_client) {
  793. opt->reverse_copy = false;
  794. if (!opt->infile_names || list_is_empty(opt->infile_names)) {
  795. err("%p: list source files required for pcp client\n");
  796. verified = false;
  797. }
  798. if (opt->outfile_name) {
  799. err("%p: output file should not be specified with pcp client\n");
  800. verified = false;
  801. }
  802. if (!opt->pcp_client_host) {
  803. err("%p: pcp client host must be specified with pcp client\n");
  804. verified = false;
  805. }
  806. /* If reverse copy the infiles should exist locally */
  807. if (opt->infile_names && !_infile_names_check(opt))
  808. verified = false;
  809. }
  810. return verified;
  811. }
  812. /* printf args */
  813. #define BOOLSTR(x) ((x) ? "Yes" : "No")
  814. #define STRORNULL(x) ((x) ? (x) : "none")
  815. #define RCMDSTR(x) (x == RCMD_BSD ? "RCMD_BSD" : \
  816. (x == RCMD_K4 ? "RCMD_K4" : \
  817. (x == RCMD_QSHELL ? "RCMD_QSHELL" : \
  818. (x == RCMD_SSH ? "RCMD_SSH" : "<Unknown>"))))
  819. #define ALLOCSTR(x) (x == ALLOC_BLOCK ? "ALLOC_BLOCK" : \
  820. (x == ALLOC_CYCLIC ? "ALLOC_CYCLIC" : "<Unknown>"))
  821. /*
  822. * List the current options.
  823. * opt (IN) option list
  824. */
  825. void opt_list(opt_t * opt)
  826. {
  827. char wcoll_str[1024];
  828. int n;
  829. if (personality == DSH) {
  830. out("-- DSH-specific options --\n");
  831. out("Separate stderr/stdout %s\n",
  832. BOOLSTR(opt->separate_stderr));
  833. out("Path prepended to cmd %s\n", STRORNULL(opt->dshpath));
  834. out("Appended to cmd %s\n", STRORNULL(opt->getstat));
  835. out("Command: %s\n", STRORNULL(opt->cmd));
  836. } else {
  837. char infiles [4096];
  838. out("-- PCP-specific options --\n");
  839. if (list_join (infiles, sizeof (infiles), ", ", opt->infile_names))
  840. out("Infile(s) %s\n", infiles);
  841. out("Outfile %s\n", STRORNULL(opt->outfile_name));
  842. out("Recursive %s\n", BOOLSTR(opt->recursive));
  843. out("Preserve mod time/mode %s\n", BOOLSTR(opt->preserve));
  844. if (opt->pcp_server) {
  845. out("pcp server %s\n", BOOLSTR(opt->pcp_server));
  846. out("target is directory %s\n", BOOLSTR(opt->target_is_directory));
  847. }
  848. }
  849. if (!opt->pcp_server) {
  850. out("Full program pathname %s\n", STRORNULL(opt->local_program_path));
  851. out("Remote program path %s\n",
  852. STRORNULL(opt->remote_program_path));
  853. out("\n-- Generic options --\n");
  854. out("Local username %s\n", opt->luser);
  855. out("Local uid %d\n", opt->luid);
  856. out("Remote username %s\n", opt->ruser);
  857. out("Rcmd type %s\n", STRORNULL(opt->rcmd_name));
  858. out("one ^C will kill pdsh %s\n", BOOLSTR(opt->sigint_terminates));
  859. out("Connect timeout (secs) %d\n", opt->connect_timeout);
  860. out("Command timeout (secs) %d\n", opt->command_timeout);
  861. out("Fanout %d\n", opt->fanout);
  862. out("Display hostname labels %s\n", BOOLSTR(opt->labels));
  863. out("Debugging %s\n", BOOLSTR(opt->debug));
  864. out("\n-- Target nodes --\n");
  865. if (opt->test_range_expansion) {
  866. n = hostlist_deranged_string(opt->wcoll, sizeof(wcoll_str),
  867. wcoll_str);
  868. } else {
  869. n = hostlist_ranged_string(opt->wcoll, sizeof(wcoll_str),
  870. wcoll_str);
  871. }
  872. if (n < 0)
  873. out("%s[truncated]\n", wcoll_str);
  874. else
  875. out("%s\n", wcoll_str);
  876. }
  877. }
  878. /*
  879. * Free heap-allocated memory associated with options, etc.
  880. * opt (IN/OUT) option struct
  881. */
  882. void opt_free(opt_t * opt)
  883. {
  884. if (opt->wcoll != NULL)
  885. hostlist_destroy(opt->wcoll);
  886. if (opt->cmd != NULL)
  887. Free((void **) &opt->cmd);
  888. if (opt->rcmd_name != NULL)
  889. Free((void **) &opt->rcmd_name);
  890. if (opt->misc_modules != NULL)
  891. Free((void **) &opt->misc_modules);
  892. if (pdsh_options)
  893. Free((void **) &pdsh_options);
  894. if (opt->dshpath)
  895. Free((void **) &opt->dshpath);
  896. if (opt->local_program_path)
  897. Free((void **) &opt->local_program_path);
  898. if (opt->remote_program_path)
  899. Free((void **) &opt->remote_program_path);
  900. if (opt->infile_names)
  901. list_destroy(opt->infile_names);
  902. if (opt->luser)
  903. Free((void **) &opt->luser);
  904. if (opt->ruser)
  905. Free((void **) &opt->ruser);
  906. rcmd_exit();
  907. }
  908. /*
  909. * Returns a string of comma separated module names of type `type'
  910. * Returns NULL if no modules of this type are loaded.
  911. */
  912. static int _module_list_string(char *type, char *buf, int len)
  913. {
  914. List l = NULL;
  915. int n = 0;
  916. if (mod_count(type) == 0)
  917. return (0);
  918. l = mod_get_module_names(type);
  919. n = list_join(buf, len, ",", l);
  920. list_destroy(l);
  921. return (n);
  922. }
  923. static int _module_list_uninitialized (char *type, char *buf, int len)
  924. {
  925. List l = NULL;
  926. int n = 0;
  927. if (mod_count(type) == 0)
  928. return (0);
  929. l = mod_get_uninitialized_module_names(type);
  930. n = list_join(buf, len, ",", l);
  931. list_destroy(l);
  932. return (n);
  933. }
  934. static char *_rcmd_module_list(char *buf, int maxlen)
  935. {
  936. int len, len2;
  937. char rbuf [1024];
  938. int n;
  939. n = _module_list_string ("rcmd", rbuf, sizeof (rbuf));
  940. len = snprintf(buf, maxlen, "%s", n ? rbuf : "(none)");
  941. if ((len < 0) || (len >= maxlen))
  942. goto done;
  943. if (mod_count("rcmd") > 1) {
  944. char *def = rcmd_get_default_module();
  945. len2 = snprintf ( buf+len, maxlen-len, " (default: %s)",
  946. def ? def : "none" );
  947. if (len2 < 0)
  948. len = -1;
  949. else
  950. len += len2;
  951. }
  952. done:
  953. if ((len < 0) || (len > maxlen))
  954. snprintf(buf + maxlen - 12, 12, "[truncated]");
  955. buf[maxlen - 1] = '\0';
  956. return buf;
  957. }
  958. /*
  959. * Spit out all the options and their one-line synopsis for the user,
  960. * then exit.
  961. */
  962. static void _usage(opt_t * opt)
  963. {
  964. char buf[1024];
  965. if (personality == DSH) {
  966. err(OPT_USAGE_DSH);
  967. #if HAVE_MAGIC_RSHELL_CLEANUP
  968. err(OPT_USAGE_STDERR);
  969. #endif
  970. } else if (!opt->reverse_copy) /* PCP */
  971. err(OPT_USAGE_PCP);
  972. else
  973. err(OPT_USAGE_RPCP);
  974. err(OPT_USAGE_COMMON);
  975. mod_print_all_options(18);
  976. err("available rcmd modules: %s\n", _rcmd_module_list(buf, 1024));
  977. exit(1);
  978. }
  979. static void _show_version(void)
  980. {
  981. char buf[1024];
  982. extern char *pdsh_version;
  983. int n;
  984. printf("%s\n", pdsh_version);
  985. printf("rcmd modules: %s\n", _rcmd_module_list(buf, sizeof (buf)));
  986. n = _module_list_string("misc", buf, sizeof (buf));
  987. printf("misc modules: %s", n ? buf : "(none)");
  988. if ((n = _module_list_uninitialized ("misc", buf, sizeof (buf)))) {
  989. printf (" (*conflicting: %s)\n", buf);
  990. printf ("[* To force-load a conflicting module,"
  991. " use the -M <name> option]\n");
  992. }
  993. else
  994. printf ("\n");
  995. exit(0);
  996. }
  997. /*
  998. * Take a string `hosts' possibly of form "rcmd_type:user@hostlist" and
  999. * place the hostlist part of the string in *hptr and the rcmd part
  1000. * of the string in rptr. Returns nonzero if an rcmd_type was found,
  1001. * zero otherwise (in which case rptr is not touched).
  1002. */
  1003. static int get_host_rcmd_type (char *hosts, char **rptr, char **hptr,
  1004. char **uptr)
  1005. {
  1006. char *p = hosts;
  1007. char *q;
  1008. *hptr = hosts;
  1009. p = strchr (hosts, ':');
  1010. q = strchr (hosts, '@');
  1011. if (p && q && p > q)
  1012. errx ("Host spec \"%s\" not of form [rcmd_type:][user@]hosts\n", hosts);
  1013. /*
  1014. * If we found a single ':' character, then everything
  1015. * preceeding that is the rcmd type. Otherwise, we ignore
  1016. * presence of all colons. This can be done b/c even if there
  1017. * were another colon later in the string, the string preceeding
  1018. * it can not be an rcmd type since colon is not extant in rcmd
  1019. * type names.
  1020. */
  1021. if (p && (*(p+1) != ':')) {
  1022. *rptr = *hptr;
  1023. *p++ = '\0';
  1024. *hptr = p;
  1025. }
  1026. /*
  1027. * If we found a '@' char then what precedes it is username.
  1028. */
  1029. if (q) {
  1030. *uptr = *hptr;
  1031. *q++ = '\0';
  1032. *hptr = q;
  1033. }
  1034. return (1);
  1035. }
  1036. void free_f (void *x)
  1037. {
  1038. Free (&x);
  1039. }
  1040. struct regex_info {
  1041. int exclude;
  1042. int cflags;
  1043. int eflags;
  1044. int compiled;
  1045. char * pattern;
  1046. regex_t reg;
  1047. };
  1048. void regex_info_destroy (struct regex_info *re)
  1049. {
  1050. if (re->compiled)
  1051. regfree (&re->reg);
  1052. if (re->pattern)
  1053. Free ((void **) &re->pattern);
  1054. Free ((void **) &re);
  1055. }
  1056. struct regex_info * regex_info_create (const char *r, int exclude)
  1057. {
  1058. int rc;
  1059. struct regex_info *re = Malloc (sizeof (*re));
  1060. re->pattern = Strdup (r);
  1061. re->exclude = exclude;
  1062. re->cflags = REG_EXTENDED | REG_NOSUB;
  1063. re->eflags = 0;
  1064. re->compiled = 0;
  1065. if ((rc = regcomp (&re->reg, re->pattern, re->cflags)) != 0) {
  1066. char msg [4096];
  1067. regerror (rc, &re->reg, msg, sizeof (msg));
  1068. err ("%p: Error %s pattern \"%s\": %s\n",
  1069. re->exclude ? "excluding" : "matching",
  1070. re->pattern, msg);
  1071. regex_info_destroy (re);
  1072. return (NULL);
  1073. }
  1074. re->compiled = 1;
  1075. return (re);
  1076. }
  1077. void hostlist_filter_regex (hostlist_t hl, struct regex_info *re)
  1078. {
  1079. char *host;
  1080. hostlist_iterator_t i;
  1081. i = hostlist_iterator_create (hl);
  1082. while ((host = hostlist_next (i))) {
  1083. int rc = regexec (&re->reg, host, 0, NULL, re->eflags);
  1084. if ((re->exclude && rc == 0) || (!re->exclude && rc == REG_NOMATCH))
  1085. hostlist_remove (i);
  1086. free (host);
  1087. }
  1088. hostlist_iterator_destroy (i);
  1089. }
  1090. static void list_push_hostlist (List l, hostlist_t hl)
  1091. {
  1092. size_t n = 4096;
  1093. char *s = Malloc (n);
  1094. while ((hostlist_ranged_string (hl, n-1, s) < 0) && (n*=2 < 0x7fffff)) {
  1095. Realloc ((void **) &s, n);
  1096. }
  1097. list_push (l, s);
  1098. }
  1099. static void hostlist_assign (hostlist_t *hlp, hostlist_t hl2)
  1100. {
  1101. if (*hlp == NULL)
  1102. *hlp = hostlist_create ("");
  1103. hostlist_push_list (*hlp, hl2);
  1104. }
  1105. static int wcoll_arg_process (char *arg, opt_t *opt)
  1106. {
  1107. struct regex_info *re;
  1108. int excluded = 0;
  1109. char *p = arg;
  1110. if (exclude_list == NULL)
  1111. exclude_list = list_create (free_f);
  1112. if (regex_list == NULL)
  1113. regex_list = list_create ((ListDelF) regex_info_destroy);
  1114. /*
  1115. * Check for excluded arg
  1116. */
  1117. if (*p == '-') {
  1118. excluded = 1;
  1119. p++;
  1120. }
  1121. /*
  1122. * Move past any leading whitespace
  1123. */
  1124. while (isspace (*p))
  1125. p++;
  1126. if (*p == '^') {
  1127. hostlist_t hl = read_wcoll (p+1, NULL);
  1128. if (hl == NULL)
  1129. errx ("%p: Error reading wcoll: %s: %m\n", p+1);
  1130. if (excluded)
  1131. list_push_hostlist (exclude_list, hl);
  1132. else
  1133. hostlist_assign (&opt->wcoll, hl);
  1134. hostlist_destroy (hl);
  1135. }
  1136. else if (*p == '/') {
  1137. int len;
  1138. ++p;
  1139. len = strlen (p);
  1140. if (p [len - 1] == '/')
  1141. p [len - 1] = '\0';
  1142. if ((re = regex_info_create (p, excluded)) == NULL)
  1143. errx ("%p: Fatal error\n");
  1144. list_push (regex_list, re);
  1145. }
  1146. else {
  1147. if (excluded) {
  1148. list_push (exclude_list, Strdup (p));
  1149. }
  1150. else {
  1151. char *rcmd_type = NULL;
  1152. char *hosts, *user = NULL;
  1153. if (!opt->wcoll)
  1154. opt->wcoll = hostlist_create ("");
  1155. get_host_rcmd_type (p, &rcmd_type, &hosts, &user);
  1156. hostlist_push (opt->wcoll, hosts);
  1157. if (rcmd_type || user) {
  1158. if (rcmd_register_defaults (hosts, rcmd_type, user) < 0)
  1159. errx ("%p: Failed to register rcmd \"%s\" for \"%s\"\n",
  1160. rcmd_type, hosts);
  1161. }
  1162. }
  1163. }
  1164. return 0;
  1165. }
  1166. static int wcoll_args_process (opt_t *opt, char * args)
  1167. {
  1168. int rc;
  1169. List l = list_split (",", args);
  1170. rc = list_for_each (l, (ListForF) wcoll_arg_process, opt);
  1171. list_destroy (l);
  1172. return (rc);
  1173. }
  1174. static void wcoll_apply_regex (opt_t *opt, List regexs)
  1175. {
  1176. struct regex_info *re;
  1177. ListIterator i;
  1178. if (!opt->wcoll || !regexs)
  1179. return;
  1180. /*
  1181. * filter any supplied regular expression args
  1182. */
  1183. i = list_iterator_create (regexs);
  1184. while ((re = list_next (i)))
  1185. hostlist_filter_regex (opt->wcoll, re);
  1186. list_iterator_destroy (i);
  1187. }
  1188. static void wcoll_apply_excluded (opt_t *opt, List excludes)
  1189. {
  1190. ListIterator i;
  1191. char *arg;
  1192. if (!opt->wcoll || !excludes)
  1193. return;
  1194. /*
  1195. * filter explicitly excluded hosts:
  1196. */
  1197. i = list_iterator_create (excludes);
  1198. while ((arg = list_next (i)))
  1199. hostlist_delete (opt->wcoll, arg);
  1200. list_iterator_destroy (i);
  1201. }
  1202. /*
  1203. * vi:tabstop=4 shiftwidth=4 expandtab
  1204. */