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