pdsh-2.36/0000775€^–Á €^–Á 0000000000015131211226020506 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/Makefile.am0000664€^–Á €^–Á 0000000303515131211226022543 0ustar arif.ali@canonical.comarif.ali@canonical.com##**************************************************************************** ## $Id$ ##**************************************************************************** ## Process this file with automake to produce Makefile.in. ##**************************************************************************** include $(top_srcdir)/config/Make-inc.mk ACLOCAL_AMFLAGS = -I config AUTOMAKE_OPTIONS = foreign dist-bzip2 SUBDIRS = src tests doc scripts config maintainer-clean-local: -(cd $(top_srcdir) && rm -rf autom4te.cache) -find . -name "Makefile.in" -exec rm {} \; distclean-local:: -rm -fr autom4te*.cache autoscan.* -rm -rf $(PACKAGE)-* uninstall-local: @rmdir $(DESTDIR)$(pkglibdir); \ if [ -d $(DESTDIR)$(pkglibdir) ]; then \ echo >&2;\ echo " Warning: The pdsh module directory $(DESTDIR)$(pkglibdir)" >&2;\ echo " is not empty after 'make uninstall.' There may be modules" >&2;\ echo " from a previous invocation of 'make install' that need" >&2;\ echo " to be removed for a clean uninstall." >&2;\ echo >&2;\ fi MAINTAINERCLEANFILES = \ aclocal.m4 \ config.h.in \ configure \ stamp-h \ stamp-h.in EXTRA_DIST = \ DISCLAIMER.UC \ DISCLAIMER.LLNS \ pdsh.spec \ README.KRB4 \ README.modules \ bootstrap # coverage CODE_COVERAGE_LCOV_RMOPTS = \ --ignore-errors unused CODE_COVERAGE_IGNORE_PATTERN = \ "/tests/*" \ "/pdsh/cbuf.*" \ "/common/hostlist.*" \ "/usr/*" CODE_COVERAGE_LCOV_OPTIONS = @CODE_COVERAGE_RULES@ pdsh-2.36/AUTHORS0000664€^–Á €^–Á 0000000026315131211226021557 0ustar arif.ali@canonical.comarif.ali@canonical.comOriginal Author and Maintainer -------- ------ --- ---------- Jim Garlick Contributors ------------ Al Chu Mark Grondona pdsh-2.36/pdsh.spec0000664€^–Á €^–Á 0000003056715131211226022333 0ustar arif.ali@canonical.comarif.ali@canonical.comName: pdsh Version: 2.34 Release: 1 Summary: Parallel remote shell program License: GPL Url: https://github.com/chaos/pdsh Group: System Environment/Base Source: pdsh-%{version}-%{release}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: pdsh-rcmd # # Enabling and disabling pdsh options # defaults: # enabled: readline, rsh, ssh, dshgroup, netgroups, exec # disabled: rms, mrsh, xcpu, genders, machines, nodeupdown # To build the various module subpackages, pass --with on # the rpmbuild command line (if your rpm is a recent enough version) # # Similarly, to disable various pdsh options pass --without on # the rpmbuild command line. # # This specfile also supports passing the --with and --without through # the environment variables PDSH_WITH_OPTIONS and PDSH_WITHOUT_OPTIONS. # e.g. PDSH_WITH_OPTIONS="genders" rpmbuild .... # # # Definition of default packages to build on various platforms: # %define _defaults ssh exec readline # LLNL system defaults %if 0%{?chaos} %define _default_with %{_defaults} mrsh nodeupdown genders slurm %else # All other defaults %define _default_with %{_defaults} dshgroups netgroup machines %endif # # Environment variables can be used to override defaults above: # %define _env_without ${PDSH_WITHOUT_OPTIONS} %define _env_with ${PDSH_WITH_OPTIONS} # Shortcut for % global expansion %define dstr "%%%%"global # Check with/out env variables for any options %define env() echo %_env_%{1}|grep -qw %%1 && echo %dstr _%{1}_%%1 --%{1}-%%1 # Check defaults %define def() echo %_default_with | grep -qw %%1 || w=out; echo %dstr _with${w}_%%1 --with${w}-%%1 # Check env variables first. If they are not set use defaults. %{expand: %%define pdsh_with() %%((%{env with})||(%{env without})||(%{def}))} # Only check environment and defaults if a --with or --without wasn't # used from the rpmbuild command line. # %define pdsh_opt() %%{!?_with_%1: %%{!?_without_%1: %%{expand: %%pdsh_with %1}}} # # Rcmd modules: # %{expand: %pdsh_opt exec} %{expand: %pdsh_opt ssh} %{expand: %pdsh_opt rsh} %{expand: %pdsh_opt mrsh} %{expand: %pdsh_opt xcpu} # # Misc modules: # %{expand: %pdsh_opt netgroup} %{expand: %pdsh_opt dshgroups} %{expand: %pdsh_opt genders} %{expand: %pdsh_opt nodeupdown} %{expand: %pdsh_opt machines} %{expand: %pdsh_opt slurm} %{expand: %pdsh_opt torque} %{expand: %pdsh_opt rms} # # Other options: # %{expand: %pdsh_opt readline} %{expand: %pdsh_opt debug} # # If "--with debug" is set compile with --enable-debug # and try not to strip binaries. # # (See /usr/share/doc/rpm-*/conditionalbuilds) # %if %{?_with_debug:1}%{!?_with_debug:0} %define _enable_debug --enable-debug %endif %{?_with_mrsh:BuildRequires: munge-devel} %{?_with_readline:BuildRequires: readline-devel} %{?_with_readline:BuildRequires: ncurses-devel} %{?_with_nodeupdown:BuildRequires: whatsup} %{?_with_genders:BuildRequires: genders > 1.0} %{?_with_slurm:BuildRequires: slurm-devel} %{?_with_torque:BuildRequires: torque-devel} ############################################################################## # Pdsh description %description Pdsh is a multithreaded remote shell client which executes commands on multiple remote hosts in parallel. Pdsh can use several different remote shell services, including standard "rsh", Kerberos IV, and ssh. ############################################################################## # # Module packages: # %package rcmd-rsh Summary: Provides bsd rcmd capability to pdsh Group: System Environment/Base Provides: pdsh-rcmd %description rcmd-rsh Pdsh module for bsd rcmd functionality. Note: This module requires that the pdsh binary be installed setuid root. %package rcmd-ssh Summary: Provides ssh rcmd capability to pdsh Group: System Environment/Base Provides: pdsh-rcmd %description rcmd-ssh Pdsh module for ssh rcmd functionality. %package rcmd-mrsh Summary: Provides mrsh rcmd capability to pdsh Group: System Environment/Base Provides: pdsh-rcmd %description rcmd-mrsh Pdsh module for mrsh rcmd functionality. %package rcmd-xcpu Summary: Provides xcpu rcmd capability to pdsh Group: System Environment/Base Provides: pdsh-xcpu %description rcmd-xcpu Pdsh module for xcpu rcmd functionality. %package rcmd-exec Summary: Provides arbitrary command execution "rcmd" method to pdsh Group: System Environment/Base Provides: pdsh-rcmd %description rcmd-exec Pdsh module for generic exec capability. This module allows execution of an arbitrary command line for each target host in place of a more specific rcmd connect method (i.e. ssh, rsh, etc.). The command executed for each host is built from the pdsh "remote" command line: The first remote argument is the command to execute, followed by any arguments including "%h", "%u", and "%n", which are the remote target, username, and rank respectively. %package mod-genders Summary: Provides libgenders support for pdsh Group: System Environment/Base Requires: genders >= 1.1 Conflicts: pdsh-mod-machines %description mod-genders Pdsh module for libgenders functionality. %package mod-nodeupdown Summary: Provides libnodeupdown support for pdsh Group: System Environment/Base Requires: whatsup %description mod-nodeupdown Pdsh module providing -v functionality using libnodeupdown. %package mod-machines Summary: Pdsh module for gathering list of target nodes from a machines file Group: System Environment/Base %description mod-machines Pdsh module for gathering list of all target nodes from a machines file. %package mod-dshgroup Summary: Provides dsh-style group file support for pdsh Group: System Environment/Base %description mod-dshgroup Pdsh module providing dsh (Dancer's shell) style "group" file support. Provides -g groupname and -X groupname options to pdsh. %package mod-netgroup Summary: Provides netgroup support for pdsh Group: System Environment/Base %description mod-netgroup Pdsh module providing support for targeting hosts based on netgroup. Provides -g groupname and -X groupname options to pdsh. %package mod-slurm Summary: Provides support for running pdsh under SLURM allocations Group: System Environment/Base Requires: slurm %description mod-slurm Pdsh module providing support for gathering the list of target nodes from an allocated SLURM job. %package mod-torque Summary: Provides support for running pdsh under Torque allocations Group: System Environment/Base Requires: torque %description mod-torque Pdsh module providing support for gathering the list of target nodes from an allocated Torque job. ############################################################################## %prep %setup ############################################################################## %build %configure --program-prefix=%{?_program_prefix:%{_program_prefix}} \ %{?_enable_debug} \ %{?_with_rsh} \ %{?_without_rsh} \ %{?_with_ssh} \ %{?_without_ssh} \ %{?_with_exec} \ %{?_without_exec} \ %{?_with_readline} \ %{?_without_readline} \ %{?_with_machines} \ %{?_without_machines} \ %{?_with_genders} \ %{?_without_genders} \ %{?_with_nodeupdown} \ %{?_without_nodeupdown} \ %{?_with_mrsh} \ %{?_without_mrsh} \ %{?_with_xcpu} \ %{?_without_xcpu} \ %{?_with_slurm} \ %{?_without_slurm} \ %{?_with_torque} \ %{?_without_torque} \ %{?_with_dshgroups} \ %{?_without_dshgroups} \ %{?_with_netgroup} \ %{?_without_netgroup} make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" # Now run tests make check ############################################################################## %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT DESTDIR="$RPM_BUILD_ROOT" make install # # Remove all module .a's as they are not needed on any known RPM platform. rm $RPM_BUILD_ROOT/%{_libdir}/pdsh/*.a rm $RPM_BUILD_ROOT/%{_libdir}/pdsh/*.la ############################################################################## %clean rm -rf "$RPM_BUILD_ROOT" ############################################################################## %files %defattr(-,root,root) %doc COPYING README NEWS DISCLAIMER.LLNS DISCLAIMER.UC %doc README.KRB4 README.modules %{_bindir}/pdsh %{_bindir}/pdcp %{_bindir}/rpdcp %{_bindir}/dshbak %dir %{_libdir}/pdsh %{_mandir}/man1/* ############################################################################## %if %{?_with_exec:1}%{!?_with_exec:0} %files rcmd-exec %defattr(-,root,root) %{_libdir}/pdsh/execcmd.* %endif ############################################################################## %if %{?_with_rsh:1}%{!?_with_rsh:0} %files rcmd-rsh %defattr(-,root,root) %{_libdir}/pdsh/xrcmd.* %endif ############################################################################## %if %{?_with_ssh:1}%{!?_with_ssh:0} %files rcmd-ssh %defattr(-,root,root) %{_libdir}/pdsh/sshcmd.* %endif ############################################################################## %if %{?_with_mrsh:1}%{!?_with_mrsh:0} %files rcmd-mrsh %defattr(-,root,root) %{_libdir}/pdsh/mcmd.* %endif ############################################################################## %if %{?_with_xcpu:1}%{!?_with_xcpu:0} %files rcmd-xcpu %defattr(-,root,root) %{_libdir}/pdsh/xcpucmd.* %endif ############################################################################## %if %{?_with_genders:1}%{!?_with_genders:0} %files mod-genders %defattr(-,root,root) %{_libdir}/pdsh/genders.* %endif ############################################################################## %if %{?_with_nodeupdown:1}%{!?_with_nodeupdown:0} %files mod-nodeupdown %defattr(-,root,root) %{_libdir}/pdsh/nodeupdown.* %endif ############################################################################## %if %{?_with_rms:1}%{!?_with_rms:0} %files mod-rms %defattr(-,root,root) %{_libdir}/pdsh/rms.* %endif ############################################################################## %if %{?_with_machines:1}%{!?_with_machines:0} %files mod-machines %defattr(-,root,root) %{_libdir}/pdsh/machines.* %endif ############################################################################## %if %{?_with_dshgroups:1}%{!?_with_dshgroups:0} %files mod-dshgroup %defattr(-,root,root) %{_libdir}/pdsh/dshgroup.* %endif ############################################################################## %if %{?_with_netgroup:1}%{!?_with_netgroup:0} %files mod-netgroup %defattr(-,root,root) %{_libdir}/pdsh/netgroup.* %endif ############################################################################## %if %{?_with_slurm:1}%{!?_with_slurm:0} %files mod-slurm %defattr(-,root,root) %{_libdir}/pdsh/slurm.* %endif ############################################################################## %if %{?_with_torque:1}%{!?_with_torque:0} %files mod-torque %defattr(-,root,root) %{_libdir}/pdsh/torque.* %endif ############################################################################## %changelog * Tue Jul 20 2016 Albert Chu - update URL to point to github URL - update Source to not point to sourceforge repo * Fri Jun 22 2007 Mark Grondona - reworked specfile conditionals to allow easy change of defaults * Mon Jun 4 2007 Mark Grondona - added rcmd-exec subpackage. * Thu Feb 22 2007 Daniel J Blueman - added 'rpmbuild ... --without pam' option passthrough - generalised 'elan3' to 'Quadrics QsNet' * Thu Dec 7 2006 Mark Grondona - Package new rpdcp command. * Fri Feb 23 2006 Ben Woodard - changed source location to point to main site not mirror. * Thu Feb 22 2006 Ben Woodard - removed change of attributes of pdsh and pcp in files section - removed .a files from packages. * Wed Feb 22 2006 Ben Woodard - add parameters to make - replace etc with _sysconfdir in most places - remove post section with unexplained removing of cached man pages. - removed dots at end of all summaries. * Wed Feb 16 2006 Ben Woodard - add in rpmlint fixes - change buildroot * Wed Feb 1 2006 Ben Woodard - Modified spec file to fix some problems uncovered by rpmlint pdsh-2.36/configure.ac0000664€^–Á €^–Á 0000001422715131211226023002 0ustar arif.ali@canonical.comarif.ali@canonical.com# # $Id$ # # Copyright (C) 2000-2002 Regents of the University of California # See ./DISCLAIMER # # This file is to be processed with autoconf to generate a configure script. AC_INIT([pdsh], m4_esyscmd([git describe --always | awk '/.*/ {sub(/^pdsh-/, ""); printf "%s",$1; exit}'])) AC_CONFIG_SRCDIR([pdsh]) AC_CONFIG_AUX_DIR([config]) AC_CONFIG_MACRO_DIR([config]) AC_CONFIG_SRCDIR([src/pdsh/dsh.h]) AC_CANONICAL_TARGET AC_GPL_LICENSED # hack to fix dejagnu.am brokenness before automake 1.6 if test x$host_alias = x ; then host_alias=$host_cpu fi # # Automake support # AM_INIT_AUTOMAKE([no-define]) AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS([config.h]) AM_MAINTAINER_MODE AX_CODE_COVERAGE # # Checks for programs. # AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_DEBUG LT_INIT AM_CONDITIONAL(WITH_GNU_LD, test "$with_gnu_ld" = "yes") # # Do we want static modules? # AC_ARG_ENABLE([static-modules], AS_HELP_STRING([--enable-static-modules],[Build static modules]), ac_static_modules=yes) AM_CONDITIONAL(WITH_STATIC_MODULES, test "$ac_static_modules" = "yes") if test "$ac_static_modules" = "yes" ; then AC_DEFINE_UNQUOTED(STATIC_MODULES, [1], [Use Static Modules]) AC_STATIC_MODULES_INIT else case "$host" in *-*-aix*) LDFLAGS="$LDFLAGS -Wl,-brtl -Wl,-bexpall" AIX_PDSH_LDFLAGS="-Wl,-bgcbypass:1000" # hack to ensure no gc in libcommon AC_DEFINE(_AIX, 1, [Define if on AIX]) ;; *-*-solaris*) AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [POSIX pthread semantics on Solaris]) ;; *-*-hpux*) LDFLAGS="$LDFLAGS -Wl,-E" ;; esac fi AC_SUBST(AIX_PDSH_LDFLAGS) # Checks for libraries. AC_CHECK_LIB([socket], [socket], LIBS="-lsocket -lnsl $LIBS",, [-lsocket -lnsl]) # Check for how to compile pthread programs: ACX_PTHREAD if test x"$acx_pthread_ok" = "xno"; then AC_MSG_ERROR([Could not figure out how to compile with pthreads.]) fi AC_DEFINE(WITH_PTHREADS, 1, [Define if you have pthreads]) # PTHREAD_CFLAGS needs to be appended to both LDFLAGS and CPPFLAGS or some # checks for headers may fail later on (e.g. on OSF systems where -pthread # is required in order to include pthread.h) LDFLAGS="$LDFLAGS $PTHREAD_CFLAGS" CPPFLAGS="$CPPFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Checks for header files. AC_CHECK_HEADERS([fcntl.h strings.h sys/file.h unistd.h features.h \ pthread.h poll.h sys/poll.h sys/sysmacros.h, sys/uio.h]) # Checks for typedefs, structures, and compiler characteristics. TYPE_SOCKLEN_T AC_SYS_LARGEFILE AC_MSGHDR_ACCRIGHTS # Checks for library functions. dnl AC_FUNC_MALLOC AC_FUNC_STRERROR_R AC_CHECK_FUNCS([strerror pthread_sigmask sigthreadmask rresvport rresvport_af atoi]) # # Check for poll vs. select() # AC_POLLSELECT # # Test for default pdsh fanout and connect timeout # AC_FANOUT AC_CONNECT_TIMEOUT # # Test for inclusion of standard "rsh" module # AC_RSH AM_CONDITIONAL(WITH_RSH, [test "$ac_with_rsh" = "yes"]) # # Test for inclusion of standard "xcpu" module # AC_XCPU AM_CONDITIONAL(WITH_XCPU, [test "$ac_with_xcpu" = "yes"]) # # Test for ssh # AC_SSH AM_CONDITIONAL(WITH_SSH, test "$ac_have_ssh" = "yes") # # Test for exec # AC_EXEC AM_CONDITIONAL(WITH_EXEC, test "$ac_have_exec" = "yes") # # Test for kerberos # AC_KRB4 AM_CONDITIONAL(WITH_KRB4, test "$ac_have_krb4" = "yes") # # Test for whether to build "machines" module. # AC_MACHINES AM_CONDITIONAL(WITH_MACHINES, [test "$ac_have_machines" = "yes"]) # # Check for genders # AC_GENDERS AM_CONDITIONAL(WITH_LIBGENDERS, [test "$ac_have_libgenders" = "yes"]) # # Check for nodeupdown # AC_NODEUPDOWN AM_CONDITIONAL(WITH_NODEUPDOWN, [test "$ac_have_libnodeupdown" = "yes"]) # # Check for libmunge and mrsh # AC_MRSH AM_CONDITIONAL(WITH_LIBMUNGE, [test "$ac_have_libmunge" = "yes"]) AM_CONDITIONAL(WITH_MRSH, [test "$ac_have_mrsh" = "yes"]) # # Determine whether to build SLURM module (support running under SLURM jobid # AC_SLURM AM_CONDITIONAL([WITH_SLURM], [test "$ac_have_slurm" = "yes"]) # # Determine whether to to build Torque mudule (support running under Torque jobid) # AC_TORQUE AM_CONDITIONAL([WITH_TORQUE], [test "$ac_have_torque" = "yes"]) # # Determine whether to build dshgroup module # (support dsh-style /etc/dsh/group/%s ~/.dsh/group/%s files) # AC_DSHGROUP AM_CONDITIONAL([WITH_DSHGROUP], [test "$ac_with_dshgroup" = "yes"]) AC_NETGROUP AM_CONDITIONAL([WITH_NETGROUP], [test "$ac_with_netgroup" = "yes"]) dnl dnl check for whether to include readline support dnl AC_READLINE AM_CONDITIONAL([WITH_READLINE], [test "$ac_with_readline" = "yes"]) dnl dnl check for inclusion of Dmalloc. dnl Note: this macro defines WITH_DMALLOC for us. dnl AC_DMALLOC # # Check for alternate rcmd rank list: # AC_RCMD_RANK_LIST if test "$ac_static_modules" = "yes" ; then AC_STATIC_MODULES_EXIT fi # # Build PDSH_VERSION string # test "$ac_static_modules" = "yes" && EXTRA_VERS="+static-modules" test "$ac_with_readline" = "yes" && EXTRA_VERS="${EXTRA_VERS}+readline" test "$ac_debug" = "yes" && EXTRA_VERS="${EXTRA_VERS}+debug" test "$ac_with_dmalloc" = "yes" && EXTRA_VERS="${EXTRA_VERS}+dmalloc" if test -n "$EXTRA_VERS" ; then PDSH_VERSION_FULL="$PACKAGE_NAME-$PACKAGE_VERSION ($EXTRA_VERS)" else PDSH_VERSION_FULL="$PACKAGE_NAME-$PACKAGE_VERSION" fi AC_SUBST(PDSH_VERSION) AC_SUBST(PDSH_VERSION_FULL) AC_DEFINE(WITH_LSD_FATAL_ERROR_FUNC, 1, [Have definition of lsd_fatal_error]) AC_DEFINE(WITH_LSD_NOMEM_FUNC, 1, [Have definition of lsd_nomem_error]) AH_BOTTOM( [#ifdef WITH_DMALLOC # include # include #endif /* WITH_DMALLOC */] ) # Should probably be defining tests for these - cheat for now AH_BOTTOM( [#ifdef _AIX # define HAVE_MTSAFE_GETHOSTBYNAME 1 # define HAVE_MAGIC_RSHELL_CLEANUP 1 # define WANT_RECKLESS_HOSTRANGE_EXPANSION 1 #else # define HAVE_MTSAFE_GETHOSTBYNAME 0 # define HAVE_MAGIC_RSHELL_CLEANUP 0 # define WANT_RECKLESS_HOSTRANGE_EXPANSION 0 #endif /* _AIX */] ) AC_CONFIG_FILES([ Makefile config/Makefile src/Makefile src/common/Makefile src/pdsh/Makefile src/modules/Makefile doc/Makefile scripts/Makefile tests/Makefile tests/test-modules/Makefile doc/pdcp.1 doc/pdsh.1 ] ) AC_OUTPUT pdsh-2.36/README.KRB40000664€^–Á €^–Á 0000001073415131211226022074 0ustar arif.ali@canonical.comarif.ali@canonical.comTo build the *API only* on an SP with AIX 4.2.1 / PSSP 2.4 and install in /usr/local/krb4, use the following manual procedure or run the attached script (but look at it first: set paths appropriate to your environment and note that it destroys an old copy of the API if it is in the way). NOTES: - This has not been tested in a long while. We have stopped using KRB4 for pdsh on our SP's because we would occasionally overwhelm our KDC. Also there is a bug which causes pdsh to run very slowly when pdsh has to refresh the tgt for a node. --jg - gcc needs to be in your PATH. 1) Obtain cns-96q4.tar.gz distribution from MIT and untar in ./kerberos. See: http://web.mit.edu/network/kerberos-form.html (verified this 3/98). Hmm there may be Y2K problems in this release... 2) Make sure the destination exists mkdir /usr/local/krb4 chmod 755 /usr/local/krb4 3) Configure mkdir krb4.aix4 cd krb4.aix4 ../kerberos/configure --prefix=/usr/local/krb4 3a) [You may skip this option and do 5a instead if you wish] Hand edit src/Makefile and src/include/Makefile and change CONFDIR=$(LIBDIR) to CONFDIR=/etc *** We choose to go with /etc at LLNL because the API is installed on an *** NFS mounted partition and we don't want things compiled with the API *** to break if the partition isn't mounted. 3b) Depend make depend 4) Build/install the pieces we want cd src/util/et make all install cd ../ss make all install cd ../../include make all cp ../lib/kadm/kadm_err.h . cp ../lib/krb/krb_err.h . make install cd ../lib/acl make all install cd ../des make all install cd ../krb make all install cd ../kdb make all install cd ../kstream make all install cd ../kadm cp ../krb/krb_err.h . make all install 5) Perform a little cleanup # return to directory that contains ./kerberos and ./krb4.aix4 cp ./kerberos/src/include/kparse.h /usr/local/krb4/include cd /usr/local/krb4 rm -rf man bin cd include rm -f c-386aix.h c-386bsd.h c-386linux.h c-386sco.h c-alpha.h \ c-apollo.h c-aux.h c-hp68k.h c-hpsnake.h c-ibm370.h c-next.h \ c-pc.h c-pyr.h c-rtpc.h c-sgi.h c-sol2.h c-sun3.h c-sun386i.h \ c-sunos4.h c-tahoe.h c-ultmips.h c-vax.h 5a) [Only if step 3a was skipped] Link SP config files into the locations wanted by the API cd /usr/local/krb4/lib ln -s /etc/krb.conf . ln -s /etc/krb.realms . ------SNIP------ #!/bin/ksh # this script blows away /usr/local/krb4 and installs the new stuff # make sure gcc is in our path PATH=/usr/bin:/usr/ucb:/usr/sbin:/usr/local/gnu/bin:/usr/local/bin # the install/build paths PATH_TOP=/usr/local/src/krb4 PATH_BUILD=$PATH_TOP/krb4.aix4 PATH_SRC=$PATH_TOP/kerberos PATH_DEST=/usr/local/krb4 # sanity check before we get started if [ ! -d $PATH_TOP ]; then echo $PATH_TOP must exist, bye. exit 1 fi if [ ! -d $PATH_SRC ]; then echo $PATH_SRC must exist, bye. exit 1 fi set -o verbose # blow away any build cruft rm -rf $PATH_BUILD mkdir $PATH_BUILD chmod 755 $PATH_BUILD # blow away any old copies <--- danger will robinson! rm -rf $PATH_DEST mkdir $PATH_DEST chmod 755 $PATH_DEST # configure cd $PATH_BUILD || exit 1 ../kerberos/configure --prefix=/usr/local/krb4 # force krb.conf and krb.realms to /etc cd $PATH_BUILD/src || exit 1 sed 's/^CONFDIR=.*/CONFDIR=\/etc/' /tmp/blah.$$ cp /tmp/blah.$$ Makefile cd $PATH_BUILD/src/include || exit 1 sed 's/^CONFDIR=.*/CONFDIR=\/etc/' /tmp/blah2.$$ cp /tmp/blah2.$$ Makefile # make depend cd $PATH_BUILD || exit 1 make depend # make the relevant pieces cd $PATH_BUILD/src/util/et || exit 1 make all install cd $PATH_BUILD/src/util/ss || exit 1 make all install cd $PATH_BUILD/src/include || exit 1 make all cp ../lib/kadm/kadm_err.h . cp ../lib/krb/krb_err.h . make install cd $PATH_BUILD/src/lib/acl || exit 1 make all install cd $PATH_BUILD/src/lib/des || exit 1 make all install cd $PATH_BUILD/src/lib/krb || exit 1 make all install cd $PATH_BUILD/src/lib/kdb || exit 1 make all install cd $PATH_BUILD/src/lib/kstream || exit 1 make all install cd $PATH_BUILD/src/lib/kadm || exit 1 cp ../krb/krb_err.h . make all install cd $PATH_SRC || exit 1 cp ./src/include/kparse.h $PATH_DEST/include # clean up useless directories and includes cd $PATH_DEST || exit 1 rm -rf man bin cd include || exit 1 rm -f c-386aix.h c-386bsd.h c-386linux.h c-386sco.h c-alpha.h \ c-apollo.h c-aux.h c-hp68k.h c-hpsnake.h c-ibm370.h c-next.h \ c-pc.h c-pyr.h c-rtpc.h c-sgi.h c-sol2.h c-sun3.h c-sun386i.h \ c-sunos4.h c-tahoe.h c-ultmips.h c-vax.h exit 0 pdsh-2.36/tests/0000775€^–Á €^–Á 0000000000015131211226021650 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/tests/Makefile.am0000664€^–Á €^–Á 0000000135615131211226023711 0ustar arif.ali@canonical.comarif.ali@canonical.com SUBDIRS = test-modules CPPFLAGS = \ -I $(top_srcdir) TEST_EXTENSIONS = .sh SH_LOG_DRIVER = \ env AM_TAP_AWK='$(AWK)' $(SHELL) \ $(top_srcdir)/config/tap-driver.sh check_SCRIPTS = \ t0001-basic.sh \ t0002-internal.sh \ t0003-wcoll.sh \ t0004-module-loading.sh \ t0005-rcmd_type-and-user.sh \ t0006-pdcp.sh \ t1001-genders.sh \ t1002-dshgroup.sh \ t1003-slurm.sh \ t2000-exec.sh \ t2001-ssh.sh \ t2002-mrsh.sh \ t5000-dshbak.sh \ t6036-long-output-lines.sh \ t6114-no-newline-corruption.sh TESTS = \ $(check_SCRIPTS) EXTRA_DIST = \ $(check_SCRIPTS) \ test-lib.sh \ aggregate-results.sh clean-local: rm -fr trash-directory.* test-results .prove *.log *.output pdsh-2.36/tests/t0006-pdcp.sh0000775€^–Á €^–Á 0000001045115131211226023705 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdcp specific tests' if ! test -x ../src/pdsh/pdsh; then echo >&2 'Failed to find pdsh binary. Please run make.' exit 1 fi . ${srcdir:-.}/test-lib.sh ########################################################################### # # First set up pdcp link to pdsh # test_expect_success 'Creating pdcp link to pdsh binary' ' ln -s $PDSH_BUILD_DIR/src/pdsh/pdsh pdcp ' test_expect_success 'Creating rpdcp link to pdsh binary' ' ln -s $PDSH_BUILD_DIR/src/pdsh/pdsh rpdcp ' export PATH=.:$PATH ########################################################################### # # Basic pdcp tests # test_expect_success 'pdcp runs' ' pdcp -w foo -q * /tmp | tail -1 | grep foo ' test_expect_success 'rpdcp runs' ' rpdcp -w foo -q * /tmp | tail -1 | grep foo ' test_expect_success 'pdcp -V works' ' pdcp -V 2>&1 | grep -q ^pdsh ' test_expect_success 'pdcp -q works' ' pdcp -q -w foo * /tmp 2>&1 | grep -q Infile ' check_pdcp_option() { flag=$1; name=$2; value=$3; flagval=$value if test "$value" = "Yes" -o "$value" = "No"; then flagval="" fi echo "flag=$flag name='$name' value=$value flagval=$flagval" pdcp -$flag$flagval -w foo -q * /tmp | grep -q "$name[ ]*$value$" } test_expect_success '-e sets remote program path' ' check_pdcp_option e "Remote program path" /remote/pdcp ' test_expect_success 'PDSH_REMOTE_PDCP_PATH sets remote program path' ' tag="Remote program path" path="/xxx/pdcp" PDSH_REMOTE_PDCP_PATH=$path pdcp -w foo -q * /tmp | grep -q "$tag[ ]*$path$" ' test_expect_success '-f sets fanout' ' check_pdcp_option f Fanout 8 ' test_expect_success '-l sets remote username' ' check_pdcp_option l "Remote username" foouser ' test_expect_success '-t sets connect timeout' ' check_pdcp_option t "Connect timeout (secs)" 33 ' test_expect_success '-u sets command timeout' ' check_pdcp_option u "Command timeout (secs)" 22 ' test_expect_success 'command timeout 0 by default' ' pdcp -w foo -q * /tmp | grep -q "Command timeout (secs)[ ]*0$" ' export T="$TEST_DIRECTORY/test-modules/.libs" test_expect_success DYNAMIC_MODULES,NOTROOT 'Have pcptest rcmd module' ' PDSH_MODULE_DIR=$T pdcp -L | grep -q pcptest ' setup_host_dirs() { pdsh -w "$1" -Rexec mkdir %h } create_random_file() { name=${1-testfile} size=${2-1} dd if=/dev/urandom of=$name bs=1024 count=$size >/dev/null 2>&1 } test_expect_success DYNAMIC_MODULES,NOTROOT 'pdcp basic functionality' ' HOSTS="host[0-10]" setup_host_dirs "$HOSTS" && test_when_finished "rm -rf host* testfile" && create_random_file testfile 10 && PDSH_MODULE_DIR=$T pdcp -Rpcptest -w "$HOSTS" testfile testfile && pdsh -SRexec -w "$HOSTS" $GIT_TEST_CMP testfile %h/testfile ' rm -rf host* testfile test_expect_success DYNAMIC_MODULES,NOTROOT 'rpdcp basic functionality' ' HOSTS="host[0-10]" setup_host_dirs "$HOSTS" test_when_finished "rm -rf host* t output" && pdsh -Rexec -w "$HOSTS" dd if=/dev/urandom of=%h/t bs=1024 count=10 >/dev/null 2>&1 && mkdir output && PDSH_MODULE_DIR=$T rpdcp -Rpcptest -w "$HOSTS" t output/ && pdsh -SRexec -w "$HOSTS" $GIT_TEST_CMP output/t.%h %h/t ' test_expect_success DYNAMIC_MODULES,NOTROOT 'initialize directory tree' ' mkdir tree && ( cd tree && echo foo >foo && ln -s foo foo.link && mkdir -p dir/a/b/c/d/e && create_random_file dir/data 1024 && echo "deep dir" > dir/a/b/c/d/e/file && mkdir bar && echo "zzz" >bar/zzz && mkdir baz && echo "#!$SHELL_PATH" > baz/exec.sh && chmod +x baz/exec.sh && echo "write protected file" > dir/a/b/c/xw && chmod -w dir/a/b/c/xw ) ' test_expect_success DYNAMIC_MODULES,NOTROOT 'pdcp -r works' ' HOSTS="host[0-10]" setup_host_dirs "$HOSTS" && test_when_finished "rm -rf host*" && PDSH_MODULE_DIR=$T pdcp -Rpcptest -w "$HOSTS" -r tree . && pdsh -SRexec -w "$HOSTS" diff -r tree %h/tree >/dev/null && pdsh -SRexec -w "$HOSTS" test -x tree/baz/exec.sh && pdsh -SRexec -w "$HOSTS" test -h tree/foo.link && pdsh -SRexec -w "$HOSTS" test ! -w dir/a/b/c/xw ' test_expect_success DYNAMIC_MODULES,NOTROOT 'rpdcp -r works' ' HOSTS="host[0-10]" setup_host_dirs "$HOSTS" && test_when_finished "rm -rf host* output" && pdsh -SRexec -w "$HOSTS" cp -r tree %h/ && mkdir output && PDSH_MODULE_DIR=$T rpdcp -Rpcptest -w "$HOSTS" -r tree output/ && pdsh -SRexec -w "$HOSTS" diff -r tree output/tree.%h >/dev/null ' test_done pdsh-2.36/tests/test-modules/0000775€^–Á €^–Á 0000000000015131211226024275 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/tests/test-modules/Makefile.am0000664€^–Á €^–Á 0000000141315131211226026330 0ustar arif.ali@canonical.comarif.ali@canonical.comAUTOMAKE_OPTIONS = foreign AM_CPPFLAGS = -I$(top_srcdir) if WITH_GNU_LD VERSION_SCRIPT = \ version.map OTHER_FLAGS = \ -Wl,--version-script=$(VERSION_SCRIPT) endif BUILT_SOURCES = \ $(VERSION_SCRIPT) MODULE_FLAGS = \ -module -avoid-version $(OTHER_FLAGS) -rpath /foo check_LTLIBRARIES = \ a.la \ b.la \ pcptest.la a_la_SOURCES = a.c a_la_LDFLAGS = $(MODULE_FLAGS) b_la_SOURCES = b.c b_la_LDFLAGS = $(MODULE_FLAGS) pcptest_la_SOURCES = pcptest.c pcptest_la_LDFLAGS = $(MODULE_FLAGS) $(VERSION_SCRIPT) : (echo "{ global:"; \ echo " pdsh_module_info;"; \ echo " pdsh_module_priority;"; \ echo " local: *;"; \ echo "};") > $(VERSION_SCRIPT) DISTCLEANFILES = \ $(VERSION_SCRIPT) pdsh-2.36/tests/test-modules/pcptest.c0000664€^–Á €^–Á 0000001140615131211226026125 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2007-2011 Lawrence Livermore National Security, LLC. * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * This module uses the "pipecmd" interface to execute * a process in a subdirectory based on the remote hostname. * Used for pdcp/rpdcp testing. * */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "src/pdsh/opt.h" #include "src/pdsh/mod.h" #include "src/pdsh/rcmd.h" #include "src/common/pipecmd.h" #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int mod_pcptest_postop(opt_t *opt); static int mod_pcptest_exit (void); static int pcptest_init(opt_t *); static int pcptest_signal(int, void *arg, int); static int pcptest(char *, char *, char *, char *, char *, int, int *, void **); static int pcptest_destroy (pipecmd_t p); /* * Export generic pdsh module operations: */ struct pdsh_module_operations pcptest_module_ops = { (ModInitF) NULL, (ModExitF) mod_pcptest_exit, (ModReadWcollF) NULL, (ModPostOpF) mod_pcptest_postop }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations pcptest_rcmd_ops = { (RcmdInitF) pcptest_init, (RcmdSigF) pcptest_signal, (RcmdF) pcptest, (RcmdDestroyF) pcptest_destroy }; /* * Export module options */ struct pdsh_module_option pcptest_module_options[] = { PDSH_OPT_TABLE_END }; /* * Sshcmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "pcptest", "Mark Grondona ", "Rcmd connect module used for pdcp/rpdcp testing", PCP, &pcptest_module_ops, &pcptest_rcmd_ops, &pcptest_module_options[0], }; static int mod_pcptest_postop(opt_t *opt) { if (opt->rcmd_name && strcmp(opt->rcmd_name, "pcptest") == 0) { if (opt->connect_timeout != CONNECT_TIMEOUT) { err("%p: Cannot specify -t with \"-R pcptest\"\n"); return 1; } } return 0; } static int pcptest_init(opt_t * opt) { /* * Drop privileges if running setuid root */ if ((geteuid() == 0) && (getuid() != 0)) setuid (getuid ()); /* * Do not resolve hostnames in pdsh when using pcptest */ if (rcmd_opt_set (RCMD_OPT_RESOLVE_HOSTS, 0) < 0) errx ("%p: pcptest_init: rcmd_opt_set: %m\n"); return 0; } static int mod_pcptest_exit (void) { return 0; } static int pcptest_signal(int fd, void *arg, int signum) { return (pipecmd_signal ((pipecmd_t) arg, signum)); } static const char **pcptest_argv_create (char *remote_cmd) { char *cmd; const char **argv; /* Prepend chdir to remote argv, then collapse args * so they can be fed to /bin/sh -c */ cmd = Strdup ("cd %h; "); xstrcat (&cmd, remote_cmd); argv = Malloc (4 * sizeof (char *)); argv[0] = Strdup ("/bin/sh"); argv[1] = Strdup ("-c"); argv[2] = cmd; argv[3] = NULL; return argv; } static int pcptest(char *ahost, char *addr, char *luser, char *ruser, char *cmd, int rank, int *fd2p, void **arg) { pipecmd_t p; const char **argv = pcptest_argv_create (cmd); if (!(p = pipecmd (argv[0], argv + 1, ahost, ruser, rank))) return (-1); if (fd2p) *fd2p = pipecmd_stderrfd (p); *arg = p; return (pipecmd_stdoutfd (p)); } static int pcptest_destroy (pipecmd_t p) { int status; if (pipecmd_wait (p, &status) < 0) return (1); pipecmd_destroy (p); return (WEXITSTATUS (status)); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/tests/test-modules/a.c0000664€^–Á €^–Á 0000000457515131211226024674 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2010 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * UCRL-CODE-2003-005. * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include "src/pdsh/mod.h" int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int opt_a(opt_t *, int, char *); static int a_init (void); /* * Export pdsh module operations structure */ struct pdsh_module_operations a_module_ops = { (ModInitF) a_init, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations a_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option a_module_options[] = { { 'a', NULL, "the a option for Module A", DSH | PCP, (optFunc) opt_a }, PDSH_OPT_TABLE_END }; /* * A module info */ struct pdsh_module pdsh_module_info = { "misc", "A", "Mark Grondona", "Module test A", DSH, &a_module_ops, &a_rcmd_ops, &a_module_options[0], }; static int opt_a(opt_t *pdsh_opt, int opt, char *arg) { fprintf (stdout, "A: got option\n"); return 0; } static int a_init (void) { fprintf (stdout, "A: in init\n"); return 0; } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/tests/test-modules/b.c0000664€^–Á €^–Á 0000000460415131211226024666 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2010 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * UCRL-CODE-2003-005. * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include "src/pdsh/mod.h" int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int opt_a(opt_t *, int, char *); static int b_init (void); /* * Export pdsh module operations structure */ struct pdsh_module_operations a_module_ops = { (ModInitF) b_init, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations a_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option a_module_options[] = { { 'a', NULL, "the a option for Module B", DSH | PCP, (optFunc) opt_a }, PDSH_OPT_TABLE_END }; /* * Machines module info */ struct pdsh_module pdsh_module_info = { "misc", "B", "Mark Grondona", "Module test A", DSH, &a_module_ops, &a_rcmd_ops, &a_module_options[0], }; static int opt_a(opt_t *pdsh_opt, int opt, char *arg) { fprintf (stdout, "B: got option\n"); return 0; } static int b_init (void) { fprintf (stdout, "B: in init\n"); return 0; } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/tests/test-lib.sh0000664€^–Á €^–Á 0000005537015131211226023741 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh # # Copyright (c) 2005 Junio C Hamano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/ . # if --tee was passed, write the output not only to the terminal, but # additionally to the file test-results/$BASENAME.out, too. test_name=$(basename "$0" .sh) test_dir=$(cd $(dirname "$0") && pwd) case "$GIT_TEST_TEE_STARTED, $* " in done,*) # do not redirect again ;; *' --tee '*|*' --va'*) mkdir -p test-results BASE=test-results/$test_name (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; echo $? > $BASE.exit) | tee $BASE.out test "$(cat $BASE.exit)" = 0 exit ;; esac # Keep the original TERM for say_color ORIGINAL_TERM=$TERM # For repeatability, reset the environment to known value. LANG=C LC_ALL=C PAGER=cat TZ=UTC TERM=dumb export LANG LC_ALL PAGER TERM TZ EDITOR=: export EDITOR # # If SHELL_PATH is not set, use a default of /bin/sh # SHELL_PATH=${SHELL_PATH:-/bin/sh} export SHELL_PATH unset VISUAL # # Pdsh variables # unset WCOLL unset PDSH_RCMD_TYPE unset PDSH_MISC_MODULES unset PDSH_MODULE_DIR unset DSHPATH unset FANOUT unset PDSH_GENDERS_FILE unset PDSH_GENDERS_DIR unset PDSH_SSH_ARGS unset PDSH_SSH_ARGS_APPEND unset SLUMR_JOBID unset PBS_JOBID # # PDSH_BUILD_DIR and PDSH_SRC_DIR are set to build and src paths # if test -z "$PDSH_BUILD_DIR"; then if test -z "${builddir}"; then PDSH_BUILD_DIR=$(pwd)/.. else PDSH_BUILD_DIR=$(cd ${builddir} && pwd)/.. fi export PDSH_BUILD_DIR fi # if test -z "$PDSH_SRC_DIR"; then if test -z "$srcdir"; then PDSH_SRC_DIR=${test_dir}/.. else PDSH_SRC_DIR=$(cd ${srcdir} && pwd)/.. fi export PDSH_SRC_DIR fi # Protect ourselves from common misconfiguration to export # CDPATH into the environment unset CDPATH unset GREP_OPTIONS # Convenience # # A regexp to match 5 and 40 hexdigits _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05" # Each test should start with something like this, after copyright notices: # # test_description='Description of this test... # This test checks if command xyzzy does the right thing... # ' # . ./test-lib.sh [ "x$ORIGINAL_TERM" != "xdumb" ] && ( TERM=$ORIGINAL_TERM && export TERM && [ -t 1 ] && tput bold >/dev/null 2>&1 && tput setaf 1 >/dev/null 2>&1 && tput sgr0 >/dev/null 2>&1 ) && color=t while test "$#" -ne 0 do case "$1" in -d|--d|--de|--deb|--debu|--debug) debug=t; shift ;; -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t; shift ;; -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests) PDSH_TEST_LONG=t; export PDSH_TEST_LONG; shift ;; -h|--h|--he|--hel|--help) help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; -q|--q|--qu|--qui|--quie|--quiet) # Ignore --quiet under a TAP::Harness. Saying how many tests # passed without the ok/not ok details is always an error. test -z "$HARNESS_ACTIVE" && quiet=t; shift ;; --with-dashes) with_dashes=t; shift ;; --no-color) color=; shift ;; --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) valgrind=t; verbose=t; shift ;; --tee) shift ;; # was handled already --root=*) root=$(expr "z$1" : 'z[^=]*=\(.*\)') shift ;; *) echo "error: unknown test option '$1'" >&2; exit 1 ;; esac done if test -n "$color"; then say_color () { ( TERM=$ORIGINAL_TERM export TERM case "$1" in error) tput bold; tput setaf 1;; # bold red skip) tput bold; tput setaf 2;; # bold green pass) tput setaf 2;; # green info) tput setaf 3;; # brown *) test -n "$quiet" && return;; esac shift printf "%s" "$*" tput sgr0 echo ) } else say_color() { test -z "$1" && test -n "$quiet" && return shift echo "$*" } fi error () { say_color error "error: $*" GIT_EXIT_OK=t exit 1 } say () { say_color info "$*" } test "${test_description}" != "" || error "Test script did not set test_description." if test "$help" = "t" then echo "$test_description" exit 0 fi exec 5>&1 if test "$verbose" = "t" then exec 4>&2 3>&1 else exec 4>/dev/null 3>/dev/null fi test_failure=0 test_count=0 test_fixed=0 test_broken=0 test_success=0 test_external_has_tap=0 die () { code=$? if test -n "$GIT_EXIT_OK" then exit $code else echo >&5 "FATAL: Unexpected exit with code $code" exit 1 fi } GIT_EXIT_OK= trap 'die' EXIT # The semantics of the editor variables are that of invoking # sh -c "$EDITOR \"$@\"" files ... # # If our trash directory contains shell metacharacters, they will be # interpreted if we just set $EDITOR directly, so do a little dance with # environment variables to work around this. # # In particular, quoting isn't enough, as the path may contain the same quote # that we're using. test_set_editor () { FAKE_EDITOR="$1" export FAKE_EDITOR EDITOR='"$FAKE_EDITOR"' export EDITOR } test_decode_color () { sed -e 's/.\[1m//g' \ -e 's/.\[31m//g' \ -e 's/.\[32m//g' \ -e 's/.\[33m//g' \ -e 's/.\[34m//g' \ -e 's/.\[35m//g' \ -e 's/.\[36m//g' \ -e 's/.\[m//g' } q_to_nul () { perl -pe 'y/Q/\000/' } q_to_cr () { tr Q '\015' } q_to_tab () { tr Q '\011' } append_cr () { sed -e 's/$/Q/' | tr Q '\015' } remove_cr () { tr '\015' Q | sed -e 's/Q$//' } test_tick () { if test -z "${test_tick+set}" then test_tick=1112911993 else test_tick=$(($test_tick + 60)) fi GIT_COMMITTER_DATE="$test_tick -0700" GIT_AUTHOR_DATE="$test_tick -0700" export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } # Use test_set_prereq to tell that a particular prerequisite is available. # The prerequisite can later be checked for in two ways: # # - Explicitly using test_have_prereq. # # - Implicitly by specifying the prerequisite tag in the calls to # test_expect_{success,failure,code}. # # The single parameter is the prerequisite tag (a simple word, in all # capital letters by convention). test_set_prereq () { satisfied="$satisfied$1 " } satisfied=" " test_have_prereq () { # prerequisites can be concatenated with ',' save_IFS=$IFS IFS=, set -- $* IFS=$save_IFS total_prereq=0 ok_prereq=0 missing_prereq= for prerequisite do total_prereq=$(($total_prereq + 1)) case $satisfied in *" $prerequisite "*) ok_prereq=$(($ok_prereq + 1)) ;; *) # Keep a list of missing prerequisites if test -z "$missing_prereq" then missing_prereq=$prerequisite else missing_prereq="$prerequisite,$missing_prereq" fi esac done test $total_prereq = $ok_prereq } # You are not expected to call test_ok_ and test_failure_ directly, use # the text_expect_* functions instead. test_ok_ () { test_success=$(($test_success + 1)) say_color pass "ok $test_count - $@" } test_failure_ () { test_failure=$(($test_failure + 1)) say_color error "not ok - $test_count $1" shift echo "$@" | sed -e 's/^/# /' test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } } test_known_broken_ok_ () { test_fixed=$(($test_fixed+1)) say_color pass "ok $test_count - $@ # TODO known breakage" } test_known_broken_failure_ () { test_broken=$(($test_broken+1)) say_color skip "not ok $test_count - $@ # TODO known breakage" } test_debug () { test "$debug" = "" || eval "$1" } test_run_ () { test_cleanup=: eval >&3 2>&4 "$1" eval_ret=$? eval >&3 2>&4 "$test_cleanup" if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then echo "" fi return 0 } test_skip () { test_count=$(($test_count+1)) to_skip= for skp in $PDSH_SKIP_TESTS do case $this_test.$test_count in $skp) to_skip=t break esac done if test -z "$to_skip" && test -n "$prereq" && ! test_have_prereq "$prereq" then to_skip=t fi case "$to_skip" in t) of_prereq= if test "$missing_prereq" != "$prereq" then of_prereq=" of $prereq" fi say_color skip >&3 "skipping test: $@" say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})" : true ;; *) false ;; esac } test_expect_failure () { test "$#" = 3 && { prereq=$1; shift; } || prereq= test "$#" = 2 || error "bug in the test script: not 2 or 3 parameters to test-expect-failure" if ! test_skip "$@" then say >&3 "checking known breakage: $2" test_run_ "$2" if [ "$?" = 0 -a "$eval_ret" = 0 ] then test_known_broken_ok_ "$1" else test_known_broken_failure_ "$1" fi fi echo >&3 "" } test_expect_success () { test "$#" = 3 && { prereq=$1; shift; } || prereq= test "$#" = 2 || error "bug in the test script: not 2 or 3 parameters to test-expect-success" if ! test_skip "$@" then say >&3 "expecting success: $2" test_run_ "$2" if [ "$?" = 0 -a "$eval_ret" = 0 ] then test_ok_ "$1" else test_failure_ "$@" fi fi echo >&3 "" } # Similar to test_must_fail and test_might_fail, but check that a # given command exited with a given exit code. Meant to be used as: # # test_expect_success 'Merge with d/f conflicts' ' # test_expect_code 1 git merge "merge msg" B master # ' test_expect_code () { want_code=$1 shift "$@" exit_code=$? if test $exit_code = $want_code then echo >&2 "test_expect_code: command exited with $exit_code: $*" return 0 else echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*" return 1 fi } # test_external runs external test scripts that provide continuous # test output about their progress, and succeeds/fails on # zero/non-zero exit code. It outputs the test output on stdout even # in non-verbose mode, and announces the external script with "# run # : ..." before running it. When providing relative paths, keep in # mind that all scripts run in "trash directory". # Usage: test_external description command arguments... # Example: test_external 'Perl API' perl ../path/to/test.pl test_external () { test "$#" = 4 && { prereq=$1; shift; } || prereq= test "$#" = 3 || error >&5 "bug in the test script: not 3 or 4 parameters to test_external" descr="$1" shift if ! test_skip "$descr" "$@" then # Announce the script to reduce confusion about the # test output that follows. say_color "" "# run $test_count: $descr ($*)" # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG # to be able to use them in script export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG export TEST_SRCDIR="${test_dir}" # Run command; redirect its stderr to &4 as in # test_run_, but keep its stdout on our stdout even in # non-verbose mode. "$@" 2>&4 if [ "$?" = 0 ] then if test $test_external_has_tap -eq 0; then test_ok_ "$descr" else say_color "" "# test_external test $descr was ok" test_success=$(($test_success + 1)) fi else if test $test_external_has_tap -eq 0; then test_failure_ "$descr" "$@" else say_color error "# test_external test $descr failed: $@" test_failure=$(($test_failure + 1)) fi fi fi } # Like test_external, but in addition tests that the command generated # no output on stderr. test_external_without_stderr () { # The temporary file has no (and must have no) security # implications. tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi stderr="$tmp/git-external-stderr.$$.tmp" test_external "$@" 4> "$stderr" [ -f "$stderr" ] || error "Internal error: $stderr disappeared." descr="no stderr: $1" shift say >&3 "# expecting no stderr from previous command" if [ ! -s "$stderr" ]; then rm "$stderr" if test $test_external_has_tap -eq 0; then test_ok_ "$descr" else say_color "" "# test_external_without_stderr test $descr was ok" test_success=$(($test_success + 1)) fi else if [ "$verbose" = t ]; then output=`echo; echo "# Stderr is:"; cat "$stderr"` else output= fi # rm first in case test_failure exits. rm "$stderr" if test $test_external_has_tap -eq 0; then test_failure_ "$descr" "$@" "$output" else say_color error "# test_external_without_stderr test $descr failed: $@: $output" test_failure=$(($test_failure + 1)) fi fi } # debugging-friendly alternatives to "test [-f|-d|-e]" # The commands test the existence or non-existence of $1. $2 can be # given to provide a more precise diagnosis. test_path_is_file () { if ! [ -f "$1" ] then echo "File $1 doesn't exist. $*" false fi } test_path_is_dir () { if ! [ -d "$1" ] then echo "Directory $1 doesn't exist. $*" false fi } test_path_is_missing () { if [ -e "$1" ] then echo "Path exists:" ls -ld "$1" if [ $# -ge 1 ]; then echo "$*" fi false fi } # This is not among top-level (test_expect_success | test_expect_failure) # but is a prefix that can be used in the test script, like: # # test_expect_success 'complain and die' ' # do something && # do something else && # test_must_fail git checkout ../outerspace # ' # # Writing this as "! git checkout ../outerspace" is wrong, because # the failure could be due to a segv. We want a controlled failure. test_must_fail () { "$@" exit_code=$? if test $exit_code = 0; then echo >&2 "test_must_fail: command succeeded: $*" return 1 elif test $exit_code -gt 129 -a $exit_code -le 192; then echo >&2 "test_must_fail: died by signal: $*" return 1 elif test $exit_code = 127; then echo >&2 "test_must_fail: command not found: $*" return 1 fi return 0 } # Similar to test_must_fail, but tolerates success, too. This is # meant to be used in contexts like: # # test_expect_success 'some command works without configuration' ' # test_might_fail git config --unset all.configuration && # do something # ' # # Writing "git config --unset all.configuration || :" would be wrong, # because we want to notice if it fails due to segv. test_might_fail () { "$@" exit_code=$? if test $exit_code -gt 129 -a $exit_code -le 192; then echo >&2 "test_might_fail: died by signal: $*" return 1 elif test $exit_code = 127; then echo >&2 "test_might_fail: command not found: $*" return 1 fi return 0 } # test_cmp is a helper function to compare actual and expected output. # You can use it like: # # test_expect_success 'foo works' ' # echo expected >expected && # foo >actual && # test_cmp expected actual # ' # # This could be written as either "cmp" or "diff -u", but: # - cmp's output is not nearly as easy to read as diff -u # - not all diff versions understand "-u" test_cmp() { $GIT_TEST_CMP "$@" } # This function can be used to schedule some commands to be run # unconditionally at the end of the test to restore sanity: # # test_expect_success 'test core.capslock' ' # git config core.capslock true && # test_when_finished "git config --unset core.capslock" && # hello world # ' # # That would be roughly equivalent to # # test_expect_success 'test core.capslock' ' # git config core.capslock true && # hello world # git config --unset core.capslock # ' # # except that the greeting and config --unset must both succeed for # the test to pass. test_when_finished () { test_cleanup="{ $* } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup" } # # pdsh convenience functions # test_output_is_expected() { OUTPUT="$1" EXPECTED="$2" if ! test "$OUTPUT" = "$EXPECTED"; then say_color error "Error: Output \'$OUTPUT\' != \'$EXPECTED\'" false fi } # Most tests can use the created repository, but some may need to create more. # Usage: test_create_repo test_create_repo () { test "$#" = 1 || error "bug in the test script: not 1 parameter to test-create-repo" repo="$1" mkdir -p "$repo" } test_done () { GIT_EXIT_OK=t if test -z "$HARNESS_ACTIVE"; then test_results_dir="$TEST_DIRECTORY/test-results" mkdir -p "$test_results_dir" test_results_path="$test_results_dir/$test_name-$$.counts" echo "total $test_count" >> $test_results_path echo "success $test_success" >> $test_results_path echo "fixed $test_fixed" >> $test_results_path echo "broken $test_broken" >> $test_results_path echo "failed $test_failure" >> $test_results_path echo "" >> $test_results_path fi if test "$test_fixed" != 0 then say_color pass "# fixed $test_fixed known breakage(s)" fi if test "$test_broken" != 0 then say_color error "# still have $test_broken known breakage(s)" msg="remaining $(($test_count-$test_broken)) test(s)" else msg="$test_count test(s)" fi case "$test_failure" in 0) # Maybe print SKIP message [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all" if test $test_external_has_tap -eq 0; then say_color pass "# passed all $msg" say "1..$test_count$skip_all" fi test -d "$remove_trash" && cd "$(dirname "$remove_trash")" && rm -rf "$(basename "$remove_trash")" exit 0 ;; *) if test $test_external_has_tap -eq 0; then say_color error "# failed $test_failure among $msg" say "1..$test_count" fi exit 1 ;; esac } # Test the binaries we have just built. The tests are kept in # t/ subdirectory and are run in 'trash directory' subdirectory. if test -z "$TEST_DIRECTORY" then # We allow tests to override this, in case they want to run tests # outside of t/, e.g. for running tests on the test library # itself. TEST_DIRECTORY=$(pwd) fi TEST_SRCDIR=${test_dir} if test -n "$valgrind" then make_symlink () { test -h "$2" && test "$1" = "$(readlink "$2")" || { # be super paranoid if mkdir "$2".lock then rm -f "$2" && ln -s "$1" "$2" && rm -r "$2".lock else while test -d "$2".lock do say "Waiting for lock on $2." sleep 1 done fi } } make_valgrind_symlink () { # handle only executables test -x "$1" || return base=$(basename "$1") symlink_target=$GIT_BUILD_DIR/$base # do not override scripts if test -x "$symlink_target" && test ! -d "$symlink_target" && test "#!" != "$(head -c 2 < "$symlink_target")" then symlink_target=../valgrind.sh fi case "$base" in *.sh|*.perl) symlink_target=../unprocessed-script esac # create the link, or replace it if it is out of date make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit } # override all executables in TEST_DIRECTORY/.. GIT_VALGRIND=$TEST_DIRECTORY/valgrind mkdir -p "$GIT_VALGRIND"/bin make_valgrind_symlink $PDSH_BUILD_DIR/src/pdsh/pdsh IFS=$OLDIFS PATH=$GIT_VALGRIND/bin:$PATH export GIT_VALGRIND elif test -n "$PDSH_TEST_INSTALLED" ; then PATH=$PDSH_TEST_INSTALLED:$PDSH_BUILD_DIR/src/pdsh:$PATH GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH} else # normal case, use ../bin-wrappers only unless $with_dashes: pdsh_path=$PDSH_BUILD_DIR/src/pdsh dshbak_path=$PDSH_SRC_DIR/scripts test -n "$dshbak_path" && PATH="$dshbak_path:$PATH" test -n "$pdsh_path" && PATH="$pdsh_path:$PATH" fi export PATH if test -z "$GIT_TEST_CMP" then if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT" then GIT_TEST_CMP="diff -c" else GIT_TEST_CMP="diff -u" fi fi test="trash-directory.$test_name" test -n "$root" && test="$root/$test" case "$test" in /*) TRASH_DIRECTORY="$test" ;; *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;; esac test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$test" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" exit 1 } test_create_repo "$test" # Use -P to resolve symlinks in our working directory so that the cwd # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$test" || exit 1 HOME=$(pwd) export HOME this_test=${0##*/} this_test=${this_test%%-*} for skp in $PDSH_SKIP_TESTS do case "$this_test" in $skp) say_color skip >&3 "skipping test $this_test altogether" skip_all="skip all tests in $this_test" test_done esac done # Provide an implementation of the 'yes' utility yes () { if test $# = 0 then y=y else y="$*" fi while echo "$y" do : done } # Fix some commands on Windows case $(uname -s) in *MINGW*) # Windows has its own (incompatible) sort and find sort () { /usr/bin/sort "$@" } find () { /usr/bin/find "$@" } sum () { md5sum "$@" } # git sees Windows-style pwd pwd () { builtin pwd -W } # no POSIX permissions # backslashes in pathspec are converted to '/' # exec does not inherit the PID ;; *) test_set_prereq POSIXPERM test_set_prereq BSLASHPSPEC test_set_prereq EXECKEEPSPID ;; esac test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PYTHON" && test_set_prereq PYTHON # test whether the filesystem supports symbolic links ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS rm -f y # When the tests are run as root, permission tests will report that # things are writable when they shouldn't be. # # Additionally, for pdsh, some tests require non-root, esp those # that use PDSH_MODULE_DIR, which doesn't work when run setuid or # as root test -w / || test_set_prereq SANITY test "$USER" = "root" || test_set_prereq NOTROOT # Set some prereqs for common commands # test "$(expr 9 - 2)" = "7" && test_set_prereq EXPR # Function to generate a random number # random() { R=$RANDOM if test -z "$R"; then if test -r /dev/urandom; then R=$(dd if=/dev/urandom count=1 2>/dev/null | cksum | cut -d' ' -f1) else R=$( (echo $$; ps; date +%s) 2>&1 | cksum | cut -d' ' -f1) fi fi if test -n "${1}"; then R=$(expr $R % $1) fi echo $R } # Shell implementation of seq(1) # seq() { if [ $# -eq 1 ]; then i=1 end=$1 else i=$1 end=$2 fi while [ $i -le $end ]; do echo $i i=$((i+1)) done } # # Run a command and send SIGALRM after timeout seconds # run_timeout() { perl -e 'alarm shift @ARGV; exec @ARGV' "$@" } # # Ensure that pdsh has been built. # if ! test -x $PDSH_BUILD_DIR/src/pdsh/pdsh; then say_color error 'Can not find a pdsh binary to test' say_color error 'Do you need to run the build system?' exit 1 fi # # If the pdsh build directory owner and the pdsh binary have # different ownership, abort the test because pdsh will not # be able to load any modules, and almost no tests will work. # path_owner() { ls -ld $1 | awk '{print $3}'; } pdsh_owner=$(path_owner $PDSH_BUILD_DIR/src/pdsh/pdsh) builddir_owner=$(path_owner $PDSH_BUILD_DIR/src) if test "$pdsh_owner" != "$builddir_owner"; then say_color error 'Build directory owner and pdsh binary owner are different' say_color error 'The testsuite will not work in this configuration' exit 1 fi # # Set loaded modules as prereqs # for mod in $(pdsh -L 2>&1 | \ sed -n 's/^Module: *\(.*\)\/\(.*\)/\1_\2/p' | \ tr a-z A-Z); do test_set_prereq MOD_${mod} done if [ -n "$PDSH_TEST_LONG" ]; then test_set_prereq LONGTESTS fi if pdsh -V | head -1 | grep -qv +static-modules; then test_set_prereq DYNAMIC_MODULES fi pdsh-2.36/tests/t0001-basic.sh0000775€^–Á €^–Á 0000001250515131211226024035 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='test framework and pdsh basics' if ! test -x ../src/pdsh/pdsh; then echo >&2 'Failed to find pdsh binary. Please run make.' exit 1 fi . $(dirname $0)/test-lib.sh ########################################################################### # # Tests of the framework. From git teststuite: # test_expect_success 'working success' ':' test_expect_failure 'pretend known breakage' ' false ' test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' " mkdir passing-todo && (cd passing-todo && cat >passing-todo.sh <out 2>err && sed -e 's/^> //' >expect < ok 1 - pretend we have fixed a known breakage # TODO known breakage > # fixed 1 known breakage(s) > # passed all 1 test(s) > 1..1 EOF test_cmp expect out) " test_set_prereq HAVEIT haveit=no test_expect_success HAVEIT 'test runs if prerequisite is satisfied' ' test_have_prereq HAVEIT && haveit=yes ' clean=no test_expect_success 'tests clean up after themselves' ' test_when_finished clean=yes ' if test $clean != yes then say "bug in test framework: basic cleanup command does not work reliably" exit 1 fi test_expect_success 'tests clean up even on failures' " mkdir failing-cleanup && (cd failing-cleanup && cat >failing-cleanup.sh <out 2>err && ! test -f \"trash directory.failing-cleanup/clean-after-failure\" && sed -e 's/Z$//' -e 's/^> //' >expect <<\EOF && > not ok - 1 tests clean up even after a failure > # Z > # touch clean-after-failure && > # test_when_finished rm clean-after-failure && > # (exit 1) > # Z > not ok - 2 failure to clean up causes the test to fail > # Z > # test_when_finished \"(exit 2)\" > # Z > # failed 2 among 2 test(s) > 1..2 EOF test_cmp expect out) " ########################################################################### # # Basic pdsh functionality # test_expect_success 'pdsh runs' ' pdsh -w foo -q | tail -1 | grep foo ' test_expect_success 'pdsh -V works' ' pdsh -V 2>&1 | grep -q ^pdsh ' test_expect_success 'pdsh -L works' ' pdsh -L 2>&1 | grep -q ^Module: ' test_expect_success 'pdsh -h works' ' pdsh -h 2>&1 | grep Usage: ' test_expect_success 'rcmd/exec module is built' ' test_have_prereq MOD_RCMD_EXEC && havit=yes ' test_expect_success 'pdsh -N option works' ' O1=$(pdsh -Rexec -w foo0 echo foo | sed "s/foo0: //") O2=$(pdsh -NRexec -w foo0 echo foo) if ! test "$O1" = "$O2"; then say_color error "Error: -N output \"$O1\" != \"$O2\"" false fi ' test_expect_success LONGTESTS '-u option is functional' ' run_timeout 5 pdsh -wfoo -Rexec -u 1 sleep 10 2>&1 \ | grep -i "command timeout" ' check_pdsh_option() { flag=$1; name=$2; value=$3; flagval=$value if test "$value" = "Yes" -o "$value" = "No"; then flagval="" fi echo "flag=$flag name='$name' value=$value flagval=$flagval" pdsh -$flag$flagval -w foo -q | grep -q "$name[ ]*$value$" } check_pdsh_env_variable() { env_var=$1; name=$2; env_var_val=$3; echo "env_var=$env_var name='$name' env_var_val=$env_var_val" env $env_var=$env_var_val pdsh -w foo -q | grep -q "$name[ ]*$env_var_val$" } test_expect_success '-f sets fanout' ' check_pdsh_option f Fanout 8 ' test_expect_success '-l sets remote username' ' check_pdsh_option l "Remote username" foouser ' test_expect_success 'too long username fails gracefully' ' i=0 u="X" while [ $i -lt 512 ]; do u="${u}X" i=$((i+1)) done pdsh -wfoo -l${u} -q 2>&1 | grep "exceeds max username length" ' test_expect_success '-t sets connect timeout' ' check_pdsh_option t "Connect timeout (secs)" 33 ' test_expect_success 'env PDSH_CONNECT_TIMEOUT sets connect timeout' ' check_pdsh_env_variable PDSH_CONNECT_TIMEOUT "Connect timeout (secs)" 33 ' test_expect_success '-u sets command timeout' ' check_pdsh_option u "Command timeout (secs)" 22 ' test_expect_success 'env PDSH_COMMAND_TIMEOUT sets command timeout' ' check_pdsh_env_variable PDSH_COMMAND_TIMEOUT "Command timeout (secs)" 22 ' test_expect_success 'command timeout 0 by default' ' pdsh -w foo -q | grep -q "Command timeout (secs)[ ]*0$" ' test_expect_success '-b enables batch mode' ' check_pdsh_option b "one \^C will kill pdsh" Yes ' test_expect_success 'pdsh -N option works' ' O1=$(pdsh -Rexec -w foo0 echo foo | sed "s/foo0: //") O2=$(pdsh -NRexec -w foo0 echo foo) if ! test "$O1" = "$O2"; then say_color error "Error: -N output \"$O1\" != \"$O2\"" false fi ' test_done pdsh-2.36/tests/t2000-exec.sh0000775€^–Á €^–Á 0000000172415131211226023702 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh exec module tests' . ${srcdir:-.}/test-lib.sh if ! test_have_prereq MOD_RCMD_EXEC; then skip_all='skipping exec tests, exec module not available' test_done fi test_expect_success 'exec module works' ' OUTPUT=$(pdsh -Rexec -w foo echo test_command) test "$OUTPUT" = "foo: test_command" ' test_debug ' echo Output: $OUTPUT ' test_expect_success 'exec cmd substitution works ' ' OUTPUT=$(pdsh -Rexec -w foo -l buser echo %u %h %n %%) test "$OUTPUT" = "foo: buser foo 0 %" ' test_debug ' echo Output: $OUTPUT ' test_expect_success 'exec module works in interactive mode' ' OUTPUT=$(echo echo test command | pdsh -R exec -w foo) echo "$OUTPUT" | grep "foo: test command" ' test_debug ' echo Output: $OUTPUT ' test_expect_success 'exec cmd susbstitution works interactive mode' ' OUTPUT=$(echo echo %u %h %n %% | pdsh -R exec -w foo -l auser) echo "$OUTPUT" | grep "foo: auser foo 0 %" ' test_debug ' echo Output: $OUTPUT ' test_done pdsh-2.36/tests/README0000664€^–Á €^–Á 0000000522215131211226022531 0ustar arif.ali@canonical.comarif.ali@canonical.comPdsh Tests ---------- This directory contains many basic test scripts for pdsh and its modules. The test framework is borrowed from the core Git testsuite, and thus shares many of the Git test features, including output in the Test Anything Protocol (TAP, testanything.org) format. Running Tests ------------- Tests may be run in 3 different ways, the easiest of which is by issuing "make check" from this directory or the top level pdsh build directory. The tests may also all be invoked via ./runtests.sh. Finally, since the tests output TAP, the tests may be run with any TAP harness, most notably the prove(1) command, e.g. prove --timer -j 4 t[0-9]*.sh [15:47:54] t0002-internal.sh ........ ok 79 ms [15:47:54] t0101-dshgroup.sh ........ ok 263 ms [15:47:54] t1000-dshbak.sh .......... ok 365 ms [15:47:54] t0004-module-loading.sh .. ok 329 ms [15:47:54] t0003-wcoll.sh ........... ok 569 ms [15:47:54] t0001-basic.sh ........... ok 676 ms [15:47:54] All tests successful. Files=8, Tests=55, 1 wallclock secs ( 0.13 usr 0.05 sys + 0.75 cusr 1.64 csys = 2.57 CPU) Result: PASS Test scripts may be run individually as well, e.g. ./t0003-wcoll.sh ok 1 - hostname range expansion works ok 2 - host range expansion does not strip leading zeros ok 3 - host range expansion handles mixed size suffixes ... ok 11 - WCOLL environment variable works ok 12 - ranges can be embedded in wcoll files # passed all 12 test(s) 1..12 Tests also support a standard set of options: -v, --verbose Print commands being run and their output -d, --debug Enable any test debugging -i, --immediate Fail immediately on first failed test -l, --long-tests Run additional long-running tests --tee Also write test output to test-results/$TEST_NAME.out --root=dir Create 'trash' directories under "dir" Skipping Tests -------------- The environment variable PDSH_SKIP_TESTS is a space separated list of patterns that tells which tests to skip, and can either match the test number t[0-9]{4} to skip an entire test script, or have an appended .$number to skip a particular test in a test script. Test Naming ------------- The test files are by convention named tNNNN-.sh where N is a decimal digit. For pdsh, only the first digit has meaning, where so far the only digits used are: 0 - basic pdsh functionality tests 1 - pdsh misc module tests 2 - pdsh rcmd module tests 5 - other tools tests (e.g. dshbak) 6 - pdsh regression tests (that don't fit in other categories) Obviously, tests are run in numerical order, so if one test depends on another it should be numbered higher. pdsh-2.36/tests/t0005-rcmd_type-and-user.sh0000775€^–Á €^–Á 0000000140615131211226026460 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh rcmd_type and remote user handling' . ${srcdir:-.}/test-lib.sh test_expect_success 'pdsh -l sets username for all hosts' ' pdsh -S -Rexec -lbar -w foo[1-100] test "%u" = bar ' test_expect_success 'Can set remote username via user@hosts' ' pdsh -S -Rexec -w bar@foo[1-100] test "%u" = bar ' test_expect_success 'user@hosts works for a subset of hosts' ' pdsh -S -Rexec -w u1@foo,u2@bar sh -c \ "if test %h = foo; then test %u = u1; else test %u = u2; fi" ' test_expect_success 'Can set rcmd_type via rcmd_type:hosts' ' PDSH_RCMD_TYPE=ssh pdsh -S -w exec:foo[1-10] true ' test_expect_success 'Can set rcmd_type and user via rcmd_type:user@hosts' ' PDSH_RCMD_TYPE=ssh pdsh -S -w exec:bar@foo[1-10] test "%u" = bar ' test_done pdsh-2.36/tests/t1003-slurm.sh0000775€^–Á €^–Á 0000000760515131211226024126 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh # # Run tests of the SLURM module if slurm is available and there are # any currently running jobs, or if we can start a job. # test_description='slurm module' . ${srcdir:-.}/test-lib.sh if ! test_have_prereq MOD_MISC_SLURM; then skip_all='skipping slurm tests, slurm module not available' test_done fi if ! squeue >/dev/null 2>&1; then skip_all='skipping slurm tests, slurm install not available' test_done fi export KILLJOBIDS="" # # Create a batch job and return the jobid or FAILED on stdout # create_batch_job() { ID=$(printf '#!/bin/sh\nsleep 100\n'|sbatch "$@" |sed 's/Submitted batch job //') count=0 while test "$(squeue -j $ID -ho %t)" != "R" && $count -lt 30; do sleep 1; $((count=count+1)) done if test "$count" -ge 30; then echo FAILED else KILLJOBIDS="$KILLJOBIDS $JOBID" echo $ID fi } # # Ensure slurm module is loaded (i.e. same as -M genders) # export PDSH_MISC_MODULES=slurm JOBIDS=$(squeue -ho %i -trunning) if [ -n "$JOBIDS" ]; then # # There are already running jobs we can use for testing # JOBID=$(echo $JOBIDS | tr ' ' '\n' | head -1) else # # Need to create our own job # (Only run if long tests were requested) # if ! test_have_prereq LONGTESTS; then skip_all='skipping slurm tests, run with --long or PDSH_TEST_LONG' test_done fi echo "Attempting to initiate slurm job" >&2 JOBID=$(create_batch_job -N2) if test "$JOBID" = "FAILED"; then skip_all='skipping slurm tests, unable to run a job' test_done fi fi # # Capture the nodes in job JOBID # NODES=$(squeue -ho %N -j $JOBID) test_expect_success 'slurm -j option works' ' O=$(pdsh -j$JOBID -q | tail -1) if test "x$O" != "x$NODES"; then say_color error "Error: pdsh -j$JOBID selected nodes $O expected $NODES" squeue -hj $JOBID false fi ' test_expect_success 'slurm module reads SLURM_JOBID if no wcoll set' ' O=$(SLURM_JOBID=$JOBID pdsh -q | tail -1) if test "x$O" != "x$NODES"; then say_color error "Error: pdsh -j$JOBID selected nodes $O expected $NODES" squeue -hj $JOBID false fi ' test_expect_success 'slurm -j all option works' ' O1=$(pdsh -j all -q | tail -1) O2=$(pdsh -j$(squeue -ho %i -trunning | tr " \n" ,,) -q | tail -1) if ! test "$O1" = "$O2"; then say_color error "Error: pdsh -j all failed to select all allocated nodes" say_color error "a: $O1" say_color error "b: $O2" false fi ' test_expect_success LONGTESTS 'slurm -j all does not select completed jobs' ' jobid=$(create_batch_job -N1) && test "$jobid" != "FAILED" && node=$(squeue -ho%N -j $jobid) && scancel $jobid && while test "$(squeue -j "$jobid" -ho %t)" = "CG"; do sleep 0.2; done if pdsh -j all -Q | tail -1 | tr , "\n" | grep "^$node$"; then if test "$(squeue -trunning -n$node -ho%t)" != "R"; then say_color error "pdsh -j all selected node $node from completed job" false fi fi ' test_expect_success 'slurm -j option handles illegal jobid gracefully' ' pdsh -j garbage 2>&1 | grep -q "invalid setting" ' test_expect_success 'slurm -P option works' ' part=$(sinfo -ho %P | head -1) O1=$(sinfo -ho %N -p $part) O2=$(pdsh -P $part -q | tail -1) if test "x$O1" != "x$O2"; then say_color error "Error: pdsh -P $part selected nodes $O2 expected $O1" false fi ' test_expect_success 'slurm -P works with -w' ' part=$(sinfo -ho %P | head -1) O1=$(sinfo -ho %N -p $part) O2=$(pdsh -P $part -w foo,bar -q | tail -1) if test "foo,bar,x$O1" != "x$O2"; then say_color error "Error: pdsh -P $part -w foo,bar got $02 expected foo,bar,$O1" fi ' test_expect_success 'slurm -C filters out nodes' ' part=$(sinfo -ho %P | head -1) if pdsh -P $part -C featurethathopefullydoesntexist -q ; then say_color error "Error: pdsh -P $part -C featurethathopefullydoesntexist resulted in hosts" fi ' # # Clean up: # echo "$KILLJOBIDS" test -n "$KILLJOBIDS" && scancel $KILLJOBIDS test_done pdsh-2.36/tests/t2002-mrsh.sh0000775€^–Á €^–Á 0000000136415131211226023731 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh mrsh module tests' . ${srcdir:-.}/test-lib.sh if ! test_have_prereq MOD_RCMD_MRSH; then skip_all='skipping mrsh tests, mrsh module not available' test_done fi if ! pdsh -SRmrsh -w localhost /bin/true 2>&1 >/dev/null; then skip_all='skipping mrsh tests, mrsh server not available on localhost' test_done fi test_expect_success 'mrsh module runs' ' OUTPUT=$(pdsh -Rmrsh -w localhost echo i am here) ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'mrsh localhost works' ' echo "$OUTPUT" | grep "localhost: i am here" ' test_expect_success 'mrsh: -S generates empty lines (Issue 54)' ' OUTPUT=$(pdsh -Rmrsh -w localhost -S cd ..) [ -z "$OUTPUT" ] ' test_debug ' echo Output: "$OUTPUT" ' test_done pdsh-2.36/tests/t2001-ssh.sh0000775€^–Á €^–Á 0000001536415131211226023561 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh ssh module tests' . ${srcdir:-.}/test-lib.sh if ! test_have_prereq MOD_RCMD_SSH; then skip_all='skipping ssh tests, ssh module not available' test_done fi # # Create ssh wrapper script that echoes its own arguments. This allows # the following tests to run without ssh installed # test_expect_success 'create ssh dummy script' ' echo "#!$SHELL_PATH" >ssh && echo "echo \"\$@\"" >>ssh && chmod 755 ssh ' export PATH=.:$PATH unset PDSH_SSH_ARGS PDSH_SSH_ARGS_APPEND test_expect_success 'ssh module runs' ' OUTPUT=$(pdsh -Rssh -w foo command) ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'ssh dummy script works' ' echo "$OUTPUT" | grep "foo: .*foo command" ' test_expect_success 'ssh works with DSHPATH' ' OUTPUT=$(DSHPATH=/test/path pdsh -Rssh -w foo command) && echo "$OUTPUT" | grep "foo: .*foo PATH=/test/path; *command" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'have ssh connect timeout option' ' SSH_CONNECT_TIMEOUT_OPTION=$(sed -ne "s/#define SSH_CONNECT_TIMEOUT_OPTION \"\(.*\)\"/\1/p" ${PDSH_BUILD_DIR}/config.h) && test -n "$SSH_CONNECT_TIMEOUT_OPTION" ' test_debug ' echo "SSH_CONNECT_TIMEOUT_OPTION=\"$SSH_CONNECT_TIMEOUT_OPTION\"" ' test_expect_success 'ssh works with connect timeout' ' OPT=$(printf -- "$SSH_CONNECT_TIMEOUT_OPTION" 55) && OUTPUT=$(pdsh -t55 -Rssh -w foo command) && echo "$OUTPUT" | grep -- "$OPT" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'ssh and pdcp work together' ' ln -s $PDSH_BUILD_DIR/src/pdsh/pdsh pdcp && touch jnk && OUTPUT=$(pdcp -Rssh -w foo jnk /tmp 2>&1 || :) && echo "$OUTPUT" | grep "pdcp -z /tmp" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS works' ' OUTPUT=$(PDSH_SSH_ARGS="-p 922 -l%u %h" pdsh -luser -Rssh -wfoo hostname) && echo "$OUTPUT" | grep "[-]p 922 -luser foo hostname" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS does not require %h' ' OUTPUT=$(PDSH_SSH_ARGS="-p 888 -l%u" pdsh -luser -Rssh -wfoo hostname) echo "$OUTPUT" | grep "[-]p 888 -luser foo hostname" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS does not require %u' ' OUTPUT=$(PDSH_SSH_ARGS="-p 888 %h" pdsh -ltestuser -Rssh -wfoo hostname) echo "$OUTPUT" | grep "[-]ltestuser" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS does not force %u when ruser not set (Issue 39)' ' OUTPUT=$(PDSH_SSH_ARGS="-p 888 %h" pdsh -Rssh -wfoo hostname) echo "$OUTPUT" | grep "[-]l" test $? -ne 0 ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'ssh does not set -l%u by default (Issue 40)' ' OUTPUT=$(pdsh -Rssh -wfoo hostname) echo "$OUTPUT" | grep "[-]l" test $? -ne 0 ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'ssh sets -l%u when ruser != luser' ' OUTPUT=$(pdsh -Rssh -wfoo,testuser@bar hostname | grep bar) && echo "$OUTPUT" | grep "[-]ltestuser" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'ssh does not set -l%u when ruser == luser (Issue 40)' ' OUTPUT=$(pdsh -Rssh -wfoo,testuser@bar hostname | grep foo) && echo "$OUTPUT" | grep "[-]l" test $? -ne 0 ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS without %u inserts %u before %h' ' OUTPUT=$(PDSH_SSH_ARGS="-p 888 %h" pdsh -ltestuser -Rssh -wfoo hostname) echo "$OUTPUT" | grep "[-]p 888 -ltestuser foo hostname" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS does not require %u or %h' ' OUTPUT=$(PDSH_SSH_ARGS="-p 777" pdsh -ltestuser -Rssh -wfoo hostname) echo "$OUTPUT" | grep "[-]p 777 -ltestuser foo hostname" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS_APPEND works' ' OUTPUT=$(PDSH_SSH_ARGS_APPEND="-p 922" pdsh -Rssh -wfoo hostname) && echo "$OUTPUT" | grep "[-]p 922" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'PDSH_SSH_ARGS_APPEND are added to PDSH_SSH_ARGS' ' OUTPUT=$(PDSH_SSH_ARGS_APPEND="-p 922" PDSH_SSH_ARGS="-x -a -l%u %h" \ pdsh -Rssh -lfoouser -wfoo hostname) && echo "$OUTPUT" | grep "[-]p 922 -x -a -lfoouser foo hostname" ' test_debug ' echo Output: "$OUTPUT" ' test_expect_success 'interactive mode works with ssh (Issue 14)' ' OUTPUT=$(echo test command line | pdsh -Rssh -wfoo) echo "$OUTPUT" | grep "test command line" ' test_debug ' echo Output: "$OUTPUT" ' # # Exit code tests: # # If adding new general tests for ssh module, place above here, # as the ssh dummy script is rewritten for exit code specific testing. # test_expect_success 'create ssh dummy script for exit code testing' ' echo "#!$SHELL_PATH" >ssh echo "# Usage: $0 -n -i -e " >>ssh echo "while getopts \":n:i:e:l:\" opt; do " >>ssh echo " case \$opt in" >>ssh echo " n) RANK=\$OPTARG ;;" >>ssh echo " i) FAILRANK=\$OPTARG ;;" >>ssh echo " e) EXITCODE=\$OPTARG ;;" >>ssh echo " l) ;;" >>ssh echo " esac" >>ssh echo "done" >>ssh echo "" >>ssh echo "if test \$RANK -eq \$FAILRANK; then exit \$EXITCODE; fi" >>ssh echo "exit 0" >>ssh chmod 755 ssh ' test_expect_success 'ssh dummy script is functional' ' TEST_EXIT_CODE=$(random 254) echo "$TEST_EXIT_CODE" ssh -n 1 -i 0 && test_expect_code "$TEST_EXIT_CODE" ssh -lfoo -n 1 -i 1 -e $TEST_EXIT_CODE test_expect_code 0 ssh -lxxx -n0 -i255 -e $TEST_EXIT_CODE ' test_expect_success 'ssh works with pdsh -S' ' TEST_EXIT_CODE=$(random 254) && export PDSH_SSH_ARGS="-n%n -i0 -e$TEST_EXIT_CODE" test_expect_code "$TEST_EXIT_CODE" pdsh -Rssh -S -w foo0 command ' unset PDSH_SSH_ARGS test_expect_success 'ssh works with pdsh -S and multiple targets' ' for n in $(seq 1 24); do TEST_EXIT_CODE=$(random 254) && FAILING_RANK=$(random $n) && export PDSH_SSH_ARGS="-n%n -i$FAILING_RANK -e$TEST_EXIT_CODE" test_expect_code "$TEST_EXIT_CODE" pdsh -Rssh -S -wfoo[0-$n] command done ' unset PDSH_SSH_ARGS test_expect_success 'ssh works with pdsh -k' ' TEST_EXIT_CODE=$(random 254) && export PDSH_SSH_ARGS="-n%n -i0 -e$TEST_EXIT_CODE" test_expect_code "1" pdsh -Rssh -k -w foo0 command ' unset PDSH_SSH_ARGS test_expect_success 'ssh works with pdsh -k and multiple targets' ' for n in $(seq 1 24); do TEST_EXIT_CODE=$(random 254) && FAILING_RANK=$(random $n) && export PDSH_SSH_ARGS="-n%n -i$FAILING_RANK -e$TEST_EXIT_CODE" test_expect_code "1" pdsh -Rssh -k -wfoo[0-$n] command done ' unset PDSH_SSH_ARGS test_done pdsh-2.36/tests/t6036-long-output-lines.sh0000775€^–Á €^–Á 0000000151015131211226026371 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='Issue 36: pdsh truncates long lines Test that pdsh does not truncate very long lines' . ${srcdir:-.}/test-lib.sh if which base64 >/dev/null; then base64="base64" elif which openssl >/dev/null; then base64="openssl base64" else skip_all 'failed to find base64 program' fi test_expect_success 'pdsh does not truncate very long lines' " dd if=/dev/urandom bs=1024 count=100 | $base64 | tr -d '\n' | fold -w8000 > testfile && echo >>testfile && pdsh -w foo -N -Rexec cat testfile > output && test_cmp testfile output " test_expect_success 'pdsh does not truncate even longer lines' " dd if=/dev/urandom bs=1024 count=100 | $base64 | tr -d '\n' | fold -w80000 > testfile2 && echo >>testfile2 && pdsh -w foo -N -Rexec cat testfile2 > output2 && test_cmp testfile2 output2 " test_done pdsh-2.36/tests/aggregate-results.sh0000775€^–Á €^–Á 0000000274215131211226025641 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh fixed=0 success=0 failed=0 broken=0 total=0 for file do while read type value do case $type in '') continue ;; fixed) fixed=$(($fixed + $value)) ;; success) success=$(($success + $value)) ;; failed) failed=$(($failed + $value)) ;; broken) broken=$(($broken + $value)) ;; total) total=$(($total + $value)) ;; esac done <"$file" done pluralize () { case $2 in 1) case $1 in test) echo test ;; failure) echo failure ;; esac ;; *) case $1 in test) echo tests ;; failure) echo failures ;; esac ;; esac } echo "pdsh test suite complete." if [ "$fixed" = "0" ] && [ "$failed" = "0" ]; then tests=$(pluralize "test" $total) printf "All $total $tests " if [ "$broken" = "0" ]; then echo "passed." else failures=$(pluralize "failure" $broken) echo "behaved as expected ($broken expected $failures)." fi; else echo "$success/$total tests passed." if [ "$broken" != "0" ]; then tests=$(pluralize "test" $broken) echo "$broken broken $tests failed as expected." fi if [ "$fixed" != "0" ]; then tests=$(pluralize "test" $fixed) echo "$fixed broken $tests now fixed." fi if [ "$failed" != "0" ]; then tests=$(pluralize "test" $failed) echo "$failed $tests failed." fi fi skipped=$(($total - $fixed - $success - $failed - $broken)) if [ "$skipped" != "0" ]; then tests=$(pluralize "test" $skipped) echo "$skipped $tests skipped." fi pdsh-2.36/tests/runtests.sh0000775€^–Á €^–Á 0000000104215131211226024073 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh rm -rf test-results trash-directory* SHELL_PATH="/bin/sh" uname_s=$(uname -s) case "${uname_s}" in AIX) # AIX /bin/sh is not functional with the testsuite SHELL_PATH=$(which bash) if [ -z "$SHELL_PATH" ]; then echo "This is AIX and I can't find bash. HELP!" fi GIT_TEST_CMP="cmp" export GIT_TEST_CMP SHELL_PATH ;; esac code=0 for test in t[0-9]*.sh; do echo "*** $test ***" $SHELL_PATH ./$test if [ $? -ne 0 ]; then code=1 fi done $SHELL_PATH aggregate-results.sh test-results/* exit $code pdsh-2.36/tests/t0003-wcoll.sh0000775€^–Á €^–Á 0000001110715131211226024073 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh wcoll (working collective) argument processing' . ${srcdir:-.}/test-lib.sh # # Wrapper to check last line of output from pdsh -Qw # test_pdsh_wcoll [] # test_pdsh_wcoll() { OUTPUT=$(pdsh -Qw $1 $3 | tail -1) test_output_is_expected "$OUTPUT" "$2" } test_expect_success 'hostname range expansion works' ' test_pdsh_wcoll "foo[0-3]" "foo0,foo1,foo2,foo3" ' test_expect_success 'host range expansion does not strip leading zeros' ' test_pdsh_wcoll "foo[00-02]" "foo00,foo01,foo02" ' test_expect_success 'host range expansion handles mixed size suffixes' ' test_pdsh_wcoll "foo[9-11]" "foo9,foo10,foo11" ' test_expect_success 'host range expansion works with "," embedded in range' ' test_pdsh_wcoll "foo[0-2,4]" "foo0,foo1,foo2,foo4" ' test_expect_success 'host range expansion works with 2 sets of brackets' ' test_pdsh_wcoll "foo[1-2]-[0-3]" \ "foo1-0,foo1-1,foo1-2,foo1-3,foo2-0,foo2-1,foo2-2,foo2-3" ' test_expect_success 'pdsh -x option works' ' test_pdsh_wcoll "foo[9-11]" "foo9,foo11" "-x foo10" ' test_expect_success 'pdsh -x option works with ranges' ' test_pdsh_wcoll "foo[0-5]" "foo0,foo4,foo5" "-x foo[1-3]" ' test_expect_success 'pdsh -x option works with ranges (gnats:118)' ' test_pdsh_wcoll "foo[0-5]" "foo0,foo4,foo5" "-x foo[1-3]" ' test_expect_success 'pdsh -x option works with non-numeric suffix (gnats:120)' ' test_pdsh_wcoll "fooi,fooj,foo[0-5]" \ "foo0,foo1,foo3,foo4,foo5" \ "-x fooj,fooi,foo2" ' test_expect_success 'pdsh -w- reads from stdin' ' echo "foo1,foo2,foo3" | test_pdsh_wcoll "-" "foo1,foo2,foo3" ' test_expect_success 'pdsh -w- can be used with other -w args' ' echo "foo1,foo2,foo3" | test_pdsh_wcoll "-" "foo1,foo2,foo3,foo4" "-wfoo4" ' cat >wcoll <wcoll <A <B </dev/null && pdsh -w "^nosuchfile" -q 2>&1 | grep -q "nosuchfile: No such file" ' test_expect_success 'host exclusion with "-" works' ' test_pdsh_wcoll "foo[9-11],-foo10" "foo9,foo11" ' test_expect_success 'regex filtering works' ' test_pdsh_wcoll "foo[0-20],/0$/" "foo0,foo10,foo20" ' test_expect_success 'regex exclusion works' ' test_pdsh_wcoll "foo[0-20],-/[1-9]$/" "foo0,foo10,foo20" ' test_expect_success 'regex exclusion works from -x' ' test_pdsh_wcoll "foo[0-20]" "foo0,foo10,foo20" "-x/[1-9]$/" ' test_expect_success 'multiple -w options' ' test_pdsh_wcoll "foo[0-20]" "foo0,foo10,foo20" "-w-/[1-9]$/" && test_pdsh_wcoll "foo8" "foo8,foo9,foo10,foo11,foo12" "-w^wcoll" && test_pdsh_wcoll "foo1,bar1" "foo1" "-w/^foo/" ' cat >A <B <testdir/A <testdir/B <testdir/A <testdir/B <testdir/C <&1 | grep -q warning ' test_expect_success 'wcoll match #include exactly' ' test_pdsh_wcoll "^testdir/B" "foo10" && pdsh -w^testdir/B -q 2>&1 | grep -q warning ' test_expect_success 'wcoll: #include alone fails' ' test_pdsh_wcoll "^testdir/C" "foo11" && pdsh -w^testdir/C -q 2>&1 | grep -q warning ' test_done pdsh-2.36/tests/t5000-dshbak.sh0000775€^–Á €^–Á 0000001034415131211226024213 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='dshbak functionality' . ${srcdir:-.}/test-lib.sh dshbak_test() { printf "$1\n" | dshbak -c > output cat output \ | while read line ; do test "$line" = "$2" && echo ok done | grep -q ok || (cat output >&2 && /bin/false) } dshbak_test_notok() { touch ok printf "$1\n" \ | dshbak -c \ | while read line ; do test "$line" = "$2" && rm ok done rm ok && : } test_expect_success 'dshbak functionality' ' cat >input <output <output2 && diff output output2 && rm input output* ' test_expect_success 'dshbak -c does not coalesce different length output' ' dshbak_test_notok " foo1: bar foo2: bar foo1: baz" "foo[1-2]" ' test_expect_success 'dshbak -c properly compresses multi-digit suffixes' ' dshbak_test " foo8: bar foo9: bar foo10: bar foo11: bar" "foo[8-11]" ' test_expect_success 'dshbak -c properly compresses prefix with embedded numerals' ' dshbak_test " foo1x8: bar foo1x9: bar foo1x10: bar foo1x11: bar" "foo1x[8-11]" ' test_expect_success 'dshbak -c does not strip leading zeros' ' dshbak_test " foo01: bar foo03: bar foo02: bar foo00: bar" "foo[00-03]" ' test_expect_success 'dshbak -c does not coalesce different zero padding' ' dshbak_test " foo0: bar foo03: bar foo01: bar foo2: bar" "foo[0,01,2,03]" ' test_expect_success 'dshbak -c properly coalesces zero padding of "00"' ' dshbak_test " foo1: bar foo01: bar foo02: bar foo3: bar foo5: bar foo00: bar" "foo[00-02,1,3,5]" ' test_expect_success 'dshbak -c can detect suffixes' ' dshbak_test " foo1s: bar foo01s: bar foo02s: bar foo3s: bar foo5s: bar foo00s: bar" "foo[00-02,1,3,5]s" ' test_expect_failure 'dshbak -c can detect suffix with numeral' ' dshbak_test " foo1s0: bar foo01s0: bar foo02s0: bar foo3s0: bar foo5s0: bar foo00s0: bar" "foo[00-02,1,3,5]s0" ' test_expect_success 'issue 19: missing commas in dshbak header output' ' dshbak_test " foo1: bar foo2: bar foo5: bar bar0: bar bar1: bar" "bar[0-1],foo[1-2,5]" ' test_expect_success 'dshbak properly joins 9,10' ' dshbak_test " foo1: bar foo2: bar foo3: bar foo4: bar foo5: bar foo6: bar foo7: bar foo8: bar foo9: bar foo10: bar foo11: bar" "foo[1-11]" ' test_expect_success 'issue 33: dshbak does not coalesce 09,10' ' dshbak_test " foo01: bar foo02: bar foo03: bar foo04: bar foo05: bar foo06: bar foo07: bar foo08: bar foo09: bar foo10: bar foo11: bar" "foo[01-11]" ' test_expect_success 'issue 33: dshbak does not coalesce 099,100' ' dshbak_test " foo090: bar foo091: bar foo092: bar foo093: bar foo094: bar foo095: bar foo096: bar foo097: bar foo098: bar foo099: bar foo100: bar foo101: bar" "foo[090-101]" ' cat >test_input <&1 | grep "Option -f may only be used with -d" ' test_expect_success 'dshbak -d fails when output dir does not exist' ' dshbak -d does_not_exist &1 | \ grep "Output directory does_not_exist does not exist" ' test_expect_success SANITY 'dshbak -d fails gracefully for non-writable dir' ' mkdir test_output && chmod 500 test_output && printf "foo0: bar\n" | dshbak -d test_output 2>&1 | tee logfile | \ grep "Failed to open output file" && rm -rf test_output logfile || : ' test_expect_success 'Issue 70: dshbak fails on hostname of 0' ' dshbak_test " 0: foo 1: foo 2: foo 0: bar 1: bar 2: bar " "[0-2]" ' test_expect_success 'Issue 132: dshbak handles empty input' ' echo "" | dshbak -c >empty.output 2>&1 && touch empty.expected && test_cmp empty.expected empty.output ' test_done pdsh-2.36/tests/t0002-internal.sh0000775€^–Á €^–Á 0000000034715131211226024572 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh internal testcases Run pdsh internal testsuite' . ${srcdir:-.}/test-lib.sh test_expect_success 'working xstrerrorcat' ' pdsh -T0 ' test_expect_success 'working pipecmd' ' pdsh -T1 ' test_done pdsh-2.36/tests/t1001-genders.sh0000775€^–Á €^–Á 0000002113615131211226024404 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/bash test_description='genders module' . ${srcdir:-.}/test-lib.sh if ! test_have_prereq MOD_MISC_GENDERS; then skip_all='skipping genders tests, genders module not available' test_done fi # # Ensure genders module is loaded (i.e. same as -M genders) # export PDSH_MISC_MODULES=genders cat >genders <genders.A <genders.B <&1 | \ grep -q "no remote hosts specified" ' test_expect_success 'pdsh -X option works' ' OUTPUT=$(pdsh -F genders.A -AX login -q | tail -1) test_output_is_expected "$OUTPUT" "n[1-10]" ' test_expect_success 'pdsh -g option supports var=val' ' OUTPUT=$(pdsh -F genders.A -g os=debian -q | tail -1) test_output_is_expected "$OUTPUT" "n[1-5]" ' test_expect_success 'pdsh -X option supports var=val' ' OUTPUT=$(pdsh -F genders.A -AX os=debian -q | tail -1) test_output_is_expected "$OUTPUT" "n[0,6-10]" ' test_expect_success 'pdsh -g option supports genders_query' ' OUTPUT=$(pdsh -F genders.A -g "os=debian&&foo" -q | tail -1) test_output_is_expected "$OUTPUT" "n[4-5]" ' test_expect_success 'pdsh -X option supports genders_query' ' OUTPUT=$(pdsh -F genders.A -AX "os=fedora&&foo" -q | tail -1) test_output_is_expected "$OUTPUT" "n[0-5,8-10]" ' test_expect_success 'pdsh -x excludes hosts selected by genders' ' OUTPUT=$(pdsh -F genders.A -AX "os=fedora&&foo" -x n5 -q | tail -1) test_output_is_expected "$OUTPUT" "n[0-4,8-10]" ' test_expect_success 'pdsh -w -host excludes hosts selected by genders' ' OUTPUT=$(pdsh -F genders.A -A -w -n5 -q | tail -1) test_output_is_expected "$OUTPUT" "n[0-4,6-10]" ' test_expect_success 'pdsh -w /regex/ filters hosts selected by genders' ' OUTPUT=$(pdsh -F genders.A -A -w /n.*0$/ -q | tail -1) test_output_is_expected "$OUTPUT" "n[0,10]" ' test_expect_success 'pdsh -w -/regex/ filters hosts selected by genders' ' OUTPUT=$(pdsh -F genders.A -A -w -/n.*0$/ -q | tail -1) test_output_is_expected "$OUTPUT" "n[1-9]" ' cat >genders.C <&1 | grep -q error; then say_color error "Error: Missing genders file causes error" false fi ' test_expect_success 'missing genders file with -F is an error' ' if !pdsh -Fdoesnotexist -w host[0-10] -q 2>&1 | grep -q error; then say_color error "Error: Missing genders file with -F doesnt cause error" false fi ' cat >genders.issue55 <genders.is.slow <genders.torture <: no remote hosts specified' 'pdsh@: no remote hosts specified' 'pdsh@: no remote hosts specified' 'pdsh@: no remote hosts specified' 'foo[1-1000]' 'foo[1-199,301-1000]' 'foo[1-49,101-1000]' 'foo[1-49,101-199,301-1000]' 'foo[31-2000]' 'foo[31-199,301-2000]' 'foo[31-49,101-2000]' 'foo[31-49,101-199,301-2000]' 'foo[31-1000]' 'foo[31-199,301-1000]' 'foo[31-49,101-1000]' 'foo[31-49,101-199,301-1000]' 'foo[1-699,701-1000]' 'foo[1-199,301-699,701-1000]' 'foo[1-49,101-699,701-1000]' 'foo[1-49,101-199,301-699,701-1000]' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'foo[31-699,701-2000]' 'foo[31-199,301-699,701-2000]' 'foo[31-49,101-699,701-2000]' 'foo[31-49,101-199,301-699,701-2000]' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'foo[1-1000]' 'foo[1-199,301-1000]' 'foo[1-49,101-1000]' 'foo[1-49,101-199,301-1000]' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'foo[31-2000]' 'foo[31-199,301-2000]' 'foo[31-49,101-2000]' 'foo[31-49,101-199,301-2000]' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'foo[1-699,701-1000]' 'foo[1-199,301-699,701-1000]' 'foo[1-49,101-699,701-1000]' 'foo[1-49,101-199,301-699,701-1000]' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'foo[31-699,701-2000]' 'foo[31-199,301-699,701-2000]' 'foo[31-49,101-699,701-2000]' 'foo[31-49,101-199,301-699,701-2000]' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' 'pdsh@: Do not specify -A or -a with -g' ) let i=0 for A in "" "-A "; do for a in "" "-a "; do for w in "" "-w foo[31-2000] "; do for g in "" "-g bar "; do for x in "" "-x foo[50-100] "; do for X in "" "-X baz "; do test_expect_success "Py's genders test #$i ($A$a$w$g$x$X)" ' OUTPUT=$(pdsh -F ./genders.torture -q $A$a$w$g$x$X 2>&1 | tail -1 | sed "s/^\(pdsh@\)[^:]*/\1/") test_output_is_expected "$OUTPUT" "${results[$i]}" ' let i=i+1 done done done done done done unset PDSH_GENDERS_DIR unset PDSH_MISC_MODULES test_done pdsh-2.36/tests/t1002-dshgroup.sh0000775€^–Á €^–Á 0000000334615131211226024614 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh # test_description='dshgroup module' . ${srcdir:-.}/test-lib.sh if ! test_have_prereq MOD_MISC_DSHGROUP; then skip_all='skipping dshgroups tests, dshgroup module not available' test_done fi # # Ensure dshgroup module is loaded # export PDSH_MISC_MODULES=dshgroup mkdir -p .dsh/group/ cat >.dsh/group/groupA <.dsh/group/groupB <.dsh/group/groupAB <.dsh/group/groupA2 <.dsh/group/groupX <.dsh/group/groupY <&1 | grep -q "target hosts in dsh group" ' test_expect_success 'dshgroup -g option works' ' O=$(pdsh -g groupA -q | tail -1) test_output_is_expected "$O" "foo[0-3,10]" ' test_expect_success 'dshgroup -g option works with more than one group' ' O=$(pdsh -g groupA,groupB -q | tail -1) test_output_is_expected "$O" "foo[0-5,10]" ' test_expect_success 'dshgroup -X option works' ' O=$(pdsh -g groupA -X groupB -q | tail -1) test_output_is_expected "$O" "foo[0-2,10]" ' test_expect_success 'dshgroup -X option works with -w' ' O=$(pdsh -w foo[0-10] -X groupA -q | tail -1) test_output_is_expected "$O" "foo[4-9]" ' test_expect_success 'dshgroup #include syntax works' ' O=$(pdsh -g groupAB -q | tail -1) test_output_is_expected "$O" "foo[0-5,8,10]" ' test_expect_success 'dshgroup #include syntax works with absolute paths' ' O=$(pdsh -g groupA2 -q | tail -1) test_output_is_expected "$O" "foo[0-3,10]" ' test_expect_success 'dshgroup #include syntax detects loops' ' pdsh -g groupX -q 2>&1 | grep -q "warning:" ' test_done pdsh-2.36/tests/t6114-no-newline-corruption.sh0000775€^–Á €^–Á 0000000126315131211226027243 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='chaos/pdsh#114: pdsh garbles long output with no newline Test that pdsh handles lots of data with no newline.' . ${srcdir:-.}/test-lib.sh perl -e 'print "a" x 1024' > 1K perl -e 'print "a" x 8192' > 8K perl -e 'print "a" x 8195' > 8K+ perl -e 'print "a" x 10000' > 10K for i in 1K 8K 8K+ 10K; do test_expect_success "pdsh does not garble $i with no newline" " pdsh -w foo -N -Rexec cat $i > output.${i} && test_cmp ${i} output.${i} " done for i in 1K 8K 8K+ 10K; do test_expect_success "pdsh labels $i with no newline only once" ' pdsh -w foo -Rexec cat $i | sed "s/foo/\n&\n/g" > labels.${i} && test $(grep -c foo labels.${i}) -eq 1 ' done test_done pdsh-2.36/tests/t0004-module-loading.sh0000775€^–Á €^–Á 0000000611015131211226025652 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh test_description='pdsh dynamic module support' TEST_MODULE_PATH="$(pwd)/test-modules/.libs" . ${srcdir:-.}/test-lib.sh if ! test_have_prereq DYNAMIC_MODULES; then skip_all='skipping dynamic module tests, pdsh built with static modules' test_done fi if ! test -f $TEST_MODULE_PATH/a.so -a -f $TEST_MODULE_PATH/b.so; then echo "$0: Test modules A & B not built, please run \"make check.\"" >&2 exit 1 fi module_list () { pdsh -L "$EXTRA_PDSH_ARGS" 2>&1 | \ perl -n -e '\ chomp; ($k,$v) = split(/: */); $m = $v if ($k eq "Module"); print "$m $v\n" if ($k eq "Active");' } loaded_modules() { module_list | awk '$2 == "yes" {print $1}' } conflicting_modules() { module_list | awk '$2 == "no" {print $1}' } module_is_active() { loaded_modules | while read m; do if [ "$m" = "$1" ]; then return 0 fi done } module_is_inactive() { conflicting_modules | while read m; do if [ "$m" = "$1" ]; then return 0 fi done } test_output_matches() { OUTPUT="$1" PATTERN="$2" if ! echo "$OUTPUT" | grep -q "$PATTERN" ; then say_color error "Error: Didn't find pattern \"$PATTERN\"" say_color info "OUTPUT=$OUTPUT" false fi } unset EXTRA_PDSH_ARGS test_expect_success NOTROOT 'PDSH_MODULE_DIR functionality' ' PDSH_MODULE_DIR=$TEST_DIRECTORY/test-modules/.libs module_is_active A && module_is_active B ' export PDSH_MODULE_DIR="$TEST_DIRECTORY/test-modules/.libs" test_expect_success NOTROOT 'module A takes precedence over B' ' module_is_active misc/A && module_is_inactive misc/B ' test_expect_success NOTROOT 'pdsh -M B ativates module B' ' EXTRA_PDSH_ARGS="-M B" module_is_active misc/B && module_is_inactive misc/A ' test_expect_success NOTROOT 'PDSH_MISC_MODULES option works' ' PDSH_MISC_MODULES=B module_is_active misc/B && module_is_inactive misc/A ' test_expect_success NOTROOT '-M option overrides PDSH_MISC_MODULES environment var' ' OUTPUT=$(PDSH_MISC_MODULES=B pdsh -MA -L 2>&1) say_color error "$OUTPUT" ' test_expect_success NOTROOT 'pdsh help string correctly displays options of loaded modules' ' OUTPUT=$(pdsh -h 2>&1 | grep ^-a) && test_output_matches "$OUTPUT" "Module A" && OUTPUT=$(pdsh -M B -h 2>&1 | grep ^-a) && test_output_matches "$OUTPUT" "Module B" ' test_expect_success NOTROOT 'Loading conflicting module with -M causes error' ' OUTPUT=$(pdsh -MA,B 2>&1 | grep Warning) test_output_matches "$OUTPUT" \ "Failed to initialize requested module \"misc/B\"" ' test_expect_success NOTROOT 'Conflicting modules dont run init()' ' if pdsh -q 2>&1 | grep "B: in init"; then say_color error "Error: init routine for module B run unexpectedly" false fi ' test_expect_success NOTROOT 'Force loaded module runs init()' ' if ! pdsh -q -MB 2>&1 | grep "B: in init"; then say_color error "Error: init routine for module B not run with -M B" false fi ' test_expect_success NOTROOT 'New conflicting module does not run init() with -M' ' PDSH_MODULE_DIR=$TEST_DIRECTORY/test-modules if pdsh -q -MB 2>&1 | grep "A: in init"; then say_color error "Error: A init routine run with -M B" false fi ' test_done pdsh-2.36/auxdir/0000775€^–Á €^–Á 0000000000015131211226022002 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/auxdir/CVS/0000775€^–Á €^–Á 0000000000015131211226022435 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/auxdir/CVS/Root0000664€^–Á €^–Á 0000000001315131211226023275 0ustar arif.ali@canonical.comarif.ali@canonical.com/chaos/cvs pdsh-2.36/auxdir/CVS/Repository0000664€^–Á €^–Á 0000000001415131211226024532 0ustar arif.ali@canonical.comarif.ali@canonical.compdsh/auxdir pdsh-2.36/auxdir/CVS/Entries0000664€^–Á €^–Á 0000000013315131211226023766 0ustar arif.ali@canonical.comarif.ali@canonical.com/install-sh/1.1/Wed Oct 3 03:21:06 2001// /mkinstalldirs/1.1/Wed Oct 3 03:21:06 2001// D pdsh-2.36/bootstrap0000775€^–Á €^–Á 0000000043115131211226022447 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/bin/sh echo "Running libtoolize --automake --copy ... " libtoolize --automake --copy echo "Running autoreconf --verbose --install -I config" autoreconf --verbose --install -I config echo "Cleaning up ..." mv aclocal.m4 config/ rm -rf autom4te.cache echo "Now run ./configure." pdsh-2.36/README.modules0000664€^–Á €^–Á 0000000520715131211226023041 0ustar arif.ali@canonical.comarif.ali@canonical.com This file describes the current list of modules distributed with pdsh. Pdsh modules come in two flavors at this time: rcmd and miscellaneous. The rcmd modules provide remote command functionality for pdsh, while the "misc" modules extend the functionality of pdsh in some other way -- by providing new options to pdsh or modifying the pdsh working collective, for example. Multiple rcmd modules may be installed at once and are chosen at runtime by either the '-R type' option to pdsh, or by setting the environment variable PDSH_RCMD_TYPE. When pdsh initializes it reads all available modules from the `pkglibdir' (typically /usr/lib/pdsh or /usr/local/lib/pdsh). If conflicting modules are installed, they are loaded on a first-come first-serve basis (i.e. the first module loaded wins). Modules may be force-initialized by specifying them to the '-M' pdsh/pdcp option, or via the PDSH_MISC_MODULES environment variable. PDSH MODULES: ============= Module: rcmd/rsh Package: pdsh-rcmd-rsh Description: BSD rcmd(3) connect method for pdsh. Conflicts: None Requires: rshd server running on remote nodes Module: rcmd/ssh Package: pdsh-rmcd-ssh Description: SSH rcmd connect method for pdsh. Conflicts: None Requires: ssh installed, sshd on remote nodes Module: rcmd/mrsh Package: pdsh-rcmd-mrsh Description: Rcmd connect method using Munge authentication. Conflicts: None Requires: munge Module: rcmd/xcpu Package: pdsh-rcmd-xcpu Description: Rcmd connect method using XCPU Conflicts: None Requires: XCPU service mounted on /mnt/xcpu//xcpu Module: misc/genders Package: pdsh-mod-genders Description: Provides -a,-i,-g node selection options using libgenders. Conflicts: misc/machines, misc/dshgroup, misc/netgroup Requires: libgenders Module: misc/nodeupdown Package: pdsh-mod-nodeupdown Description: Provides -v option using nodeupdown library Conflicts: None Requires: libnodeupdown Module: misc/machines Package: pdsh-mod-machines Description: Provides -a option using a flat-file list of hosts Conflicts: misc/genders, misc/nodeattr Module: misc/slurm Package: pdsh-mod-slurm Description: Allows list of targets to be built off SLURM_JOBID or `-j jobid' Conflictls: None Requires: slurm Module: misc/dshgroup Package: pdsh-mod-dshgroup Description: Allows list of targets to be built from dsh-style "group" files. Conflicts: misc/genders, misc/netgroup, misc/nodeattr Module: misc/netgroup Package: pdsh-mod-netgroup Description: Allows list of targets to be build from netgroups. Conflicts: misc/genders, misc/dshgroup, misc/nodeattr pdsh-2.36/.mergify.yml0000664€^–Á €^–Á 0000000224015131211226022747 0ustar arif.ali@canonical.comarif.ali@canonical.comqueue_rules: - name: default queue_conditions: - base=master - label="merge-when-passing" - label!="work-in-progress" - "approved-reviews-by=@chaos/chaos-developers" - "#approved-reviews-by>0" - "#changes-requested-reviews-by=0" - -title~=^\[*[Ww][Ii][Pp] merge_conditions: - base=master - status-success="validate commits" - status-success="ubuntu" - status-success="macos" - status-success="coverage" - label="merge-when-passing" - label!="work-in-progress" - "approved-reviews-by=@chaos/chaos-developers" - "#approved-reviews-by>0" - "#changes-requested-reviews-by=0" - -title~=^\[*[Ww][Ii][Pp] update_method: rebase merge_method: merge pull_request_rules: - name: remove outdated approved reviews conditions: - author!=@chaos-developers actions: dismiss_reviews: approved: true changes_requested: false message: | Approving reviews have been dismissed because this pull request was updated. - name: rebase and merge when passing all checks conditions: [] actions: queue: pdsh-2.36/README0000664€^–Á €^–Á 0000001504015131211226021366 0ustar arif.ali@canonical.comarif.ali@canonical.com+-------------+ | Description | +-------------+ Pdsh is a multithreaded remote shell client which executes commands on multiple remote hosts in parallel. Pdsh can use several different remote shell services, including standard "rsh", Kerberos IV, and ssh. See the man page in the doc directory for usage information. +---------------+ | Configuration | +---------------+ Pdsh uses GNU autoconf for configuration. Dynamically loadable modules of each shell service (as well as other features) will be compiled based on configuration. By default, rsh, Kerberos IV, and SDR (for IBM SPs) will be compiled if they exist on the system. The README.modules file distributed with pdsh contains a description of each module available, as well as its requirements and/or conflicts. If your system does not support dynamically loadable modules, you may compile modules in statically using the --enable-static-modules option. To configure in additional feature modules: ./configure [options] --without-rsh Disable support for BSD rcmd(3) (standard rsh). --with-ssh Enable support of ssh(1) remote shell service. --with-machines=/path/to/machines Use a flat file list of machine names for -a instead of genders --with-genders Enable support of a genders database through the genders(3) library. For pdsh's -i option to function properly, the genders database must have alternate node names listed as the value of the "altname" attribute. --with-dshgroups Enable support of dsh-style group files in ~/.dsh/group/groupname or /etc/dsh/group/groupname. Allows use of -g/-X to target or exclude hosts in dsh group files. --with-netgroup Enable use of netgroups (via /etc/netgroup or NIS) to build lists of target hosts using -g/-X to include/exclude hosts. --with-nodeupdown Enable support of dynamic elimination of down nodes through the nodeupdown(3) library. --with-mrsh Enable support of mrsh(1) remote shell service. --with-slurm Support running pdsh under SLURM allocation. --with-fanout=N Specify default fanout (default is 32). --with-timeout=N Set default connect timeout (default is 10 seconds). --with-readline Use the GNU readline library to parse input in interactive mode. Note that a number of the above configurations options may "conflict" with each other because they perform identical operations. For example, genders and nodeattr both support the -g option. If several modules are installed that support identical options, the options will default to one particular module. Static compilation of modules will fail if conflicting modules are selected. See the man page in this directory for details on which modules conflict. +------------+ | INSTALLING | +------------+ make make install By default, pdsh is now installed without setuid permissions. This is because, for the majority of the rcmd connect protocols, root permissions are not necessarily needed. If you are using either of the "rcmd/rsh" or "rcmd/qsh" modules, you will need to change the permissions of pdsh and pdcp to be setuid root after the install. For example: > chown root PREFIX/bin/pdsh PREFIX/bin/pdcp > chmod 4755 PREFIX/bin/pdsh PREFIX/bin/pdcp +---------+ | GOTCHAS | +---------+ Watch out for the following gotchas: 1) When executing remote commands via rsh, krb4, qsh, or ssh, pdsh uses one reserved socket for each active connection, two if it is maintaining a separate connection for stderr. It obtains these sockets by calling rresvport(), which normally draws from a pool of 256 sockets. You may exhaust these if multiple pdsh's are running simultanously on a machine, or if the fanout is set too high. Mrsh and mqsh do not use reserved ports, and therefore are not affected this problem as severely. 2) When pdsh is using a remote shell service that is wrapped with TCP wrappers, there are three areas where bottlenecks can be created: IDENT, DNS, and SYSLOG. If your hosts.allow includes "user@", e.g. "in.rshd : ALL@ALL : ALLOW" and TCP wrappers is configured to support IDENT, each simultaneous remote shell connection will result in an IDENT query back to the source. For large fanouts this can quickly overwhelm the source. Similarly, if TCP wrappers is configured to query the DNS on every connection, pdsh may overwhelm the DNS server. Finally, if every remote shell connection results in a remote syslog entry, syslogd on your loghost may be overwhelmed and logs may grow excessively long. If local security policy permits, consider configuring TCP wrappers to avoid calling IDENT, DNS, or SYSLOG on every remote shell connection. Configuring without the "PARANOID" option (which requires all connections to be registered in the DNS), permitting a simple list of IP addresses or a subnet (no names, and no user@ prefix), and setting the SYSLOG severity for the remote shell service to a level that is not remotely logged will avoid these pitfalls. If these actions are not possible, you may wish to reduce pdsh's default fanout (configure --with-fanout=N). +---------------------+ | THEORY OF OPERATION | +---------------------+ We will generalize for the common remote shell service rsh. The following is similar for all other shell services (ssh, krb4, qsh, etc.), but other shell services may include additional security or features. A thread is created for each rsh connection to a node. Each thread opens a connection using an MT-safe rcmd-like function, returns stdin and stderr streams, then terminates. The mainline starts fanout number of rsh threads and waits on a condition variable that is signalled by the rsh threads as they terminate. When the condition variable is signalled, the main thread starts a new rsh thread to maintain the fanout, until all remote commands have been executed. A timeout thread is created that monitors the state of the threads and terminates any that take too much time connecting or, if requested on the command line, take too long to complete. Typing ^C causes pdsh to list threads that are in the connected state. Another ^C immediately following the first one terminates the program. +--------+ | AUTHOR | +--------+ Jim Garlick Please send suggestions, bug reports, or just a note letting me know that you are using pdsh (it would be interesting to hear how many nodes are in your cluster). +------+ | NOTE | +------+ This product includes software developed by the University of California, Berkeley and its contributors. Modifications have been made and bugs are probably mine. The PDSH software package has no affiliation with the Democratic Party of Albania (www.pdsh.org). pdsh-2.36/NEWS0000664€^–Á €^–Á 0000005300015131211226021203 0ustar arif.ali@canonical.comarif.ali@canonical.comThis file describes changes in recent versions of pdsh. It primarily documents those changes that are of interest to users and admins. * Changes in pdsh-2.36 (2025-01-12) =================================== -- fix build on C23 compiler (Egbert Eich) -- mcmd: fix compiler warnings on modern GCC (Adrian Reber) -- fix closeall performance with large _SC_OPEN_MAX (Zheyu Shen) -- genders: fix typo in comment (Al Chu) -- fix hostlist out of bounds array access (Al Chu) * Changes in pdsh-2.35 (2023-12-19) =================================== -- slurm: call slurm_init() once before any call to Slurm API (Egbert Eich) -- slurm: fix compile of slurm plugin against Slurm >= 23.x -- log module option errors with -d (Erik Jacobson) -- fail fast on connect error or non-zero return code (Jerry Mannil) -- release a lock that is no longer used (ycaibb) -- build: use LDADD instead of LDFLAGS for libcommon.la (orbea) -- Remove README.QsNet from the RPM SPEC file's doc list (Jason St. John) -- slurm: add -C to restrict hostlist to nodes with features (Dylan Simon) -- ssh: fix sshcmd_signal on macos -- dsbak: fix handling of empty input lines * Changes in pdsh-2.34 (2020-01-07) =================================== -- Fix for output corruption with no newlines (#114) -- pipecmd: fix result check error handling (Dylan Simon) -- slurm: workaround slurm export of internal List interfaces -- readline: add application name as argv[0] (#112) -- Fix errors from lgtm.com scan * Changes in pdsh-2.33 (2017-06-28) =================================== -- Fix segfault and build issues on Mac OSX (#95) -- Always pass RTLD_GLOBAL to dlopen(3) of modules. Fixes missing symbol errors from modules using libraries that also use dlopen() (e.g. nodeupdown, slurm) * Changes in pdsh-2.32 (2017-06-22) =================================== -- Autotools update -- Switch to dlopen(3)/dlsym(3) instead of using libltdl -- Drop qshell, mqshell, rmsquery, nodeattr and sdr modules. -- Fix issue 70: dshbak: handle hostname of "0" -- Allow PDSH_CONNECT_TIMEOUT and PDSH_COMMAND_TIMEOUT environment variables (Erik Jacobson) -- Fix some old URLs in documentation (Al Chu) -- Avoid exporting POSIXLY_CORRECT to child processes (Dorian Krause) -- Fix mcmd start offset bug in max bytes calculation (Egbert Eich) * Changes in pdsh-2.31 (2013-11-07) =================================== -- Fix issue 56: slurm: Allow mixed use of -P, -w and -j options. -- Fix issue 59: pdsh very slow when using a few thousand hosts and genders. -- testsuite: Expanded tests for genders module (Pythagoras Watson) * Changes in pdsh-2.30 (2013-03-02) =================================== -- Fix issue 55: genders -X option removes more hosts than expected. (This was a generic fix for hostname matching, so it probably affected -x and other options as well.) -- testsuite: Add test for issue 55. * Changes in pdsh-2.29 (2013-02-12) =================================== -- Fix issue 42: --with-dshgroup sets DSHGROUP_PATH to "yes" -- Fix issue 53: Add -P option to target SLURM partitions (Michael Fenn) -- Fix issue 54: pdsh prints empty lines with -S -- pdcp: Add more levels of directory recursion (Pythagoras Watson) * Changes in pdsh-2.28 (2011-10-19) =================================== -- Fix issue 39: ssh forces use of -l option -- Fix issue 40: -l%u added to ssh args only if remote and local usernames differ -- testsuite: Added tests for -l%u handing in ssh * Changes in pdsh-2.27 (2011-09-07) =================================== -- Fix issue 17: Allow dshgroup files to include other files -- Fix issue 33: dshbak breaks up host lists at zeropad boundaries, (e.g. 01-09,10-11, 010-099,100-101, etc.) -- Fix issue 34: dshgroup path override broken in ./configure -- Fix issue 36: pdsh truncates output lines at 8K -- dshgroup: Allow dshgroup search path to be overridden by DSHGROUP_PATH, a colon-separated list of directories to search. $HOME/.dsh/group is still always prepended to this path. -- Allow wcoll files (-w ^file and WCOLL=file) to include other files with a "#include FILE" syntax. If included files have no path, then a search path of the dirname of the included file ("." for stdin) is used. -- testsuite: added tests, fixed some portability issues. -- Fix some minor memory leaks and locking bugs reported by Coverity. * Changes in pdsh-2.26 (2011-04-29) =================================== -- Fix issue 14: interactive mode broken with ssh -- Fix issue 19: missing commas in dshbak(1) header output -- Fix issue 20: compilation error in genders.c with non-GCC compilers -- Fix issue 23: compilation failure with --enable-static-modules -- Fix issue 24: don't arbitrarily limit usernames to 16 characters -- Fix issue 25: PDSH_SSH_ARGS should not require %u and %h -- Fix issue 26: document use of %u and %h in PDSH_SSH_ARGS -- Fix interactive mode with rcmd/exec. -- genders: do not look for genders_query(3) support at runtime, as this causes too many problems on some systems. Instead, use autoconf to include or exclude genders_query support from the genders module. (fixes Issue 1) -- Fix build on AIX -- Add -Wl,-E to LDFLAGS on HP-UX (Issue 18) -- Fixes for testsuite portability (Issues 15, 22, 28) * Changes in pdsh-2.25 (2011-03-09) =================================== -- Fix pdcp breakage with ssh (bug introduced in pdsh-2.24). (Resolves issue 12: pdcp executes file instead of copying.) -- testsuite: Skip tests dependent on PDSH_MODULE_DIR when testsuite run as root (Resolves issue 13: testsuite broken when run as root). -- testsuite: Skip dshbak -d test to non-writable directory when directory permissions do not seem to apply (e.g. privileged user). (Possibly resolves issue 11: tests fail on Mac OSX under fink) -- testsuite: add simple ssh tests. * Changes in pdsh-2.24 (2011-02-28) =================================== -- Resolve issue 7: Allow PDSH_REMOTE_PDCP_PATH to set default path to remote pdcp program (pdcp -e). -- Resolve issue 9: Fix use of PDSH_SSH_ARGS_APPEND. -- Resolve issue 10: dshbak: Support writing output to file-per-host. Adds new -d DIR and -f options to dshbak. -- genders: Allow relative paths to genders files with -F and PDSH_GENDERS_FILE. -- genders: Don't print an error if genders file is missing, unless a genders optin (-F, -a, -g, -i, -X) is explicitly used. -- genders: Allow -g to be used with other node selection options as a filter. For example: pdsh -w host[0-1024] -g attr1 ... -- ssh: Better preservation of remote command args in ssh module. Previous versions of pdsh would alwas collapse all args into a single argument passed to ssh: "cmd arg1 arg2 ..." With this fix the argv list will more closely match the form passed to pdsh. -- Refactored large portions of dshbak, improve usage output, and update manpage. -- Expanded testsuite. * Changes in pdsh-2.23 (2010-10-22) =================================== -- Fix issue 4: List available but conflicting modules in -V and -L output. -- Fix issue 5: dshbak -c doesn't properly coalesce hosts with different zero-padding in the numeric suffix. -- Added torque module for setting list of target hosts based on Torque/PBS jobs (Issue 2). -- Enhance syntax of -w and -x to allow preceeding arguments with: `-' - Exclude hosts instead of include with -w ( -w foo[1-9],-foo8) `^' - insert/exclude list of hosts from a file (^/path/to/file) `/' - filter hosts based on regex (/host.*0$/) -- Introduce new pdsh testsuite with many new tests. See tests/README for more information. * Changes in pdsh-2.22 (2010-09-03) =================================== -- Fixes several bugs introduced in pdsh-2.21, Including: - genders: segfault when genders file doesn't exist - Failure to initialize some modules if others conflict - -M option doesn't work in some cases -- Sort modules by name before initialization so that modules initialize in a reproducible order. * Changes in pdsh-2.21 (2010-09-02) =================================== -- New option -M name,... forces load of misc modules by name, thus allowing users to select between conflicting modules. (PDSH_MISC_MODULES environment variable may also be used) -- Due to above, an error is no longer printed when module options conflict. -- genders: Support PDSH_GENDERS_FILE environment variable to override default genders file name (default = "genders") -- genders: Support PDSH_GENDERS_DIR environment variable to override default directory for genders files (default = /etc). -- genders: Allow non-absolute path to be specified to -F option. -- Fix parsing of hostlist expressions with multiple brackets (from SF.net user don.fanucci) -- Fix compile error on platforms where setnetgrent() doesn't return an int. -- Allow path to dshgroup files to be specified to ./configure with --with-dshgroups=PATH. -- Update libltdl * Changes in pdsh-2.20 (2009-09-29) =================================== -- Fix bug in pdcp which serialized copy of files to nodes regardless of fanout. * Changes in pdsh-2.19 (2009-05-21) =================================== -- New pdsh configure option --with-rcmd-rank-list allows the default pdsh rcmd module priority list to be overridden at compile time. -- genders: Always read genders file in case there are any pdsh_rcmd_type attributes to apply to the target hosts. -- genders: Fix bug where altnames were not used by default in some cases due to a bug in generic module loading code. (patch from Py Watson) -- Fix for coredump when no rcmd module is loaded. (patch from Py Watson) -- %{_libdir}/pdsh should be owned by pdsh RPM. (patch from Py Watson) * Changes in pdsh-2.18 (2009-03-24) =================================== -- Fix duplicate error output from ssh module. (Patch from Paul D Smith ) -- Add -e option to pdcp to explicitly specify remote execution path. This is necessary if pdcp is installed in a different path locally than on the target hosts. -- Fix recursive pdcp (-r) which has been broken since pdsh-2.12. -- Support --without-ssh-connect-timeout-option for ./configure. -- Add note to pdsh(1) manpage that only very old versions of the genders nodeattr script are supported by the pdsh nodeattr module. The genders module is the preferred interface to genders. * Changes in pdsh-2.17 (2008-09-23) =================================== -- Ignore multiple consecutive ':' characters in host list argument to -w option. Only a single ':' by itself will be interpreted as the special "rcmd_type:hosts" syntax. -- dshbak: Allow for ':' in pdsh output lines. (patch from Ashley Pittman) -- Fix for compile against glibc 2.8, which no longer defines ARG_MAX (patch from Tom 'spot' Callaway via sf.net). -- Include missing unistd.h in hostlist.h. (patch from Ashley Pittman) -- Fix compile on Solaris by updating to latest ACX_PTHREAD autoconf macro from autoconf-archive.cryp.to. * Changes in pdsh-2.16 (2008-03-19) =================================== -- Don't attempt to read WCOLL file unless no other host selection method is utilized. -- (genders) Don't attempt to read a genders file if no genders options are used. -- When specifying rcmd_type via -w (i.e. [rcmd_type:][user@]host) have rcmd_type apply across all hosts until the next rcmd specification. That is ssh:host1,host2 now uses ssh for both host1 and host2. Prior to this change, ssh would only be used to contact host1. Same change for [user]. -- Check for valid fanout specified to -f option. -- Always sort output of dshbak, and remove dshbak -s option. -- Strip leading space (added by pdsh) from dsbak output. * Changes in pdsh-2.15 (2007-10-24) =================================== -- Refactor ssh module to use built in "pipecmd" support (same code as the "exec" module) -- Add connect timeout support for ssh implementations which support it. By default, the OpenSSH "-oConnectTimeout=%d" option is used. This can be overridden at configure time using --with-ssh-connect-timeout-option="OPTION" where OPTION should contain one %d which will be expanded to the actual timeout value specified on the command line. -- Add ncurses-devel to pdsh.spec BuildRequires. * Changes in pdsh-2.14 (2007-06-22) =================================== -- Fix compile error in src/pdsh/mod.c when using --enable-static-modules. -- Allow conditional and static build of "exec" rcmd module. -- (slurm) Re-order headers in slurm module to avoid redefinition of pdsh bool type. * Changes in pdsh-2.13 (2007-06-06) =================================== -- New "exec" rcmd module for executing arbitrary commands. (Thanks to Christopher Maestas for the idea.) -- New -N option to disable hostname: prefix on lines of output. -- dshbak: Allow host range compression for numerically keyed hosts which have non-numeric suffix. -- Fix for compilation problem on some machines for sshcmd.c. -- Fix for erroneous command timeouts with ssh due to stray signals. -- Fix handling of empty lines when parsing WCOLL files. * Changes in pdsh-2.12 (2006-12-11) =================================== -- Fix Solaris build. -- New command rpdcp(1) ("reverse" pdcp) copies files from remote hosts to the local machine (with remote hostname appended). -- Do not truncate host domain in output labels if any domains differ. -- (genders) Default user name may be specified to pdsh_rcmd_type attr by prepending "user@". -- (slurm) `-j' "all" parameter to target all nodes running SLURM jobs. -- (slurm) Fix for build issue against slurm-1.0. -- Minor code cleanup. -- Minor RPM spec file cleanup. * Changes in pdsh-2.11 (2006-07-26) =================================== -- Fix for WCOLL file contents unconditionally added to host list. Prior behavior was to have -w override WCOLL. -- Supply more specific errors when pdsh detects that the path to pdsh modules is insecure. -- Fix man page typos. -- Fix compile for Tru64. -- (xcpu) Use lexec facility. -- New dshbak -s option to sort output. -- Allow users to cancel pending threads by issuing ctrl-C ctrl-Z (ctrl-Z within one second of initial ctrl-C). Pending threads are those threads which have not yet invoked a remote command. * Changes in pdsh-2.10 (2006-03-22) =================================== -- Fix for -w hosts parsing bug. -- Fix missing support for "-w user@hosts" in pdsh-2.9. -- Add support for command history in interactive mode with readline. Command history file is ~/.pdsh/history. -- (xcpu) Support for signals and -S. * Changes in pdsh-2.9 (2006-03-15) =================================== -- Add netgroup module which provides -g/-X groupname option to read list of hosts from netgroup entries (/etc/netgroup or NIS). -- Add support for specifying alternate rcmd connection type and/or remote username for a subset of hosts on the commandline. Format is ``-w [rcmd_type:][user@]hosts...'' -- (genders) Add support for pdsh_rcmd_type genders attribute to set the default rcmd connection type for certain hosts via the genders database. -- Add a trivial form of privilege separation to pdsh when pdsh or pdcp binaries are installed setuid root. This is necessary to safely support multiple rcmd connection methods in a single invocation of pdsh. Currently, the privileged process in pdsh is only used to serve reserved ports to threads that need it (e.g. rsh, qsh), while allowing all threads to still run as the "user" to support ssh and mrsh. This is *not* a security enhancement, and doesn't make installing pdsh setuid root any more secure. -- (ssh) Allow arguments to ssh command to be overridden and augmented with environment variables PDSH_SSH_ARGS and PDSH_SSH_ARGS_APPEND. -- Fix for race condition in ssh support which caused occasional pdsh hang, especially when running with a larger fanout. -- Fix for ``-x hosts'' processing bug: occasionally hosts provided to -x option would not be removed from the list of targets because the list of excluded hosts was being processed too early. -- Fixes for Mac OSX support: Update libtool/ltmain.sh and test for and include sys/uio.h. -- Add support for LANL XCPU. * Changes in pdsh-2.8 (2005-10-13) =================================== -- (dshgroup) Fix bug in dshgroup module which resulted in the module trying to read only from ~/.dsh/group/ even if /etc/dsh/group/ should have been used. (patch from Joe Ramey ) -- Print error strings returned by remote RCP server instead of generic "Failed to initiate RCP protocol." -- Allow hostlist expressions to have suffixes (e.g. foo[0-10]-bar). -- Fix for command timeout when its value is less than connect timeout. -- (ssh) Remove ssh_reaper thread and instead reap child ssh processes within each thread spawned by pdsh. This fixes reported problem under LinuxThreads. -- (sdr) Fix assertion failure if host_responds info exists in SDR for a node number that doesn't exist in Node class. * Changes in pdsh-2.7 (2005-08-15) =================================== -- Add dshgroup module which adds -g/-X groupname to utilize dsh-style "group" files in ~/.dsh/group/groupname || /etc/dsh/group/groupname. -- Fix possible truncation of output when poll() returns POLLHUP before all data has been read. -- Close pdsh file descriptors opened to ssh commands before invoking subsequent ssh connections to fix possible pdsh hang with -Rssh. -- Replace setenv() & seteuid() with putenv() & setuid() for portability. -- Module path elements now considered secure if they have same ownership as pdsh binary (as well as either root or current user). Allows pdsh to be installed with other than root ownership. * Changes in pdsh-2.6 (2005-08-02) =================================== -- (ssh) Fix pdcp hang with ssh on HP-UX (and possibly other systems). (Yehia Beyh ) -- Remove pdcp debugging code released with pdsh-2.5. * Changes in pdsh-2.5 (2005-07-21) =================================== -- (ssh) Added ssh "reaper" thread to collect exit status of ssh commands invoked by pdsh. At the end of the pdsh or pdcp run, any non-zero exit status from ssh will be written to stderr instead of silently ignored. -- (genders) Added genders "query" support for genders versions that support this functionality. (Al Chu ) -- (ssh) Fix for a possible pdsh hang when running with ssh on Mac OS X. -- (qshell) Fix for m/qshd failure on nodes without an elan3 adapter. (Daniel Blueman ) -- Update path to xinetd script in pdsh specfile (Daniel Blueman). -- Don't increase number of open files rlimit until after rcmd modules are initialized in case rcmd module increases fanout. -- Don't consider directory permissions insecure when other has write access if the sticky bit is set. * Changes in pdsh-2.4 (2004-12-08) =================================== -- (mrsh) Update to mrsh protocol version 2.1, provided by mrsh >= 2.3. This version of the protocol allows connections over "localhost." * Changes in pdsh-2.3 (2004-11-22) =================================== -- Support copying files >2GB in pdcp. -- Increase max number of open files to the hard limit if a large fanout is requested. * Changes in pdsh-2.2 (2004-07-30) =================================== -- Do not abort when excluded hosts are not within the target list. -- (slurm) Allow multiple SLURM jobids to be passed to -j option. * Changes in pdsh-2.1 (2004-06-09) =================================== -- Fix dshbak script to allow whitespace in hostname "tags" (Ashley Pittman ). -- Fix broken QsNet support on Elan3 systems without libelanctrl. -- Explicitly set thread stack size to 128K to avoid arbitrarily large stack set from current rlimit under Linux/NPTL. * Changes in pdsh-2.0 (2004-05-26) =================================== -- Reorganized pdsh source tree. -- Modularized pdsh functionality into dynamic runtime "modules." - Moved remote command api (e.g. rsh, ssh, etc.) into "rcmd" modules. - New pdsh option `-R' for selection of rcmd type at runtime. - All other optional functionality moved into "misc" modules (e.g. genders). - See README.modules in pdsh distribution for more information. -- Package core pdsh functionality and pdsh modules separately in order to make pdsh installation much more flexible. -- Removed pdsh dependency on rcp. Rcp server code used by pdsh is now coded directly into pdcp. (Note: This means pdcp *must* be installed on all target nodes) -- Added mrsh and mqshell client modules, as well as mqshd server for Munge authenticated rcmd() support. -- Added SLURM module to build list of targets from SLURM allocations. -- (genders) Added "-X" option to exclude targets based on genders attributes. -- (genders) Multiple attributes may now be passed to "-g" and "-X." -- (genders) "-a" renamed to "-A" -- "-a" now skips any nodes in genders database with "pdsh_all_skip" attribute. -- (genders) New "-F" option to specify non-default genders file. -- QsNet support now requires "/etc/elanhosts" config file (see README.QsNet). -- QsNet support includes support for QSW elanctrl interface. (Elan3/Elan4) -- Added support for running Elan network error resolver thread in qshd/mqshd. (Elan3 only) -- Nominal support for Elan multirail jobs (largely untested). See qshell and mqshell `-r railmask' option. $Id$ pdsh-2.36/TODO0000664€^–Á €^–Á 0000000067515131211226021206 0ustar arif.ali@canonical.comarif.ali@canonical.com- Addy requests more prun-like options - Optional pdsh.conf file for configurable system wide defaults - Improve xpoll() interface?? - autoconf putenv - support long options - command line check for fanout > posix thread limit - option to allocate all threads/mem before executing command - important so that if fanout is too large, commands won't be started and killed halfway through. - bproc rcmd module - ssh module should use pipecmd pdsh-2.36/config/0000775€^–Á €^–Á 0000000000015131211226021753 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/config/Makefile.am0000664€^–Á €^–Á 0000000152215131211226024007 0ustar arif.ali@canonical.comarif.ali@canonical.com##**************************************************************************** ## $Id$ ##**************************************************************************** ## Process this file with automake to produce Makefile.in. ##**************************************************************************** EXTRA_DIST = \ Make-inc.mk \ ac_connect_timeout.m4 \ ac_debug.m4 \ ac_dmalloc.m4 \ ac_fanout.m4 \ ac_genders.m4 \ ac_gpl_licensed.m4 \ ac_krb4.m4 \ ac_machines.m4 \ ac_mrsh.m4 \ ac_xcpu.m4 \ ac_nodeupdown.m4 \ ac_pollselect.m4 \ ac_readline.m4 \ ac_socklen_t.m4 \ ac_ssh.m4 \ ac_exec.m4 \ ac_static_modules.m4 \ acx_pthread.m4 \ ac_rsh.m4 \ ac_slurm.m4 \ ac_dshgroup.m4 \ ac_dshgroup.m4 \ ac_msghdr_accrights.m4 \ libtool.m4 \ tap-driver.sh pdsh-2.36/config/ax_code_coverage.m40000664€^–Á €^–Á 0000002707415131211226025504 0ustar arif.ali@canonical.comarif.ali@canonical.com# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html # =========================================================================== # # SYNOPSIS # # AX_CODE_COVERAGE() # # DESCRIPTION # # Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, # CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included # in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every # build target (program or library) which should be built with code # coverage support. Also defines CODE_COVERAGE_RULES which should be # substituted in your Makefile; and $enable_code_coverage which can be # used in subsequent configure output. CODE_COVERAGE_ENABLED is defined # and substituted, and corresponds to the value of the # --enable-code-coverage option, which defaults to being disabled. # # Test also for gcov program and create GCOV variable that could be # substituted. # # Note that all optimization flags in CFLAGS must be disabled when code # coverage is enabled. # # Usage example: # # configure.ac: # # AX_CODE_COVERAGE # # Makefile.am: # # @CODE_COVERAGE_RULES@ # my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ... # my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... # my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... # my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... # # This results in a "check-code-coverage" rule being added to any # Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module # has been configured with --enable-code-coverage). Running `make # check-code-coverage` in that directory will run the module's test suite # (`make check`) and build a code coverage report detailing the code which # was touched, then print the URI for the report. # # In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined # instead of CODE_COVERAGE_LIBS. They are both still defined, but use of # CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is # deprecated. They have the same value. # # This code was derived from Makefile.decl in GLib, originally licenced # under LGPLv2.1+. # # LICENSE # # Copyright (c) 2012, 2016 Philip Withnall # Copyright (c) 2012 Xan Lopez # Copyright (c) 2012 Christian Persch # Copyright (c) 2012 Paolo Borelli # Copyright (c) 2012 Dan Winship # Copyright (c) 2015 Bastien ROUCARIES # # This library is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or (at # your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . #serial 25 AC_DEFUN([AX_CODE_COVERAGE],[ dnl Check for --enable-code-coverage AC_REQUIRE([AC_PROG_SED]) # allow to override gcov location AC_ARG_WITH([gcov], [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) AC_MSG_CHECKING([whether to build with code coverage support]) AC_ARG_ENABLE([code-coverage], AS_HELP_STRING([--enable-code-coverage], [Whether to enable code coverage support]),, enable_code_coverage=no) AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes]) AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) AC_MSG_RESULT($enable_code_coverage) AS_IF([ test "$enable_code_coverage" = "yes" ], [ # check for gcov AC_CHECK_TOOL([GCOV], [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], [:]) AS_IF([test "X$GCOV" = "X:"], [AC_MSG_ERROR([gcov is needed to do coverage])]) AC_SUBST([GCOV]) dnl Check if gcc is being used AS_IF([ test "$GCC" = "no" ], [ AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) ]) AC_CHECK_PROG([LCOV], [lcov], [lcov]) AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) AS_IF([ test -z "$LCOV" ], [ AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed]) ]) AS_IF([ test -z "$GENHTML" ], [ AC_MSG_ERROR([Could not find genhtml from the lcov package]) ]) dnl Build the code coverage flags dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility CODE_COVERAGE_CPPFLAGS="-DNDEBUG" CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" CODE_COVERAGE_LIBS="-lgcov" CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS" AC_SUBST([CODE_COVERAGE_CPPFLAGS]) AC_SUBST([CODE_COVERAGE_CFLAGS]) AC_SUBST([CODE_COVERAGE_CXXFLAGS]) AC_SUBST([CODE_COVERAGE_LIBS]) AC_SUBST([CODE_COVERAGE_LDFLAGS]) [CODE_COVERAGE_RULES_CHECK=' -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture '] [CODE_COVERAGE_RULES_CAPTURE=' $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" '] [CODE_COVERAGE_RULES_CLEAN=' clean: code-coverage-clean distclean: code-coverage-clean code-coverage-clean: -$(LCOV) --directory $(top_builddir) -z -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY) -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete '] ], [ [CODE_COVERAGE_RULES_CHECK=' @echo "Need to reconfigure with --enable-code-coverage" '] CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK" CODE_COVERAGE_RULES_CLEAN='' ]) [CODE_COVERAGE_RULES=' # Code coverage # # Optional: # - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. # Multiple directories may be specified, separated by whitespace. # (Default: $(top_builddir)) # - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated # by lcov for code coverage. (Default: # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) # - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage # reports to be created. (Default: # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) # - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, # set to 0 to disable it and leave empty to stay with the default. # (Default: empty) # - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov # instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) # - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov # instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) # - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov # - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the # collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) # - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov # instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) # - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering # lcov instance. (Default: empty) # - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov # instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) # - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the # genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) # - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml # instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) # - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore # # The generated report will be titled using the $(PACKAGE_NAME) and # $(PACKAGE_VERSION). In order to add the current git hash to the title, # use the git-version-gen script, available online. # Optional variables CODE_COVERAGE_DIRECTORY ?= $(top_builddir) CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage CODE_COVERAGE_BRANCH_COVERAGE ?= CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ --rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) CODE_COVERAGE_IGNORE_PATTERN ?= GITIGNOREFILES ?= GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\ $(CODE_COVERAGE_OUTPUT_FILE); code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\ $(CODE_COVERAGE_IGNORE_PATTERN); code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY); code_coverage_quiet = $(code_coverage_quiet_$(V)) code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) code_coverage_quiet_0 = --quiet # sanitizes the test-name: replaces with underscores: dashes and dots code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) # Use recursive makes in order to ignore errors during check check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"' # Capture code coverage data code-coverage-capture: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"' # Hook rule executed before code-coverage-capture, overridable by the user code-coverage-capture-hook: '"$CODE_COVERAGE_RULES_CLEAN"' A''M_DISTCHECK_CONFIGURE_FLAGS ?= A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean '] AC_SUBST([CODE_COVERAGE_RULES]) m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])]) ]) pdsh-2.36/config/ac_static_modules.m40000664€^–Á €^–Á 0000000715315131211226025705 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Al Chu # # SYNOPSIS: # AC_STATIC_MODULE # # DESCRIPTION: # Output #include files for static modules. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** # This must be called before checks before any specific modules are done AC_DEFUN([AC_STATIC_MODULES_INIT], [ rm -f static_modules.h ]) AC_DEFUN([AC_ADD_STATIC_MODULE], [ if test "$ac_static_modules" = "yes" ; then MODULES="$MODULES $1" fi ]) AC_DEFUN([AC_STATIC_MODULES_EXIT], [ # check for module conflicts if test "$ac_have_libgenders" = "yes" && test "$ac_have_machines" = "yes"; then AC_MSG_ERROR([--with-genders conflicts with --with-machines]) fi for module in $MODULES; do LIBMODS_OBJS="$LIBMODS_OBJS ${module}.lo" done AC_SUBST(LIBMODS_OBJS) # Output the static_modules.h file # must be in current directory, other paths may not exist yet. output_file="static_modules.h" ##----------------------------------------------------------------- cat >$output_file <<_MEOF /* * This file is generated by autoconf and is included by mod.c. * It allows mod.c to access all statically compiled pdsh_module * structures available. */ #ifndef _STATIC_MODULES_H #define _STATic_MODULES_H #if HAVE_CONFIG_H # include "config.h" #endif #if STATIC_MODULES /* module information structures */ _MEOF ##----------------------------------------------------------------- for i in $MODULES do echo "extern struct pdsh_module ${i}_module_info;" >> $output_file echo "extern int ${i}_module_priority;" >> $output_file done ##----------------------------------------------------------------- cat >>$output_file <<_MEOF /* * Array of all pdsh_module structures we are compiling */ struct pdsh_module *static_mods[[]] = { _MEOF ##----------------------------------------------------------------- for i in $MODULES do echo " &${i}_module_info," >> $output_file done ##----------------------------------------------------------------- cat >>$output_file <<_MEOF NULL }; /* * Names of all the module structures */ char *static_mod_names[[]] = { _MEOF ##----------------------------------------------------------------- for i in $MODULES do echo " \"${i}\"," >> $output_file done ##----------------------------------------------------------------- cat >>$output_file <<_MEOF NULL }; /* * Module priorities */ int *priority[[]] = { _MEOF ##----------------------------------------------------------------- for i in $MODULES do echo " &${i}_module_priority," >> $output_file done ##----------------------------------------------------------------- cat >>$output_file <<_MEOF NULL }; #endif /* STATIC_MODULES */ #endif /* _STATIC_MODULES_H */ _MEOF ##----------------------------------------------------------------- ]) pdsh-2.36/config/ac_msghdr_accrights.m40000664€^–Á €^–Á 0000000201715131211226026173 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id: ac_socklen_t.m4 552 2003-07-03 00:23:32Z grondo $ ##***************************************************************************** # AUTHOR: # Mark Grondona # # SYNOPSIS: # AC_MSGHDR_ACCRIGHTS # # DESCRIPTION: # Check whether sys/socket.h defines msghdr with accrights field. # Please note that some systems require sys/types.h to be included # before sys/socket.h can be compiled. ##***************************************************************************** AC_DEFUN([AC_MSGHDR_ACCRIGHTS], [AC_CACHE_CHECK([for msg_accrights in struct msghdr], ac_cv_msghdr_accrights, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [[struct msghdr m; m.msg_accrights = 0]])],[ac_cv_msghdr_accrights=yes],[ac_cv_msghdr_accrights=no]) ]) if test "$ac_cv_msghdr_accrights" = "yes"; then AC_DEFINE([HAVE_MSGHDR_ACCRIGHTS], [1], [Define if struct msghdr has msg_accrights]) fi ]) pdsh-2.36/config/ac_machines.m40000664€^–Á €^–Á 0000000250515131211226024451 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_MACHINES # # DESCRIPTION: # Adds support for the "--with-machines=" configure script option to # specify a flat file list of all nodes. # # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_MACHINES], [ AC_MSG_CHECKING([for path to machines file]) AC_ARG_WITH([machines], AS_HELP_STRING([--with-machines(=PATH)],[Specify a flat file list of all nodes]), [ case "$withval" in no) ac_have_machines=no ;; yes) ac_have_machines=yes MACHINES="/etc/machines" ;; *) ac_have_machines=yes MACHINES=$withval esac ] ) AC_MSG_RESULT([${ac_have_machines=no}]) : ${ac_have_machines=no} if test "$ac_have_machines" = yes; then AC_ADD_STATIC_MODULE("machines") AC_DEFINE([HAVE_MACHINES], [1], [Define if you have machines]) AC_DEFINE_UNQUOTED([_PATH_MACHINES], ["$MACHINES"], [Define to default machines file.]) fi AC_SUBST(HAVE_MACHINES) AC_SUBST(MACHINES) ]) pdsh-2.36/config/ac_ssh.m40000664€^–Á €^–Á 0000000357315131211226023465 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Albert Chu # # SYNOPSIS: # AC_SSH # # DESCRIPTION: # Check if user wants to compile sshcmd # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_SSH], [ # # Check for whether to include ssh module # AC_MSG_CHECKING([for whether to build ssh module]) AC_ARG_WITH([ssh], AS_HELP_STRING([--with-ssh],[Build ssh module]), [ case "$withval" in no) ac_with_ssh=no ;; yes) ac_with_ssh=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-ssh]) ;; esac ] ) AC_MSG_RESULT([${ac_with_ssh=no}]) if test "$ac_with_ssh" = "yes"; then ac_have_ssh=yes AC_ADD_STATIC_MODULE("sshcmd") AC_DEFINE([HAVE_SSH], [1], [Define if you have ssh.]) x_ac_ssh_connect_timeout_option="-oConnectTimeout=%d" AC_MSG_CHECKING([for ssh connect timeout option]) AC_ARG_WITH( [ssh-connect-timeout-option], AS_HELP_STRING(--with-ssh-connect-timeout-option=OPT, SSH option for connect timeout), [x_ac_ssh_connect_timeout_option=$withval]) if ! echo "$x_ac_ssh_connect_timeout_option" | grep -i '^no$' 2>/dev/null \ && ! echo "$x_ac_ssh_connect_timeout_option" | grep -i '^none$' 2>/dev/null; then AC_MSG_RESULT([${x_ac_ssh_connect_timeout_option}]) AC_DEFINE([SSH_HAS_CONNECT_TIMEOUT], [1], [Define if SSH supports a connect timeout option.]) AC_DEFINE_UNQUOTED( [SSH_CONNECT_TIMEOUT_OPTION], "$x_ac_ssh_connect_timeout_option", [Define to SSH connect timeout option]) fi fi AC_SUBST(HAVE_SSH) ]) pdsh-2.36/config/ac_slurm.m40000664€^–Á €^–Á 0000000251515131211226024025 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Mark Grondona # # SYNOPSIS: # AC_SLURM # # DESCRIPTION: # Checks for whether to include slurm module # ##***************************************************************************** AC_DEFUN([AC_SLURM], [ # # Check for whether to build slurm module # AC_MSG_CHECKING([for whether to build slurm module]) AC_ARG_WITH([slurm], AS_HELP_STRING([--with-slurm],[support running pdsh under SLURM allocation]), [ case "$withval" in yes) ac_with_slurm=yes ;; no) ac_with_slurm=no ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-slurm]) ;; esac ] ) AC_MSG_RESULT([${ac_with_slurm=no}]) if test "$ac_with_slurm" = "yes"; then AC_CHECK_LIB(slurm, slurm_load_jobs, [ac_have_libslurm=yes], []) if test "$ac_have_libslurm" != "yes"; then AC_MSG_NOTICE([Cannot support slurm without libslurm.]) else ac_have_slurm=yes AC_ADD_STATIC_MODULE("slurm") SLURM_LIBS="-lslurm" AC_DEFINE([HAVE_SLURM], [1], [Define if you have slurm.]) fi fi AC_SUBST(HAVE_SLURM) AC_SUBST(SLURM_LIBS) ]) pdsh-2.36/config/Make-inc.mk0000664€^–Á €^–Á 0000000170215131211226023730 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** ## Originally taken from Chris Dunlap's Munge project: ## $MungeID: Make-inc.mk,v 1.2 2003/04/30 18:44:14 dun Exp$ ##***************************************************************************** # Dependencies to ensure requisite libraries are rebuilt # $(top_builddir)/src/common/libcommon.la \ $(top_builddit)/src/modules/libmods.la \ : force-dependency-check @cd `dirname $@` && make `basename $@` force-dependency-check: # Generic ``distclean'' hook. # # The double-colon allows this target to be defined multiple times, # thereby allowing a Makefile.am to include its own distclean-local hook. # distclean-local:: -rm -f *~ \#* .\#* cscope*.out core *.core tags TAGS # # Code coverage flags AM_CFLAGS = \ $(CODE_COVERAGE_CFLAGS) AM_LDFLAGS = \ $(CODE_COVERAGE_LDFLAGS) pdsh-2.36/config/ac_torque.m40000664€^–Á €^–Á 0000000307215131211226024201 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** # AUTHOR: # Mattias Slabanja # # SYNOPSIS: # AC_TORQUE # # DESCRIPTION: # Checks for whether to include torque module # ##***************************************************************************** AC_DEFUN([AC_TORQUE], [ # # Check for whether to build torque module # AC_MSG_CHECKING([for whether to build torque module]) AC_ARG_WITH([torque], AS_HELP_STRING([--with-torque],[support running pdsh under Torque allocation]), [ case "$withval" in yes) ac_with_torque=yes ;; no) ac_with_torque=no ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-torque]) ;; esac ] ) AC_MSG_RESULT([${ac_with_torque=no}]) if test "$ac_with_torque" = "yes"; then if pbs-config 1>/dev/null 2>&1; then TORQUE_LIBS=$(pbs-config --libs) TORQUE_CPPFLAGS=$(pbs-config --cflags) saveLIBS="$LIBS" LIBS="$TORQUE_LIBS $LIBS" AC_CHECK_LIB(torque, pbs_connect, [ac_have_libtorque=yes], []) LIBS="$saveLIBS" if test "$ac_have_libtorque" != "yes"; then AC_MSG_NOTICE([Cannot support torque without libtorque.]) else ac_have_torque=yes AC_ADD_STATIC_MODULE("torque") AC_DEFINE([HAVE_TORQUE], [1], [Define if you have torque.]) fi else AC_MSG_NOTICE([Cannot find pbs-config. Torque module will not be build.]) fi fi AC_SUBST(HAVE_TORQUE) AC_SUBST(TORQUE_LIBS) AC_SUBST(TORQUE_CPPFLAGS) ]) pdsh-2.36/config/ac_dmalloc.m40000664€^–Á €^–Á 0000000217615131211226024301 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_DMALLOC # # DESCRIPTION: # Adds support for --with-dmalloc. # # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_DMALLOC], [ AC_MSG_CHECKING([if malloc debugging is wanted]) AC_ARG_WITH(dmalloc, AS_HELP_STRING([--with-dmalloc],[compile using Gray Watson's dmalloc]), [ case "$withval" in yes) ac_with_dmalloc=yes ;; no) ac_with_dmalloc=no ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --withdmalloc]) ;; esac ] ) AC_MSG_RESULT([${ac_with_dmalloc=no}]) if test "$ac_with_dmalloc" = "yes"; then AC_CHECK_LIB([dmalloc], [xmalloc],, AC_MSG_ERROR([Cannot find libdmalloc!])) AC_DEFINE(WITH_DMALLOC, 1, [Define if using dmalloc debugging malloc package.]) fi ]) pdsh-2.36/config/depcomp0000775€^–Á €^–Á 0000003554515131211226023344 0ustar arif.ali@canonical.comarif.ali@canonical.com#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2004-05-31.23 # Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try \`$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by `PROGRAMS ARGS'. object Object file output by `PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputing dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit 0 ;; -v | --v*) echo "depcomp $scriptversion" exit 0 ;; esac if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ## The second -e expression handles DOS-style file names with drive letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the `deleted header file' problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. tr ' ' ' ' < "$tmpdepfile" | ## Some versions of gcc put a space before the `:'. On the theory ## that the space means something, we add a space to the output as ## well. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like `#:fec' to the end of the # dependency line. tr ' ' ' ' < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ tr ' ' ' ' >> $depfile echo >> $depfile # The second pass generates a dummy entry for each header file. tr ' ' ' ' < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> $depfile else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile # "include basename.Plo" scheme. echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts `$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` tmpdepfile="$stripped.u" if test "$libtool" = yes; then "$@" -Wc,-M else "$@" -M fi stat=$? if test -f "$tmpdepfile"; then : else stripped=`echo "$stripped" | sed 's,^.*/,,'` tmpdepfile="$stripped.u" fi if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi if test -f "$tmpdepfile"; then outname="$stripped.o" # Each line is of the form `foo.o: dependent.h'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile # "include basename.Plo" scheme. echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; icc) # Intel's C compiler understands `-MD -MF file'. However on # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c # ICC 7.0 will fill foo.d with something like # foo.o: sub/foo.c # foo.o: sub/foo.h # which is wrong. We want: # sub/foo.o: sub/foo.c # sub/foo.o: sub/foo.h # sub/foo.c: # sub/foo.h: # ICC 7.1 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using \ : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in `foo.d' instead, so we check for that too. # Subdirectories are respected. dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` test "x$dir" = "x$object" && dir= base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` if test "$libtool" = yes; then # Dependencies are output in .lo.d with libtool 1.4. # With libtool 1.5 they are output both in $dir.libs/$base.o.d # and in $dir.libs/$base.o.d and $dir$base.o.d. We process the # latter, because the former will be cleaned when $dir.libs is # erased. tmpdepfile1="$dir.libs/$base.lo.d" tmpdepfile2="$dir$base.o.d" tmpdepfile3="$dir.libs/$base.d" "$@" -Wc,-MD else tmpdepfile1="$dir$base.o.d" tmpdepfile2="$dir$base.d" tmpdepfile3="$dir$base.d" "$@" -MD fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi if test -f "$tmpdepfile1"; then tmpdepfile="$tmpdepfile1" elif test -f "$tmpdepfile2"; then tmpdepfile="$tmpdepfile2" else tmpdepfile="$tmpdepfile3" fi if test -f "$tmpdepfile"; then sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" # That's a tab and a space in the []. sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" else echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test $1 != '--mode=compile'; do shift done shift fi # Remove `-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for `:' # in the target name. This is to cope with DOS-style filenames: # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. "$@" $dashmflag | sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" tr ' ' ' ' < "$tmpdepfile" | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test $1 != '--mode=compile'; do shift done shift fi # X makedepend shift cleared=no for arg in "$@"; do case $cleared in no) set ""; shift cleared=yes ;; esac case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix="`echo $object | sed 's/^.*\././'`" touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" sed '1,2d' "$tmpdepfile" | tr ' ' ' ' | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test $1 != '--mode=compile'; do shift done shift fi # Remove `-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E | sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o, # because we must use -o when running libtool. "$@" || exit $? IFS=" " for arg do case "$arg" in "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" echo " " >> "$depfile" . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: pdsh-2.36/config/ac_nodeupdown.m40000664€^–Á €^–Á 0000000476115131211226025052 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_NODEUPDOWN # # DESCRIPTION: # Checks for nodeupdown support. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_NODEUPDOWN], [ # # Check for whether to include libnodeupdown module # AC_MSG_CHECKING([for whether to build nodeupdown module]) AC_ARG_WITH([nodeupdown], AS_HELP_STRING([--with-nodeupdown],[Build nodeupdown module]), [ case "$withval" in no) ac_with_libnodeupdown=no ;; yes) ac_with_libnodeupdown=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-nodeupdown]) ;; esac ] ) AC_MSG_RESULT([${ac_with_libnodeupdown=no}]) if test "$ac_with_libnodeupdown" = "yes"; then AC_CHECK_LIB([nodeupdown], [nodeupdown_handle_create], [ac_found_libnodeupdown=yes], []) if test "$ac_found_libnodeupdown" != "yes" ; then AC_MSG_NOTICE([Cannot support nodeupdown without libnodeupdown]) else # Which nodeupdown API version do we have? AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[nodeupdown_load_data(NULL, NULL, NULL, NULL, 0,0);]])],[ac_nodeupdown_load_data_6=yes],[ac_nodeupdown_load_data_6=no]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[nodeupdown_load_data(NULL, NULL, 0, 0, NULL);]])],[ac_nodeupdown_load_data_5=yes],[ac_nodeupdown_load_data_5=no]) if test "$ac_nodeupdown_load_data_6" = "yes"; then AC_DEFINE(HAVE_NODEUPDOWN_LOAD_DATA_6, 1, [6 param nodeupdown_load_data]) ac_have_libnodeupdown=yes elif test "$ac_nodeupdown_load_data_5" = "yes"; then AC_DEFINE(HAVE_NODEUPDOWN_LOAD_DATA_5, 1, [5 param nodeupdown_load_data]) ac_have_libnodeupdown=yes else AC_MSG_NOTICE([Unnkown libnodeupdown library]) fi if test "$ac_have_libnodeupdown" = "yes"; then AC_ADD_STATIC_MODULE("nodeupdown") AC_DEFINE([HAVE_LIBNODEUPDOWN], [1], [Define if you have libnodeupdown.]) NODEUPDOWN_LIBS="-lnodeupdown" fi fi fi AC_SUBST(HAVE_LIBNODEUPDOWN) AC_SUBST(NODEUPDOWN_LIBS) ]) pdsh-2.36/config/ac_xcpu.m40000664€^–Á €^–Á 0000000210615131211226023636 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id:$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_XCPU # # DESCRIPTION: # Checks for xcpu # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_XCPU], [ # # Check for whether to include xcpu module # AC_MSG_CHECKING([for whether to build xcpu module]) AC_ARG_WITH([xcpu], AS_HELP_STRING([--with-xcpu],[Build xcpu module]), [ case "$withval" in no) ac_with_xcpu=no ;; yes) ac_with_xcpu=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-xcpu]) ;; esac ] ) AC_MSG_RESULT([${ac_with_xcpu=no}]) if test "$ac_with_xcpu" = "yes"; then ac_have_xcpu=yes AC_ADD_STATIC_MODULE("xcpucmd") AC_DEFINE([HAVE_XCPU], [1], [Define if you have XCPU.]) fi AC_SUBST(HAVE_XCPU) ]) pdsh-2.36/config/ac_exec.m40000664€^–Á €^–Á 0000000176715131211226023617 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Albert Chu # # SYNOPSIS: # AC_EXEC # # DESCRIPTION: # Check if user wants to compile execcmd # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_EXEC], [ # # Check for whether to include exec module # AC_MSG_CHECKING([for whether to build exec module]) AC_ARG_WITH([exec], AS_HELP_STRING([--with-exec],[Build exec module]), [ case "$withval" in no) ac_with_exec=no ;; yes) ac_with_exec=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-exec]) ;; esac ] ) AC_MSG_RESULT([${ac_with_exec=yes}]) if test "$ac_with_exec" = "yes"; then ac_have_exec=yes AC_ADD_STATIC_MODULE("execcmd") fi ]) pdsh-2.36/config/Make-test.mk0000664€^–Á €^–Á 0000000216615131211226024143 0ustar arif.ali@canonical.comarif.ali@canonical.comDEJATOOL = `pwd`/$(PROJECT) RUNTESTFLAGS = RUNTESTDEFAULTFLAGS = --all --tool $(PROJECT) --srcdir $$srcdir/testsuite EXPECT = /usr/bin/expect RUNTEST = /usr/bin/runtest srcdir = $(top_srcdir) check: site.exp @srcdir=`cd $(srcdir) && pwd`; export srcdir; \ EXPECT=$(EXPECT); export EXPECT; \ if [ -x $(RUNTEST) ]; then \ $(RUNTEST) $(RUNTESTDEFAULTFLAGS) $(RUNTESTFLAGS); exit 0; \ else \ echo "Could not find $(RUNTEST). Is dejagnu installed?" 1>&2; :;\ fi site.exp: Makefile @echo 'Making a new site.exp file...' @test ! -f site.bak || rm -f site.bak @echo '## these variables are automatically generated by make ##' > $@-t @echo '# Do not edit here. If you wish to override these values' >> $@-t @echo '# edit the last section' >> $@-t @echo 'set tool $(DEJATOOL)' >> $@-t @echo 'set srcdir $(srcdir)' >> $@-t @echo 'set objdir' `pwd` >> $@-t @echo '## All variables above are generated by configure. Do Not Edit ##' >> $@-t @test ! -f site.exp || sed '1,/^## All variables above are.*##/ d' site.exp >> $@-t @test ! -f site.exp || mv site.exp site.bak @mv $@-t site.exp pdsh-2.36/config/ac_pollselect.m40000664€^–Á €^–Á 0000000214315131211226025026 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Albert Chu # # SYNOPSIS: # AC_POLLSELECT # # DESCRIPTION: # Checks for poll() and select() and determines which to use. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_POLLSELECT], [ AC_CHECK_FUNC([poll], [ac_have_poll=yes], [ac_have_poll=no]) if test "$ac_have_poll" = "yes" ; then AC_DEFINE([HAVE_POLL], [1], [Define that you will use poll()]) else AC_CHECK_FUNC([select], [ac_have_select=yes], [ac_have_select=no]) if test "$ac_have_select" = "yes" ; then AC_MSG_WARN([System does not support poll(), default to select()]) AC_DEFINE([HAVE_SELECT], [1], [Define that you will use select()]) else AC_MSG_ERROR([System does not support select() or poll(), get a real operating system!!!]) fi fi ]) pdsh-2.36/config/ac_connect_timeout.m40000664€^–Á €^–Á 0000000204315131211226026056 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_CONNECT_TIMEOUT # # DESCRIPTION: # Adds support for the "--with-connect-timeout=" configure script # option to specify the default pdsh connect timeout. # # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_CONNECT_TIMEOUT], [ AC_MSG_CHECKING([for default connect timeout]) AC_ARG_WITH([timeout], AS_HELP_STRING([--with-timeout=N],[Specify default connect timeout (secs)]), [ case "$withval" in no) CONNECT_TIMEOUT=0 ;; *) CONNECT_TIMEOUT=$withval ;; esac ] ) AC_MSG_RESULT([${CONNECT_TIMEOUT=10}]) AC_DEFINE_UNQUOTED(CONNECT_TIMEOUT, $CONNECT_TIMEOUT, [Define to default pdsh connect timeout.]) AC_SUBST(CONNECT_TIMEOUT) ]) pdsh-2.36/config/ac_rcmd_rank_list.m40000664€^–Á €^–Á 0000000257315131211226025662 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Mark Grondona # # SYNOPSIS: # AC_RCMD_RANK_LIST # # DESCRIPTION: # Create user # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_RCMD_RANK_LIST], [ AC_ARG_WITH([rcmd-rank-list], AS_HELP_STRING([--with-rcmd-rank-list], [Specify priority ordered list of rcmd modules. Default is mrsh,rsh,ssh,krb4,exec,xcpu]), [ for t in `echo $withval | tr "," " "`; do if echo mrsh,rsh,ssh,krb4,qsh,mqsh,exec,xcpu | grep -q $t; then if test -z "$ac_cv_rcmd_rank_list" ; then ac_cv_rcmd_rank_list=\"$t\" else ac_cv_rcmd_rank_list="$ac_cv_rcmd_rank_list, \"$t\"" fi else AC_MSG_ERROR([Invalid rcmd type $t!]) fi done ], []) AC_MSG_CHECKING([rcmd rank list]) if test -z "$ac_cv_rcmd_rank_list"; then ac_cv_rcmd_rank_list='"mrsh", "rsh", "ssh", "krb4", "exec", "xcpu"' fi AC_MSG_RESULT([$ac_cv_rcmd_rank_list]) AC_DEFINE_UNQUOTED(RCMD_RANK_LIST, $ac_cv_rcmd_rank_list, [Define to quoted, comma-separated, priority-ordered list of rcmd types]) ]) pdsh-2.36/config/ac_netgroup.m40000664€^–Á €^–Á 0000000221215131211226024520 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id: ac_dshgroup.m4 771 2004-02-11 00:12:20Z grondo $ ##***************************************************************************** # AUTHOR: # Mark Grondona # # SYNOPSIS: # AC_NETGROUP # # DESCRIPTION: # Checks for whether to include the standard dshgroup module. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_NETGROUP], [ # # Check for whether to include the dshgroup module # AC_MSG_CHECKING([for whether to build netgroup module]) AC_ARG_WITH([netgroup], AS_HELP_STRING([--with-netgroup],[Build netgroup module for netgroups support]), [ case "$withval" in no) ac_with_netgroup=no ;; yes) ac_with_netgroup=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-netgroup]) ;; esac ] ) AC_MSG_RESULT([${ac_with_netgroup=no}]) if test "$ac_with_netgroup" = "yes"; then AC_ADD_STATIC_MODULE("netgroup") fi ]) pdsh-2.36/config/ac_dshgroup.m40000664€^–Á €^–Á 0000000241215131211226024512 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id: ac_dshgroup.m4 771 2004-02-11 00:12:20Z grondo $ ##***************************************************************************** # AUTHOR: # Mark Grondona # # SYNOPSIS: # AC_DSHGROUP # # DESCRIPTION: # Checks for whether to include the standard dshgroup module. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_DSHGROUP], [ # # Check for whether to include the dshgroup module # AC_MSG_CHECKING([for whether to build dshgroup module]) ac_dshgroup_path="/etc/dsh/group"; AC_ARG_WITH([dshgroups], AS_HELP_STRING([--with-dshgroups(=PATH)],[Build dshgroup module for dsh group file support (with optional PATH)]), [ case "$withval" in no) ac_with_dshgroup=no ;; yes) ac_with_dshgroup=yes ;; *) ac_with_dshgroup=yes; ac_dshgroup_path="$withval" ;; esac ] ) AC_MSG_RESULT([${ac_with_dshgroup=no}]) if test "$ac_with_dshgroup" = "yes"; then AC_ADD_STATIC_MODULE("dshgroup") AC_DEFINE_UNQUOTED(DSHGROUP_PATH, "$ac_dshgroup_path", [Path to dshgroup files]) fi ]) pdsh-2.36/config/ac_fanout.m40000664€^–Á €^–Á 0000000163315131211226024157 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_FANOUT # # DESCRIPTION: # Adds support for the "--with-fanout=" configure script option to # specify the default pdsh fanout. # # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_FANOUT], [ AC_MSG_CHECKING([for default fanout]) AC_ARG_WITH([fanout], AS_HELP_STRING([--with-fanout=N],[Specify default fanout]), [ case "$withval" in no) FANOUT=1 ;; *) FANOUT=$withval ;; esac ] ) AC_MSG_RESULT([${FANOUT=32}]) AC_DEFINE_UNQUOTED(DFLT_FANOUT, $FANOUT, [Define to default pdsh fanout.]) AC_SUBST(FANOUT) ]) pdsh-2.36/config/ac_gpl_licensed.m40000664€^–Á €^–Á 0000000110115131211226025301 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Chris Dunlap # # SYNOPSIS: # AC_GPL_LICENSED # # DESCRIPTION: # Acknowledge being licensed under terms of the GNU General Public License. ##***************************************************************************** AC_DEFUN([AC_GPL_LICENSED], [ AC_DEFINE([GPL_LICENSED], [1], [Define to 1 if licensed under terms of the GNU General Public License.] ) ]) pdsh-2.36/config/ac_readline.m40000664€^–Á €^–Á 0000000250715131211226024447 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_READLINE # # DESCRIPTION: # Adds support for --with-readline. Exports READLINE_LIBS if found # # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_READLINE], [ AC_MSG_CHECKING([for whether to include readline suport]) AC_ARG_WITH([readline], AS_HELP_STRING([--with-readline],[compile with readline support]), [ case "$withval" in yes) ac_with_readline=yes ;; no) ac_with_readline=no ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-readline]) ;; esac ] ) AC_MSG_RESULT([${ac_with_readline=no}]) if test "$ac_with_readline" = "yes"; then savedLIBS="$LIBS" READLINE_LIBS="-lreadline -lhistory -lncurses" AC_CHECK_LIB([readline], [readline], [], AC_MSG_ERROR([Cannot find libreadline!]), [ -lhistory -lncurses ]) AC_DEFINE([HAVE_READLINE], [1], [Define if you are compiling with readline.]) LIBS="$savedLIBS" fi AC_SUBST(READLINE_LIBS) ]) pdsh-2.36/config/ac_rsh.m40000664€^–Á €^–Á 0000000223215131211226023453 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Mark Grondona # # SYNOPSIS: # AC_MRSH # # DESCRIPTION: # Checks for whether to include the standard "rsh" module. By default, # this module is included, but may be disabled by passing --without-rsh # to the configure script. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_RSH], [ # # Check for whether to include the standard rsh module # AC_MSG_CHECKING([for whether to build rsh module]) AC_ARG_WITH([rsh], AS_HELP_STRING([--without-rsh],[Do not include the standard rsh module]), [ case "$withval" in no) ac_with_rsh=no ;; yes) ac_with_rsh=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-rsh]) ;; esac ] ) AC_MSG_RESULT([${ac_with_rsh=yes}]) if test "$ac_with_rsh" = "yes"; then AC_ADD_STATIC_MODULE("xrcmd") fi ]) pdsh-2.36/config/acx_pthread.m40000664€^–Á €^–Á 0000002546015131211226024506 0ustar arif.ali@canonical.comarif.ali@canonical.com# =========================================================================== # http://autoconf-archive.cryp.to/acx_pthread.html # =========================================================================== # # SYNOPSIS # # ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # LAST MODIFICATION # # 2008-04-12 # # COPYLEFT # # Copyright (c) 2008 Steven G. Johnson # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Macro Archive. When you make and # distribute a modified version of the Autoconf Macro, you may extend this # special exception to the GPL to apply to your modified version as well. AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH([C]) acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ]])],[acx_pthread_ok=yes],[]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int attr=$attr; return attr;]])],[attr_name=$attr; break],[]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_POP([]) ])dnl ACX_PTHREAD pdsh-2.36/config/ac_mrsh.m40000664€^–Á €^–Á 0000000261315131211226023633 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_MRSH # # DESCRIPTION: # Checks for mrsh # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_MRSH], [ # # Check for whether to include mrsh module # AC_MSG_CHECKING([for whether to build mrsh module]) AC_ARG_WITH([mrsh], AS_HELP_STRING([--with-mrsh],[Build mrsh module]), [ case "$withval" in no) ac_with_mrsh=no ;; yes) ac_with_mrsh=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-mrsh]) ;; esac ] ) AC_MSG_RESULT([${ac_with_mrsh=no}]) if test "$ac_with_mrsh" = "yes"; then # is libmunge installed? AC_CHECK_LIB([munge], [munge_encode], [ac_have_libmunge=yes], []) if test "$ac_have_libmunge" != "yes" ; then AC_MSG_NOTICE([Cannot support mrsh without libmunge]) fi if test "$ac_have_libmunge" = "yes" ; then ac_have_mrsh=yes AC_ADD_STATIC_MODULE("mcmd") MRSH_LIBS="-lmunge" AC_DEFINE([HAVE_MRSH], [1], [Define if you have mrsh.]) fi fi AC_SUBST(HAVE_MRSH) AC_SUBST(MRSH_LIBS) ]) pdsh-2.36/config/ac_genders.m40000664€^–Á €^–Á 0000000330615131211226024311 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_GENDERS # # DESCRIPTION: # Checks for genders support. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_GENDERS], [ # # Check for whether to include libgenders module # AC_MSG_CHECKING([for whether to build genders module]) AC_ARG_WITH([genders], AS_HELP_STRING([--with-genders],[Build genders module for libgenders support]), [ case "$withval" in no) ac_with_libgenders=no ;; yes) ac_with_libgenders=yes ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$withval" for --with-genders]) ;; esac ] ) AC_MSG_RESULT([${ac_with_libgenders=no}]) if test "$ac_with_libgenders" = "yes"; then AC_CHECK_LIB([genders], [genders_handle_create], [ac_have_libgenders=yes], []) if test "$ac_have_libgenders" != "yes" ; then AC_MSG_NOTICE([Cannot support genders without libgenders]) fi if test "$ac_have_libgenders" = "yes"; then AC_ADD_STATIC_MODULE("genders") AC_DEFINE([HAVE_LIBGENDERS], [1], [Define if you have libgenders.]) GENDERS_LIBS="-lgenders" AC_CHECK_LIB([genders], [genders_query], [AC_DEFINE( [HAVE_GENDERS_QUERY], [1], [Define if genders library has genders_query.])], [ac_have_genders_query=no] ) fi fi AC_SUBST(HAVE_LIBGENDERS) AC_SUBST(GENDERS_LIBS) ]) pdsh-2.36/config/ac_socklen_t.m40000664€^–Á €^–Á 0000000201515131211226024637 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Lars Brinkhoff # # SYNOPSIS: # TYPE_SOCKLEN_T # # DESCRIPTION: # Check whether sys/socket.h defines type socklen_t. # Please note that some systems require sys/types.h to be included # before sys/socket.h can be compiled. ##***************************************************************************** AC_DEFUN([TYPE_SOCKLEN_T], [AC_CACHE_CHECK([for socklen_t], ac_cv_type_socklen_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [[socklen_t len = 42; return 0;]])],[ac_cv_type_socklen_t=yes],[ac_cv_type_socklen_t=no]) ]) if test "$ac_cv_type_socklen_t" = "yes"; then AC_DEFINE([HAVE_SOCKLEN_T], [1], [Define if you have the socklen_t type.]) fi AH_VERBATIM([HAVE_SOCKLEN_T_], [#ifndef HAVE_SOCKLEN_T # define HAVE_SOCKLEN_T typedef int socklen_t; #endif]) ]) pdsh-2.36/config/tap-driver.sh0000775€^–Á €^–Á 0000004601015131211226024370 0ustar arif.ali@canonical.comarif.ali@canonical.com#! /bin/sh # Copyright (C) 2011-2013 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . scriptversion=2013-12-23.17; # UTC # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u me=tap-driver.sh fatal () { echo "$me: fatal: $*" >&2 exit 1 } usage_error () { echo "$me: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat < # trap : 1 3 2 13 15 if test $merge -gt 0; then exec 2>&1 else exec 2>&3 fi "$@" echo $? ) | LC_ALL=C ${AM_TAP_AWK-awk} \ -v me="$me" \ -v test_script_name="$test_name" \ -v log_file="$log_file" \ -v trs_file="$trs_file" \ -v expect_failure="$expect_failure" \ -v merge="$merge" \ -v ignore_exit="$ignore_exit" \ -v comments="$comments" \ -v diag_string="$diag_string" \ ' # TODO: the usages of "cat >&3" below could be optimized when using # GNU awk, and/on on systems that supports /dev/fd/. # Implementation note: in what follows, `result_obj` will be an # associative array that (partly) simulates a TAP result object # from the `TAP::Parser` perl module. ## ----------- ## ## FUNCTIONS ## ## ----------- ## function fatal(msg) { print me ": " msg | "cat >&2" exit 1 } function abort(where) { fatal("internal error " where) } # Convert a boolean to a "yes"/"no" string. function yn(bool) { return bool ? "yes" : "no"; } function add_test_result(result) { if (!test_results_index) test_results_index = 0 test_results_list[test_results_index] = result test_results_index += 1 test_results_seen[result] = 1; } # Whether the test script should be re-run by "make recheck". function must_recheck() { for (k in test_results_seen) if (k != "XFAIL" && k != "PASS" && k != "SKIP") return 1 return 0 } # Whether the content of the log file associated to this test should # be copied into the "global" test-suite.log. function copy_in_global_log() { for (k in test_results_seen) if (k != "PASS") return 1 return 0 } function get_global_test_result() { if ("ERROR" in test_results_seen) return "ERROR" if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) return "FAIL" all_skipped = 1 for (k in test_results_seen) if (k != "SKIP") all_skipped = 0 if (all_skipped) return "SKIP" return "PASS"; } function stringify_result_obj(result_obj) { if (result_obj["is_unplanned"] || result_obj["number"] != testno) return "ERROR" if (plan_seen == LATE_PLAN) return "ERROR" if (result_obj["directive"] == "TODO") return result_obj["is_ok"] ? "XPASS" : "XFAIL" if (result_obj["directive"] == "SKIP") return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; if (length(result_obj["directive"])) abort("in function stringify_result_obj()") return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL } function decorate_result(result) { color_name = color_for_result[result] if (color_name) return color_map[color_name] "" result "" color_map["std"] # If we are not using colorized output, or if we do not know how # to colorize the given result, we should return it unchanged. return result } function report(result, details) { if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) { msg = ": " test_script_name add_test_result(result) } else if (result == "#") { msg = " " test_script_name ":" } else { abort("in function report()") } if (length(details)) msg = msg " " details # Output on console might be colorized. print decorate_result(result) msg # Log the result in the log file too, to help debugging (this is # especially true when said result is a TAP error or "Bail out!"). print result msg | "cat >&3"; } function testsuite_error(error_message) { report("ERROR", "- " error_message) } function handle_tap_result() { details = result_obj["number"]; if (length(result_obj["description"])) details = details " " result_obj["description"] if (plan_seen == LATE_PLAN) { details = details " # AFTER LATE PLAN"; } else if (result_obj["is_unplanned"]) { details = details " # UNPLANNED"; } else if (result_obj["number"] != testno) { details = sprintf("%s # OUT-OF-ORDER (expecting %d)", details, testno); } else if (result_obj["directive"]) { details = details " # " result_obj["directive"]; if (length(result_obj["explanation"])) details = details " " result_obj["explanation"] } report(stringify_result_obj(result_obj), details) } # `skip_reason` should be empty whenever planned > 0. function handle_tap_plan(planned, skip_reason) { planned += 0 # Avoid getting confused if, say, `planned` is "00" if (length(skip_reason) && planned > 0) abort("in function handle_tap_plan()") if (plan_seen) { # Error, only one plan per stream is acceptable. testsuite_error("multiple test plans") return; } planned_tests = planned # The TAP plan can come before or after *all* the TAP results; we speak # respectively of an "early" or a "late" plan. If we see the plan line # after at least one TAP result has been seen, assume we have a late # plan; in this case, any further test result seen after the plan will # be flagged as an error. plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) # If testno > 0, we have an error ("too many tests run") that will be # automatically dealt with later, so do not worry about it here. If # $plan_seen is true, we have an error due to a repeated plan, and that # has already been dealt with above. Otherwise, we have a valid "plan # with SKIP" specification, and should report it as a particular kind # of SKIP result. if (planned == 0 && testno == 0) { if (length(skip_reason)) skip_reason = "- " skip_reason; report("SKIP", skip_reason); } } function extract_tap_comment(line) { if (index(line, diag_string) == 1) { # Strip leading `diag_string` from `line`. line = substr(line, length(diag_string) + 1) # And strip any leading and trailing whitespace left. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # Return what is left (if any). return line; } return ""; } # When this function is called, we know that line is a TAP result line, # so that it matches the (perl) RE "^(not )?ok\b". function setup_result_obj(line) { # Get the result, and remove it from the line. result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) sub("^(not )?ok[ \t]*", "", line) # If the result has an explicit number, get it and strip it; otherwise, # automatically assing the next progresive number to it. if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) { match(line, "^[0-9]+") # The final `+ 0` is to normalize numbers with leading zeros. result_obj["number"] = substr(line, 1, RLENGTH) + 0 line = substr(line, RLENGTH + 1) } else { result_obj["number"] = testno } if (plan_seen == LATE_PLAN) # No further test results are acceptable after a "late" TAP plan # has been seen. result_obj["is_unplanned"] = 1 else if (plan_seen && testno > planned_tests) result_obj["is_unplanned"] = 1 else result_obj["is_unplanned"] = 0 # Strip trailing and leading whitespace. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # This will have to be corrected if we have a "TODO"/"SKIP" directive. result_obj["description"] = line result_obj["directive"] = "" result_obj["explanation"] = "" if (index(line, "#") == 0) return # No possible directive, nothing more to do. # Directives are case-insensitive. rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" # See whether we have the directive, and if yes, where. pos = match(line, rx "$") if (!pos) pos = match(line, rx "[^a-zA-Z0-9_]") # If there was no TAP directive, we have nothing more to do. if (!pos) return # Let`s now see if the TAP directive has been escaped. For example: # escaped: ok \# SKIP # not escaped: ok \\# SKIP # escaped: ok \\\\\# SKIP # not escaped: ok \ # SKIP if (substr(line, pos, 1) == "#") { bslash_count = 0 for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) bslash_count += 1 if (bslash_count % 2) return # Directive was escaped. } # Strip the directive and its explanation (if any) from the test # description. result_obj["description"] = substr(line, 1, pos - 1) # Now remove the test description from the line, that has been dealt # with already. line = substr(line, pos) # Strip the directive, and save its value (normalized to upper case). sub("^[ \t]*#[ \t]*", "", line) result_obj["directive"] = toupper(substr(line, 1, 4)) line = substr(line, 5) # Now get the explanation for the directive (if any), with leading # and trailing whitespace removed. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) result_obj["explanation"] = line } function get_test_exit_message(status) { if (status == 0) return "" if (status !~ /^[1-9][0-9]*$/) abort("getting exit status") if (status < 127) exit_details = "" else if (status == 127) exit_details = " (command not found?)" else if (status >= 128 && status <= 255) exit_details = sprintf(" (terminated by signal %d?)", status - 128) else if (status > 256 && status <= 384) # We used to report an "abnormal termination" here, but some Korn # shells, when a child process die due to signal number n, can leave # in $? an exit status of 256+n instead of the more standard 128+n. # Apparently, both behaviours are allowed by POSIX (2008), so be # prepared to handle them both. See also Austing Group report ID # 0000051 exit_details = sprintf(" (terminated by signal %d?)", status - 256) else # Never seen in practice. exit_details = " (abnormal termination)" return sprintf("exited with status %d%s", status, exit_details) } function write_test_results() { print ":global-test-result: " get_global_test_result() > trs_file print ":recheck: " yn(must_recheck()) > trs_file print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file for (i = 0; i < test_results_index; i += 1) print ":test-result: " test_results_list[i] > trs_file close(trs_file); } BEGIN { ## ------- ## ## SETUP ## ## ------- ## '"$init_colors"' # Properly initialized once the TAP plan is seen. planned_tests = 0 COOKED_PASS = expect_failure ? "XPASS": "PASS"; COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; # Enumeration-like constants to remember which kind of plan (if any) # has been seen. It is important that NO_PLAN evaluates "false" as # a boolean. NO_PLAN = 0 EARLY_PLAN = 1 LATE_PLAN = 2 testno = 0 # Number of test results seen so far. bailed_out = 0 # Whether a "Bail out!" directive has been seen. # Whether the TAP plan has been seen or not, and if yes, which kind # it is ("early" is seen before any test result, "late" otherwise). plan_seen = NO_PLAN ## --------- ## ## PARSING ## ## --------- ## is_first_read = 1 while (1) { # Involutions required so that we are able to read the exit status # from the last input line. st = getline if (st < 0) # I/O error. fatal("I/O error while reading from input stream") else if (st == 0) # End-of-input { if (is_first_read) abort("in input loop: only one input line") break } if (is_first_read) { is_first_read = 0 nextline = $0 continue } else { curline = nextline nextline = $0 $0 = curline } # Copy any input line verbatim into the log file. print | "cat >&3" # Parsing of TAP input should stop after a "Bail out!" directive. if (bailed_out) continue # TAP test result. if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) { testno += 1 setup_result_obj($0) handle_tap_result() } # TAP plan (normal or "SKIP" without explanation). else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) { # The next two lines will put the number of planned tests in $0. sub("^1\\.\\.", "") sub("[^0-9]*$", "") handle_tap_plan($0, "") continue } # TAP "SKIP" plan, with an explanation. else if ($0 ~ /^1\.\.0+[ \t]*#/) { # The next lines will put the skip explanation in $0, stripping # any leading and trailing whitespace. This is a little more # tricky in truth, since we want to also strip a potential leading # "SKIP" string from the message. sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") sub("[ \t]*$", ""); handle_tap_plan(0, $0) } # "Bail out!" magic. # Older versions of prove and TAP::Harness (e.g., 3.17) did not # recognize a "Bail out!" directive when preceded by leading # whitespace, but more modern versions (e.g., 3.23) do. So we # emulate the latter, "more modern" behaviour. else if ($0 ~ /^[ \t]*Bail out!/) { bailed_out = 1 # Get the bailout message (if any), with leading and trailing # whitespace stripped. The message remains stored in `$0`. sub("^[ \t]*Bail out![ \t]*", ""); sub("[ \t]*$", ""); # Format the error message for the bailout_message = "Bail out!" if (length($0)) bailout_message = bailout_message " " $0 testsuite_error(bailout_message) } # Maybe we have too look for dianogtic comments too. else if (comments != 0) { comment = extract_tap_comment($0); if (length(comment)) report("#", comment); } } ## -------- ## ## FINISH ## ## -------- ## # A "Bail out!" directive should cause us to ignore any following TAP # error, as well as a non-zero exit status from the TAP producer. if (!bailed_out) { if (!plan_seen) { testsuite_error("missing test plan") } else if (planned_tests != testno) { bad_amount = testno > planned_tests ? "many" : "few" testsuite_error(sprintf("too %s tests run (expected %d, got %d)", bad_amount, planned_tests, testno)) } if (!ignore_exit) { # Fetch exit status from the last line. exit_message = get_test_exit_message(nextline) if (exit_message) testsuite_error(exit_message) } } write_test_results() exit 0 } # End of "BEGIN" block. ' # TODO: document that we consume the file descriptor 3 :-( } 3>"$log_file" test $? -eq 0 || fatal "I/O or internal error" # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: pdsh-2.36/config/mkinstalldirs0000775€^–Á €^–Á 0000000370415131211226024565 0ustar arif.ali@canonical.comarif.ali@canonical.com#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Public domain errstatus=0 dirmode="" usage="\ Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." # process command line arguments while test $# -gt 0 ; do case $1 in -h | --help | --h*) # -h for help echo "$usage" 1>&2 exit 0 ;; -m) # -m PERM arg shift test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } dirmode=$1 shift ;; --) # stop option processing shift break ;; -*) # unknown option echo "$usage" 1>&2 exit 1 ;; *) # first non-opt arg break ;; esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac case $dirmode in '') if mkdir -p -- . 2>/dev/null; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" fi ;; *) if mkdir -m "$dirmode" -p -- . 2>/dev/null; then echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" fi ;; esac for file do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d do pathcomp="$pathcomp$d" case $pathcomp in -*) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr else if test ! -z "$dirmode"; then echo "chmod $dirmode $pathcomp" lasterr="" chmod "$dirmode" "$pathcomp" || lasterr=$? if test ! -z "$lasterr"; then errstatus=$lasterr fi fi fi fi pathcomp="$pathcomp/" done done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 2 # End: # mkinstalldirs ends here pdsh-2.36/config/ac_krb4.m40000664€^–Á €^–Á 0000000172515131211226023527 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Jim Garlick # # SYNOPSIS: # AC_KRB4 # # DESCRIPTION: # Adds support for kerberos rcmd method. Checks for kerberos # libraries. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_KRB4], [ # # Check for kerberos libraries, if they exist, automatically build # kerberos module # AC_CHECK_LIB([krb], [krb_sendauth], [ac_have_krb4=yes], [ac_have_krb4=no], [-lkrb -ldes]) if test "$ac_have_krb4" = "yes" ; then AC_ADD_STATIC_MODULE("k4cmd") KRB_LIBS="-lkrb -ldes" AC_DEFINE([HAVE_KRB4], [1], [Define if you have Kerberos]) fi AM_CONDITIONAL(WITH_KRB4, test "$ac_have_krb4" = "yes") AC_SUBST(KRB_LIBS) ]) pdsh-2.36/config/ac_debug.m40000664€^–Á €^–Á 0000000315515131211226023752 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** # AUTHOR: # Chris Dunlap # # SYNOPSIS: # AC_DEBUG # # DESCRIPTION: # Adds support for the "--enable-debug" configure script option. # If CFLAGS are not passed to configure, they will be set based # on whether debugging has been enabled. Also, the NDEBUG macro # (used by assert) will be set accordingly. # # WARNINGS: # This macro must be placed after AC_PROG_CC or equivalent. ##***************************************************************************** AC_DEFUN([AC_DEBUG], [ AC_MSG_CHECKING([whether debugging is enabled]) AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug],[enable debugging code for development]), [ case "$enableval" in yes) ac_debug=yes ;; no) ac_debug=no ;; *) AC_MSG_RESULT([doh!]) AC_MSG_ERROR([bad value "$enableval" for --enable-debug]) ;; esac ] ) if test "$ac_debug" = yes; then if test -z "$ac_save_CFLAGS"; then test "$ac_cv_prog_cc_g" = yes && CFLAGS="-g" test "$GCC" = yes && CFLAGS="$CFLAGS -Wall" fi else if test -z "$ac_save_CFLAGS"; then test "$GCC" = yes && CFLAGS="-O3 -Wall -fno-strict-aliasing" || CFLAGS="-O3" # Do not strip binaries on Mac OS X. # if echo "$host" | grep -v darwin; then LDFLAGS="${LDFLAGS--s}" fi fi AC_DEFINE([NDEBUG], [1], [Define to 1 if you are building a production release.]) fi AC_MSG_RESULT([${ac_debug=no}]) ]) pdsh-2.36/DISCLAIMER.UC0000664€^–Á €^–Á 0000000421215131211226022352 0ustar arif.ali@canonical.comarif.ali@canonical.comCopyright (C) 2002-2006 The Regents of the University of California. Produced at Lawrence Livermore National Laboratory. Written by Jim Garlick . UCRL-CODE-2003-007. This file is part of Pdsh, an paralel remote shell program. For details, see . Pdsh is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Pdsh; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA This notice is required to be provided under our contract with the U.S. Department of Energy (DOE). This work was produced at the University of California, Lawrence Livermore National Laboratory under Contract No. W-7405-ENG-48 with the DOE. Neither the United States Government nor the University of California nor any of their employees, makes any warranty, express or implied, or assumes any liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately-owned rights. Also, reference herein to any specific commercial products, process, or services by trade name, trademark, manufacturer or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or the University of California. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or the University of California, and shall not be used for advertising or product endorsement purposes. The precise terms and conditions for copying, distribution and modification are specified in the file "COPYING". pdsh-2.36/src/0000775€^–Á €^–Á 0000000000015131211226021275 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/src/Makefile.am0000664€^–Á €^–Á 0000000061615131211226023334 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** ## Process this file with automake to produce Makefile.in. ##***************************************************************************** include $(top_srcdir)/config/Make-inc.mk SUBDIRS = \ common \ modules \ pdsh pdsh-2.36/src/modules/0000775€^–Á €^–Á 0000000000015131211226022745 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/src/modules/Makefile.am0000664€^–Á €^–Á 0000000660415131211226025007 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** ## Process this file with automake to produce Makefile.in. ##***************************************************************************** include $(top_srcdir)/config/Make-inc.mk AUTOMAKE_OPTIONS = foreign AM_CPPFLAGS = -I$(top_srcdir) if WITH_SSH SSH_MODULE = sshcmd.la endif if WITH_LIBGENDERS GENDERS_MODULE = genders.la endif if WITH_NODEUPDOWN NODEUPDOWN_MODULE = nodeupdown.la endif if WITH_MACHINES MACHINES_MODULE = machines.la endif if WITH_MRSH MRSH_MODULE = mcmd.la endif if WITH_KRB4 KRB4_MODULE = k4cmd.la endif if WITH_RSH RSH_MODULE = xrcmd.la endif if WITH_XCPU XCPU_MODULE = xcpucmd.la endif if WITH_SLURM SLURM_MODULE = slurm.la endif if WITH_TORQUE TORQUE_MODULE = torque.la endif if WITH_DSHGROUP DSHGROUP_MODULE = dshgroup.la endif if WITH_NETGROUP NETGROUP_MODULE = netgroup.la endif if WITH_EXEC EXEC_MODULE = execcmd.la endif if WITH_GNU_LD VERSION_SCRIPT = \ version.map OTHER_FLAGS = \ -Wl,--version-script=$(VERSION_SCRIPT) endif if WITH_STATIC_MODULES noinst_LTLIBRARIES = \ libmods.la libmods_la_LIBADD = \ $(LIBMODS_OBJS) libmods_la_DEPENDENCIES = \ $(LIBMODS_OBJS) else pkglib_LTLIBRARIES = \ $(RSH_MODULE) \ $(XCPU_MODULE) \ $(SSH_MODULE) \ $(MRSH_MODULE) \ $(GENDERS_MODULE) \ $(NODEUPDOWN_MODULE) \ $(MACHINES_MODULE) \ $(KRB4_MODULE) \ $(SLURM_MODULE) \ $(TORQUE_MODULE) \ $(DSHGROUP_MODULE) \ $(NETGROUP_MODULE) \ $(EXEC_MODULE) BUILT_SOURCES = \ $(VERSION_SCRIPT) MODULE_FLAGS = \ -module -avoid-version $(OTHER_FLAGS) endif nodist_libmods_la_SOURCES = \ $(top_builddir)/static_modules.h EXTRA_libmods_la_SOURCES = \ xrcmd.c \ sshcmd.c \ mcmd.c \ genders.c \ nodeupdown.c \ machines.c \ k4cmd.c \ slurm.c \ dshgroup.c \ netgroup.c \ xcpucmd.c \ execcmd.c libmods_la_LDFLAGS = \ $(MRSH_LIBS) \ $(NODEUPDOWN_LIBS) \ $(KRB_LIBS) \ $(GENDERS_LIBS) \ $(SLURM_LIBS) \ $(TORQUE_LIBS) xrcmd_la_SOURCES = xrcmd.c xrcmd_la_LDFLAGS = $(MODULE_FLAGS) xcpucmd_la_SOURCES = xcpucmd.c xcpucmd_la_LDFLAGS = $(MODULE_FLAGS) sshcmd_la_SOURCES = sshcmd.c sshcmd_la_LDFLAGS = $(MODULE_FLAGS) mcmd_la_SOURCES = mcmd.c mcmd_la_LDFLAGS = $(MODULE_FLAGS) $(MRSH_LIBS) k4cmd_la_SOURCES = k4cmd.c k4cmd_la_LDFLAGS = $(MODULE_FLAGS) $(KRB_LIBS) execcmd_la_SOURCES = execcmd.c execcmd_la_LDFLAGS = $(MODULE_FLAGS) machines_la_SOURCES = machines.c machines_la_LDFLAGS = $(MODULE_FLAGS) genders_la_SOURCES = genders.c genders_la_LDFLAGS = $(MODULE_FLAGS) $(GENDERS_LIBS) nodeupdown_la_SOURCES = nodeupdown.c nodeupdown_la_LDFLAGS = $(MODULE_FLAGS) $(NODEUPDOWN_LIBS) slurm_la_SOURCES = slurm.c slurm_la_LDFLAGS = $(MODULE_FLAGS) $(SLURM_LIBS) torque_la_SOURCES = torque.c torque_la_LDFLAGS = $(MODULE_FLAGS) $(TORQUE_LIBS) torque_la_CPPFLAGS = $(TORQUE_CPPFLAGS) dshgroup_la_SOURCES = dshgroup.c dshgroup_la_LDFLAGS = $(MODULE_FLAGS) netgroup_la_SOURCES = netgroup.c netgroup_la_LDFLAGS = $(MODULE_FLAGS) $(VERSION_SCRIPT) : (echo "{ global:"; \ echo " pdsh_module_info;"; \ echo " pdsh_module_priority;"; \ echo " local: *;"; \ echo "};") > $(VERSION_SCRIPT) DISTCLEANFILES = \ $(VERSION_SCRIPT) pdsh-2.36/src/modules/xcpucmd.c0000664€^–Á €^–Á 0000001614315131211226024561 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id: $ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #if HAVE_PTHREAD_H #include #endif #include #include #include #if HAVE_FCNTL_H #include #endif #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #include "src/common/err.h" #include "src/common/list.h" #include "src/common/xpoll.h" #include "src/common/xmalloc.h" #include "src/pdsh/dsh.h" #include "src/pdsh/mod.h" #include "src/pdsh/privsep.h" struct xcpu_info_struct { char *hostname; int sid; }; #if STATIC_MODULES # define pdsh_module_info xcpucmd_module_info # define pdsh_module_priority xcpucmd_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int xcpucmd_init(opt_t *); static int xcpucmd_signal(int, void *, int); static int xcpucmd_destroy(struct xcpu_info_struct *); static int xcpucmd(char *, char *, char *, char *, char *, int, int *, void **); /* * Export pdsh module operations structure */ struct pdsh_module_operations xcpucmd_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations xcpucmd_xcpucmd_ops = { (RcmdInitF) xcpucmd_init, (RcmdSigF) xcpucmd_signal, (RcmdF) xcpucmd, (RcmdDestroyF) xcpucmd_destroy, }; /* * Export module options */ struct pdsh_module_option xcpucmd_module_options[] = { PDSH_OPT_TABLE_END }; /* * Xxcpucmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "xcpu", "Jim Garlick ", "XCPU connect method", DSH | PCP, &xcpucmd_module_ops, &xcpucmd_xcpucmd_ops, &xcpucmd_module_options[0], }; static int xcpucmd_init(opt_t * opt) { /* not implemented */ return 0; } #define CLONE_TMPL "/mnt/xcpu/%s/xcpu/clone" #define SESSFILE_TMPL "/mnt/xcpu/%s/xcpu/%x/%s" static FILE * _openclone(char *hostname, int *sidp) { char path[MAXPATHLEN]; FILE *f; sprintf(path, CLONE_TMPL, hostname); f = fopen(path, "r"); if (f == NULL) err("%s: %m\n", path); else if (fscanf(f, "%x", sidp) != 1) { err("error reading %s\n", path); fclose(f); f = NULL; } return f; } static int _openfilefd(char *hostname, int sid, mode_t mode, char *name) { char path[MAXPATHLEN]; int fd; sprintf(path, SESSFILE_TMPL, hostname, sid, name); fd = open(path, mode); if (fd < 0) err("%s: %m\n", path); return fd; } static int _writefile(char *hostname, int sid, char *name, char *data) { char path[MAXPATHLEN]; FILE *f; int res = 0; sprintf(path, SESSFILE_TMPL, hostname, sid, name); f = fopen(path, "w"); if (f == NULL) { err("%s: %m\n", path); goto done; } if (fprintf(f, "%s", data) != strlen(data)) { err("error writing to %s\n", path); goto done; } res = 1; done: if (f) { if (fclose(f) != 0) res = 0; } return res; } #define QUOTE '\'' #define SHCMD_TMPL "/bin/sh -c '%s'" static char * _mkshcmd(char *cmd) { char *qcmd = Malloc(2*strlen(cmd) + 1); char *p, *q, *shcmd; /* Quote cmd string Plan 9 style: ' becomes ''. */ for (p = cmd, q = qcmd; *p != '\0'; p++) { switch (*p) { case QUOTE: *q++ = QUOTE; *q++ = QUOTE; break; default: *q++ = *p; break; } } *q = '\0'; /* Now embed the quoted command/script in a shell command line. */ shcmd = Malloc(strlen(SHCMD_TMPL) + strlen(qcmd) + 1); sprintf(shcmd, SHCMD_TMPL, qcmd); Free((void **)&qcmd); return shcmd; } static int _xcpucmd(char *hostname, char *cmd, int *fd2p, int *sidp) { int sid; FILE *fclone = NULL; int fd = -1; char *argstr = _mkshcmd(cmd); /* Establish a session by reading its number (sid) from the clone file. */ fclone = _openclone(hostname, &sid); if (fclone == NULL) goto done; /* don't close it yet - this preserves session */ if (_writefile(hostname, sid, "argv", argstr) == 0) goto done; if (_writefile(hostname, sid, "ctl", "lexec") == 0) goto done; fd = _openfilefd(hostname, sid, O_RDWR, "io"); if (fd >= 0) { if (fd2p) *fd2p = _openfilefd(hostname, sid, O_RDONLY, "stderr"); } done: if (argstr) Free((void **)&argstr); if (fclone) fclose(fclone); if (fd >= 0) *sidp = sid; return fd; /* session goes away when fd is closed */ } static int xcpucmd_signal(int efd, void *arg, int signum) { char cmd[256]; struct xcpu_info_struct *x = (struct xcpu_info_struct *)arg; sprintf(cmd, "signal %d", signum); (void) _writefile(x->hostname, x->sid, "ctl", cmd); return 0; } static int xcpucmd_destroy(struct xcpu_info_struct *x) { if (x) { if (x->hostname) Free((void **)&x->hostname); Free((void **)&x); } /* XXX Insert retreival of exit status here when we have a wait file. */ return 0; } static int xcpucmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **arg) { int sid, fd; struct xcpu_info_struct *x; if (strcmp(locuser, remuser) != 0) { err("remote user must match local user for xcpu rcmd method\n"); return -1; } fd = _xcpucmd(ahost, cmd, fd2p, &sid); if (fd >= 0) { x = Malloc(sizeof(struct xcpu_info_struct)); x->hostname = Strdup(ahost); x->sid = sid; *arg = x; } return fd; } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/machines.c0000664€^–Á €^–Á 0000000563315131211226024707 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "src/pdsh/wcoll.h" #include "src/pdsh/mod.h" #include "src/common/hostlist.h" #include "src/common/err.h" #if STATIC_MODULES # define pdsh_module_info machines_module_info # define pdsh_module_priority machines_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static hostlist_t read_machines(opt_t *opt); static int machines_opt_a(opt_t *, int, char *); static bool allnodes = false; /* * Export pdsh module operations structure */ struct pdsh_module_operations machines_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) read_machines, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations machines_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option machines_module_options[] = { { 'a', NULL, "target all nodes", DSH | PCP, (optFunc) machines_opt_a }, PDSH_OPT_TABLE_END }; /* * Machines module info */ struct pdsh_module pdsh_module_info = { "misc", "machines", "Jim Garlick ", "Read list of all nodes from a machines file", DSH | PCP, &machines_module_ops, &machines_rcmd_ops, &machines_module_options[0], }; static int machines_opt_a(opt_t *pdsh_opt, int opt, char *arg) { allnodes = true; return 0; } static hostlist_t read_machines(opt_t *opt) { if (!allnodes) return NULL; if (opt->wcoll) errx("Do not specify both -w and -a"); return read_wcoll(_PATH_MACHINES, NULL); } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/execcmd.c0000664€^–Á €^–Á 0000001166415131211226024531 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * This module uses the "pipecmd" interface to execute * arbitrary processes. First of the "remote" args is the * command to execute. Some simple parameters are substituted * on the command line: * * %h : Target "hostname" * %u : Remote username * %n : Remote "rank" (0-n) * * E.g.: * * pdsh -Rexec -w foo[0-1] ssh -l %u -x %h hostname * * would somewhat mimic the existing ssh module. * */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "src/pdsh/opt.h" #include "src/pdsh/mod.h" #include "src/pdsh/rcmd.h" #include "src/common/pipecmd.h" #include "src/common/err.h" #if STATIC_MODULES # define pdsh_module_info execcmd_module_info # define pdsh_module_priority execcmd_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int mod_exec_postop(opt_t *opt); static int mod_exec_exit (void); static int exec_init(opt_t *); static int exec_signal(int, void *arg, int); static int execcmd(char *, char *, char *, char *, char *, int, int *, void **); static int exec_destroy (pipecmd_t p); /* * Export generic pdsh module operations: */ struct pdsh_module_operations execcmd_module_ops = { (ModInitF) NULL, (ModExitF) mod_exec_exit, (ModReadWcollF) NULL, (ModPostOpF) mod_exec_postop }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations execcmd_rcmd_ops = { (RcmdInitF) exec_init, (RcmdSigF) exec_signal, (RcmdF) execcmd, (RcmdDestroyF) exec_destroy }; /* * Export module options */ struct pdsh_module_option execcmd_module_options[] = { PDSH_OPT_TABLE_END }; /* * Sshcmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "exec", "Mark Grondona ", "arbitrary command rcmd connect method", DSH, &execcmd_module_ops, &execcmd_rcmd_ops, &execcmd_module_options[0], }; static int mod_exec_postop(opt_t *opt) { if (strcmp(opt->rcmd_name, "exec") == 0) { if (opt->connect_timeout != CONNECT_TIMEOUT) { err("%p: Cannot specify -t with \"-R exec\"\n"); return 1; } } return 0; } static int exec_init(opt_t * opt) { /* * Drop privileges if running setuid root */ if ((geteuid() == 0) && (getuid() != 0)) { if (setuid (getuid ()) < 0) errx ("%p: setuid: %m\n"); } /* * Do not resolve hostnames in pdsh when using exec */ if (rcmd_opt_set (RCMD_OPT_RESOLVE_HOSTS, 0) < 0) errx ("%p: execcmd_init: rcmd_opt_set: %m\n"); return 0; } static int mod_exec_exit (void) { return 0; } static int exec_signal(int fd, void *arg, int signum) { return (pipecmd_signal ((pipecmd_t) arg, signum)); } static int execcmd(char *ahost, char *addr, char *luser, char *ruser, char *cmd, int rank, int *fd2p, void **arg) { pipecmd_t p; const char **argv = pdsh_remote_argv (); /* * If pdsh_remote_argv is empty or NULL we may be running * in interactive dsh mode. Don't try to split the cmd * into args ourselves in this case, instead just pass * to a shell: */ const char *alt_argv[] = { "sh", "-c", cmd, NULL }; if (!argv || *argv == NULL) argv = alt_argv; if (!(p = pipecmd (argv[0], argv + 1, ahost, ruser, rank))) return (-1); if (fd2p) *fd2p = pipecmd_stderrfd (p); *arg = p; return (pipecmd_stdoutfd (p)); } static int exec_destroy (pipecmd_t p) { int status; if (pipecmd_wait (p, &status) < 0) return (1); pipecmd_destroy (p); return (WEXITSTATUS (status)); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/nodeupdown.c0000664€^–Á €^–Á 0000001015515131211226025275 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "src/common/hostlist.h" #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/pdsh/mod.h" #ifndef GENDERS_ALTNAME_ATTRIBUTE # define GENDERS_ALTNAME_ATTRIBUTE "altname" #endif #if STATIC_MODULES # define pdsh_module_info nodeupdown_module_info # define pdsh_module_priority nodeupdown_module_priority #endif int pdsh_module_priority = 120; static int mod_nodeupdown_postop(opt_t *opt); static int nodeupdown_opt_v(opt_t *, int, char *); static void remove_all_down_nodes(hostlist_t); static bool verify = false; /* * Export pdsh module operations structure */ struct pdsh_module_operations nodeupdown_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) mod_nodeupdown_postop }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations nodeupdown_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option nodeupdown_module_options[] = { { 'v', NULL, "exclude targets if they are down", DSH | PCP, (optFunc) nodeupdown_opt_v }, PDSH_OPT_TABLE_END }; /* * Nodeupdown module info */ struct pdsh_module pdsh_module_info = { "misc", "nodeupdown", "Al Chu ", "remove targets if down according to libnodeupdown", DSH | PCP, &nodeupdown_module_ops, &nodeupdown_rcmd_ops, &nodeupdown_module_options[0], }; static int nodeupdown_opt_v(opt_t *pdsh_opt, int opt, char *arg) { verify = true; return 0; } static int mod_nodeupdown_postop(opt_t *opt) { if (!verify || !opt->wcoll) return 0; remove_all_down_nodes(opt->wcoll); return 0; } /* * Remove down nodes from hostlist wcoll using "nodeupdown_is_down_node" * on each member of wcoll. Supposedly, it doesn't matter whether you * pass in the canonical or altname. */ static void remove_all_down_nodes(hostlist_t wcoll) { nodeupdown_t nh = NULL; char * host = NULL; hostlist_iterator_t i = NULL; if ((nh = nodeupdown_handle_create()) == NULL) errx("%p: Unable to create nodeupdown handle.\n"); #if HAVE_NODEUPDOWN_LOAD_DATA_6 if (nodeupdown_load_data(nh, NULL, NULL, NULL, 0, 0) < 0) #else if (nodeupdown_load_data(nh, NULL, 0, 0, NULL) < 0) #endif errx("%p: nodeupdown: %s\n", nodeupdown_errormsg(nh)); i = hostlist_iterator_create(wcoll); while ((host = hostlist_next(i))) { if (nodeupdown_is_node_down(nh, host) > 0) hostlist_remove(i); free(host); } hostlist_iterator_destroy(i); if (nodeupdown_handle_destroy(nh) < 0) err("%p: nodeupdown_handle_destroy: %s\n", nodeupdown_errormsg(nh)); return; } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/netgroup.c0000664€^–Á €^–Á 0000001201715131211226024755 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2005-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include /* getenv */ #include #include #include "src/pdsh/wcoll.h" #include "src/pdsh/mod.h" #include "src/common/hostlist.h" #include "src/common/xmalloc.h" #include "src/common/err.h" #include "src/common/list.h" #include "src/common/split.h" #if STATIC_MODULES # define pdsh_module_info netgroup_module_info # define pdsh_module_priority netgroup_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static hostlist_t read_netgroup(opt_t *opt); static int netgroup_postop (opt_t *); static int netgroup_process_opt(opt_t *, int, char *); static List groups = NULL; static List exgroups = NULL; /* * Export pdsh module operations structure */ struct pdsh_module_operations netgroup_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) read_netgroup, (ModPostOpF) netgroup_postop, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations netgroup_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option netgroup_module_options[] = { { 'g', "groupname", "target hosts in netgroup \"groupname\"", DSH | PCP, (optFunc) netgroup_process_opt }, { 'X', "groupname", "exclude hosts in netgroup \"groupname\"", DSH | PCP, (optFunc) netgroup_process_opt }, PDSH_OPT_TABLE_END }; /* * Machines module info */ struct pdsh_module pdsh_module_info = { "misc", "netgroup", "Mark Grondona ", "Target netgroups from pdsh", DSH | PCP, &netgroup_module_ops, &netgroup_rcmd_ops, &netgroup_module_options[0], }; static int netgroup_process_opt(opt_t *pdsh_opt, int opt, char *arg) { switch (opt) { case 'g': groups = list_split_append (groups, ",", arg); break; case 'X': exgroups = list_split_append (exgroups, ",", arg); break; default: err ("%p: netgroup_process_opt: invalid option `%c'\n", opt); return -1; break; } return 0; } static hostlist_t _read_netgroup (const char *group) { hostlist_t hl = NULL; char *host, *user, *domain; int rc; setnetgrent (group); while ((rc = getnetgrent (&host, &user, &domain))) { if (hl == NULL) hl = hostlist_create (host); else hostlist_push (hl, host); } endnetgrent (); return (hl); } static hostlist_t _read_groups (List grouplist) { ListIterator i = NULL; hostlist_t hl = NULL; char *group; i = list_iterator_create (grouplist); while ((group = list_next (i))) { hostlist_t l = _read_netgroup (group); if (l == NULL) continue; if (hl == NULL) { hl = l; } else { hostlist_push_list (hl, l); hostlist_destroy (l); } } list_iterator_destroy (i); if (hl != NULL) hostlist_uniq (hl); return (hl); } static hostlist_t read_netgroup (opt_t *opt) { if (!groups) return NULL; if (opt->wcoll && groups) errx("Do not specify both -w and -g"); return _read_groups (groups); } static int _delete_all (hostlist_t hl, hostlist_t dl) { int rc = 0; char * host = NULL; hostlist_iterator_t i = hostlist_iterator_create (dl); while ((host = hostlist_next (i))) { rc += hostlist_delete_host (hl, host); free (host); } hostlist_iterator_destroy (i); return (rc); } static int netgroup_postop (opt_t *opt) { hostlist_t hl = NULL; if (!opt->wcoll || !exgroups) return (0); if ((hl = _read_groups (exgroups)) == NULL) return (0); _delete_all (opt->wcoll, hl); return 0; } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/torque.c0000664€^–Á €^–Á 0000002312215131211226024430 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id: torque.c $ *****************************************************************************/ /* * torque.c created using slurm.c as template. * Mattias Slabanja */ /***************************************************************************** * Copyright (C) 2001-2007 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "src/common/hostlist.h" #include "src/common/split.h" #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/pdsh/mod.h" #include "src/pdsh/opt.h" #include #include #if STATIC_MODULES # define pdsh_module_info torque_module_info # define pdsh_module_priority torque_module_priority #endif /* * Give this module low priority */ int pdsh_module_priority = 10; /* * Call this module after all option processing. The module will only * try to read the PBS_JOBID if opt->wcoll is not already set. * Calling the module in postop allows us to be sure that all other * modules had a chance to update the wcoll. */ static int mod_torque_init(void); static int mod_torque_wcoll(opt_t *opt); static int mod_torque_exit(void); static hostlist_t _torque_wcoll(List jobids); static int torque_process_opt(opt_t *, int opt, char *arg); static List job_list = NULL; /* * Export generic pdsh module options */ struct pdsh_module_operations torque_module_ops = { (ModInitF) mod_torque_init, (ModExitF) mod_torque_exit, (ModReadWcollF) mod_torque_wcoll, (ModPostOpF) NULL }; /* * Export module options */ struct pdsh_module_option torque_module_options[] = { { 'j', "jobid,...", "Run on nodes allocated to TORQUE job(s)", DSH | PCP, (optFunc) torque_process_opt }, PDSH_OPT_TABLE_END }; /* * TORQUE module info */ struct pdsh_module pdsh_module_info = { "misc", "torque", "Mattias Slabanja ", "Attempt to create wcoll from PBS_JOBID env var", DSH | PCP, &torque_module_ops, NULL, &torque_module_options[0], }; static int mod_torque_init (void) { return (0); } static int32_t str2jobid (const char *str) { char *p = NULL; long int jid; if (str == NULL) return (-1); jid = strtoul (str, &p, 10); if( *p != '\0' ) errx ("%p: invalid jobid format \"%s\" for -j\n", str); return ((int32_t) jid); } static int torque_process_opt(opt_t *pdsh_opts, int opt, char *arg) { switch (opt) { case 'j': job_list = list_split_append (job_list, ",", arg); break; default: break; } return (0); } static int mod_torque_exit(void) { if (job_list) list_destroy (job_list); return (0); } /* * If no wcoll has been established by this time, look for the * PBS_JOBID env var, and set wcoll to the list of nodes allocated * to that job. */ static int mod_torque_wcoll(opt_t *opt) { if (job_list && opt->wcoll) errx("%p: do not specify -j with any other node selection option.\n"); if (!opt->wcoll) opt->wcoll = _torque_wcoll (job_list); return 0; } static void _create_fq_jobid(char *dst, const char *jobid, const char *servername){ /* * Create fully qualified jobid, "." */ if ( str2jobid(jobid) < 0 ){ *dst = '\0'; return; } strncpy(dst, jobid, PBS_MAXSEQNUM); strncat(dst, ".", 1); strncat(dst, servername, PBS_MAXSERVERNAME); } static char *_next_hname(char *p){ /* * Find next hostname in a "exechost list". * Find next '/'-character, then find next '+'-character, */ if( p == NULL ) return NULL; while( *p != '/' ) if( *p++ == '\0' ) return NULL; while( *p != '+' ) if( *p++ == '\0' ) return NULL; /* And finally step past the '+'-character */ if( *p++ == '\0' ) return NULL; return p; } static void _copy_hname(char *dst, const char *src){ /* Copy at most PBS_MAXHOSTNAME-1 characters. Hostname in src ends before a '/'-character. */ const char *q = src; while( (*src != '\0') && \ (*src != '/') && \ (src - q) < (PBS_MAXHOSTNAME-2) ){ *dst++ = *src++; } *dst = '\0'; } static hostlist_t _hl_append_host (hostlist_t hl, char *node) { if (hl == NULL) return (hostlist_create (node)); else hostlist_push_host (hl, node); return (hl); } static hostlist_t _add_jobnodes(hostlist_t hl, int connect, char *jobdesc){ /* * Add the nodes allocated by job jobdesc to hostlist hl. */ struct batch_status *status; struct attrl a_exechost = {NULL, ATTR_exechost, NULL, NULL}; char *p, hname[PBS_MAXHOSTNAME]; if( (status = pbs_statjob(connect, jobdesc, &a_exechost, EXECQUEONLY)) == NULL ){ err ("%p: Failed to retrieve information for job %s: (%d) %s\n", jobdesc, pbs_errno, pbs_strerror(pbs_errno)); } else if ( status->attribs == NULL ){ /* We end up here e.g. if the job is not started. */ //errx("Job %s has no allocated nodes.\n", jobdesc); ; } else { /* An "exechost"-list is available for the job. * exechosts are on the format * host/slot+host/slot+host/slot+..., i.e. hostname slash integer plus ... * Just ignore the slash-integer parts, split at the '+'-signs, * and let hostlist_uniq deal with duplicates later on. */ for(p = status->attribs->value; p != NULL; p = _next_hname(p)){ _copy_hname(hname, p); hl = _hl_append_host(hl, hname); } pbs_statfree(status); } return(hl); } static hostlist_t _torque_wcoll (List joblist) { hostlist_t hl = NULL; ListIterator li = NULL; char jobid[PBS_MAXCLTJOBID]; char *envjobid = NULL; char *p; struct batch_status *status; struct attrl a_servername = {NULL, ATTR_servername, NULL, NULL}; char servername[PBS_MAXSERVERNAME]; int connect; /* * A joblist provided via the command line will take presence over PBS_JOBID. * If there is a joblist, don't bother checking PBS_JOBID. */ if ((joblist == NULL) && ((envjobid = getenv("PBS_JOBID")) == NULL)) return (NULL); /* Connect to "default_server". */ if( (connect = pbs_connect(NULL)) < 0 ){ const char msg[] = "Failed to connect to torque server"; /* * If pbs_server is not set after this call, we presume that * PBS_DEFAULT isn't set or a default server is not available. * Generate an alternate error message accordingly: */ if (pbs_server == NULL) errx ("%p: %s: PBS_DEFAULT not set or no default server\n", msg); errx ("%p: %s %s: (%d) %s\n", msg, pbs_server, pbs_errno, pbs_strerror(pbs_errno)); } /* However, fully qualified JobIDs need the fully qualified server name. * We can get it by calling pbs_statserver. */ if( (status = pbs_statserver(connect, &a_servername, NULL)) == NULL ){ errx("%p: Failed to retrieve fully qualified servername for torque server.\n"); } else { strncpy(servername, status->name, PBS_MAXSERVERNAME); /* Some versions of torque will return server name as FQDN:PORT * which ends up being in the JobID sent back to the pbs_server. */ strtok(servername, ":"); pbs_statfree(status); } if( joblist != NULL ){ /* Add the nodes for the jobs provided with the -j flag. */ li = list_iterator_create(joblist); while( (p = list_next(li)) ){ /* The provided jobids are expected to be "integer only" jobids * so the fully qualified server name must be appended */ _create_fq_jobid(jobid, p, servername); hl = _add_jobnodes(hl, connect, jobid); } list_iterator_destroy(li); } else if( envjobid != NULL ) { /* The env variable PBS_JOBID is expected to be a fully qualified jobid, * hence _create_fq_jobid is not called. */ hl = _add_jobnodes(hl, connect, envjobid); } if( pbs_disconnect(connect) ){ err("%p: Failed to disconnect from torque server %s: (%d) %s\n", pbs_server, pbs_errno, pbs_strerror(pbs_errno)); } if (hl) hostlist_uniq (hl); return (hl); } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/sshcmd.c0000664€^–Á €^–Á 0000002412515131211226024376 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * This is an rcmd() replacement originally by Chris Siebenmann * . There was no copyright information on the original. * If this finds its way back to the original author please let me know if * you would like this header block changed... * * Brought in to pdsh from USC rdist -jg * Changes: * - added fd2p arg handling * - changed name, func prototype, and added sshcmd() and rshcmd() wrappers * - use 'err' for output * - unset DISPLAY and call setsid() so ssh won't hang prompting for passphrase */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include /* putenv */ #if HAVE_UNISTD_H #include #endif #if HAVE_PTHREAD_H #include #endif #include /* memset */ #include #include #include #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/err.h" #include "src/common/list.h" #include "src/common/split.h" #include "src/common/pipecmd.h" #include "src/pdsh/dsh.h" #include "src/pdsh/mod.h" #if STATIC_MODULES # define pdsh_module_info sshcmd_module_info # define pdsh_module_priority sshcmd_module_priority #endif #define DEFAULT_SSH_ARGS "-2 -a -x %h" int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int mod_ssh_postop(opt_t *opt); static int mod_ssh_exit (void); static int sshcmd_init(opt_t *); static int sshcmd_signal(int, void *arg, int); static int sshcmd(char *, char *, char *, char *, char *, int, int *, void **); static int sshcmd_destroy (pipecmd_t p); static int sshcmd_args_init (void); static int fixup_ssh_args (List ssh_args_list, int need_user); List ssh_args_list = NULL; /* * Export generic pdsh module operations: */ struct pdsh_module_operations sshcmd_module_ops = { (ModInitF) NULL, (ModExitF) mod_ssh_exit, (ModReadWcollF) NULL, (ModPostOpF) mod_ssh_postop }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations sshcmd_rcmd_ops = { (RcmdInitF) sshcmd_init, (RcmdSigF) sshcmd_signal, (RcmdF) sshcmd, (RcmdDestroyF) sshcmd_destroy }; /* * Export module options */ struct pdsh_module_option sshcmd_module_options[] = { PDSH_OPT_TABLE_END }; /* * Sshcmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "ssh", "Jim Garlick ", "ssh based rcmd connect method", DSH | PCP, &sshcmd_module_ops, &sshcmd_rcmd_ops, &sshcmd_module_options[0], }; static char **ssh_argv_create (List arg_list, const char **remote_argv) { int n; char *arg; char **argv; const char **p; ListIterator i; n = 0; for (p = remote_argv; *p; p++) n++; n += list_count (arg_list) + 2; argv = (char **) Malloc (n * sizeof (char *)); memset (argv, 0, n); n = 0; i = list_iterator_create (arg_list); while ((arg = list_next (i))) argv[n++] = Strdup (arg); list_iterator_destroy (i); /* Append remote_argv to standard list of args */ for (p = remote_argv; *p; p++) argv[n++] = Strdup (*p); return (argv); } static void ssh_argv_destroy (char **args) { int i = 0; while (args[i]) Free ((void **) &args[i++]); Free ((void **) &args); } static int ssh_args_prepend_timeout (int timeout) { #if SSH_HAS_CONNECT_TIMEOUT char buf[64]; if (timeout <= 0) return (0); snprintf (buf, 64, SSH_CONNECT_TIMEOUT_OPTION, timeout); list_prepend (ssh_args_list, Strdup (buf)); #endif return (0); } static int mod_ssh_postop(opt_t *opt) { sshcmd_args_init (); ssh_args_prepend_timeout (opt->connect_timeout); /* * Append PATH=...; to ssh args if DSHPATH was set */ if (opt->dshpath) list_append (ssh_args_list, Strdup (opt->dshpath)); return 0; } static int sshcmd_init(opt_t * opt) { /* * Drop privileges if running setuid root */ if ((geteuid() == 0) && (getuid() != 0)) { if (setuid (getuid ()) < 0) errx ("%p: setuid: %m\n"); } /* * Do not resolve hostnames in pdsh when using ssh */ if (rcmd_opt_set (RCMD_OPT_RESOLVE_HOSTS, 0) < 0) errx ("%p: sshcmd_init: rcmd_opt_set: %m\n"); return 0; } static void free_f (void *x) { Free ((void **) &x); } static List ssh_args_list_copy (List args) { List copy; ListIterator i = list_iterator_create (args); const char *arg; copy = list_create ((ListDelF) free_f); while ((arg = list_next (i))) list_append (copy, Strdup (arg)); list_iterator_destroy (i); return (copy); } static int mod_ssh_exit (void) { if (ssh_args_list) list_destroy (ssh_args_list); return 0; } /* * SSH doesn't support signal forwarding, at least the way pdsh uses it * at this time. Instead we always send SIGTERM which seems to have the * desired effect of killing off ssh most of the time. */ static int sshcmd_signal(int fd, void *arg, int signum) { /* * Always send SIGTERM. SIGINT doesn't seem to get forwarded by ssh, and * really termination of the connection is probably the desired result. */ return (pipecmd_signal ((pipecmd_t) arg, SIGTERM)); } static int sshcmd(char *ahost, char *addr, char *luser, char *ruser, char *cmd, int rank, int *fd2p, void **arg) { pipecmd_t p = NULL; const char **remote_argv = pdsh_remote_argv (); const char *alt_argv[] = { cmd, NULL }; char **ssh_args; List args_list; /* * If running as pdcp/rpdcp, then the dsh code has rewritten * the cmd to invoke pdcp server on remote nodes. Therefore * avoid using pdsh_remote_argv() directly, instead use cmd: */ if (pdsh_personality() == PCP) remote_argv = alt_argv; /* * In interactive dsh mode, pdsh_remote_argv() will be empty * so we can't use it. */ if (!remote_argv || !*remote_argv) remote_argv = alt_argv; /* * Create a copy of ssh_args_list to customize for this target */ args_list = ssh_args_list_copy (ssh_args_list); if (strcmp (luser, ruser) != 0) fixup_ssh_args (args_list, 1); else fixup_ssh_args (args_list, 0); ssh_args = ssh_argv_create (args_list, remote_argv); list_destroy (args_list); if (!(p = pipecmd ("ssh", (const char **) ssh_args, ahost, ruser, rank))) goto out; if (fd2p) *fd2p = pipecmd_stderrfd (p); *arg = (void *) p; out: ssh_argv_destroy (ssh_args); return (p ? pipecmd_stdoutfd (p) : -1); } static int sshcmd_destroy (pipecmd_t p) { int status = 0; if (pipecmd_wait (p, &status) < 0) err ("%p: %S: wait on ssh cmd: %m\n", pipecmd_target (p)); pipecmd_destroy (p); return WEXITSTATUS (status); } /* * Check string argument [arg] for parameter substitution sequence [s]. * If [s] is found in [arg] then also check that [s] is not preceeded * by '%' which would have the effect of escaping the parameter * substitution. */ static int arg_has_parameter (const char *arg, const char *s) { char *p; if ((p = strstr (arg, s)) && ((p == arg) || (*(p-1) != '%'))) return 1; return 0; } /* * Scan the current ssh_args_list for presence of %u and %h. * If they are not present, assume we need to append them to the * ssh args. */ static int fixup_ssh_args (List ssh_args_list, int need_user) { ListIterator i = list_iterator_create (ssh_args_list); char *arg; int got_user = 0; int got_host = 0; while ((arg = list_next (i))) { if (need_user && arg_has_parameter (arg, "%u")) got_user = 1; if (arg_has_parameter (arg, "%h")) got_host = 1; } if (need_user && !got_user) { if (got_host) { /* * Ensure "%lu" is not inserted after "%h": */ list_iterator_reset (i); arg = list_find (i, (ListFindF) arg_has_parameter, "%h"); list_insert (i, Strdup ("-l%u")); } else list_append (ssh_args_list, Strdup ("-l%u")); } /* * Always append "%h" to end of args */ if (!got_host) list_append (ssh_args_list, Strdup ("%h")); list_iterator_destroy (i); return (0); } static int sshcmd_args_init (void) { char *val = NULL; char *str = NULL; /* * Place SSH_ARGS_APPEND at the beggining of the args string * because %h must always come last... */ if ((val = getenv ("PDSH_SSH_ARGS_APPEND"))) { str = Strdup (val); xstrcatchar (&str, ' '); } if (!(val = getenv ("PDSH_SSH_ARGS"))) val = DEFAULT_SSH_ARGS; xstrcat (&str, val); ssh_args_list = list_split (" ", str); Free ((void **) &str); return (0); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/xrcmd.c0000664€^–Á €^–Á 0000002512615131211226024234 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * This code is based on the BSD rcmd.c with MT safety added, and the * interface changed. Original UC regents header included below. */ /* * Copyright (c) 1983, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Changes: * - MT save * - changed functional interface. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; #endif /* LIBC_SCCS and not lint */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #if HAVE_PTHREAD_H #include #endif #include #include #include #if HAVE_FCNTL_H #include #endif #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_STRINGS_H #include /* AIX FD_SET calls bzero */ #endif #include "src/common/err.h" #include "src/common/list.h" #include "src/common/xpoll.h" #include "src/pdsh/dsh.h" #include "src/pdsh/mod.h" #include "src/pdsh/privsep.h" #define RSH_PORT 514 #if STATIC_MODULES # define pdsh_module_info xrcmd_module_info # define pdsh_module_priority xrcmd_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int xrcmd_init(opt_t *); static int xrcmd_signal(int, void *, int); static int xrcmd(char *, char *, char *, char *, char *, int, int *, void **); /* * Export pdsh module operations structure */ struct pdsh_module_operations xrcmd_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations xrcmd_rcmd_ops = { (RcmdInitF) xrcmd_init, (RcmdSigF) xrcmd_signal, (RcmdF) xrcmd, }; /* * Export module options */ struct pdsh_module_option xrcmd_module_options[] = { PDSH_OPT_TABLE_END }; /* * Xrcmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "rsh", "Jim Garlick ", "BSD rcmd connect method", DSH | PCP, &xrcmd_module_ops, &xrcmd_rcmd_ops, &xrcmd_module_options[0], }; static int xrcmd_init(opt_t * opt) { /* not implemented */ return 0; } /* * Use rcmd backchannel to propagate signals. * efd (IN) file descriptor connected socket (-1 if not used) * signum (IN) signal number to send */ static int xrcmd_signal(int efd, void *arg, int signum) { char c; if (efd >= 0) { /* set non-blocking mode for write - just take our best shot */ if (fcntl(efd, F_SETFL, O_NONBLOCK) < 0) err("%p: fcntl: %m\n"); c = (char) signum; if (write(efd, &c, 1) < 0) err("%p: write (efd): %m\n"); } return 0; } /* * The rcmd call itself. * ahost (IN) remote hostname * addr (IN) 4 byte internet address * locuser (IN) local username * remuser (IN) remote username * cmd (IN) command to execute * rank (IN) MPI rank for this process * fd2p (IN/OUT) if non-NULL, open stderr backchannel on this fd * s (RETURN) socket for stdout/sdin or -1 on failure */ static int xrcmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **arg) { struct sockaddr_in sin, from; sigset_t oldset, blockme; pid_t pid; int s, lport, timo, rv; char c; struct xpollfd xpfds[2]; memset (xpfds, 0, sizeof (xpfds)); pid = getpid(); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); pthread_sigmask(SIG_BLOCK, &blockme, &oldset); for (timo = 1, lport = IPPORT_RESERVED - 1;;) { s = privsep_rresvport(&lport); if (s < 0) { if (errno == EAGAIN) err("%p: %S: rcmd: socket: all ports in use\n", ahost); else err("%p: %S: rcmd: socket: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } fcntl(s, F_SETOWN, pid); memset (&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(RSH_PORT); rv = connect(s, (struct sockaddr *) &sin, sizeof(sin)); if (rv >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == ECONNREFUSED && timo <= 16) { (void) sleep(timo); timo *= 2; continue; } if (errno == EINTR) err("%p: %S: connect: timed out\n", ahost); else err("%p: %S: connect: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } lport--; if (fd2p == 0) { if (write(s, "", 1) != 1) { err ("%p: %S: write: %m\n", ahost); goto bad; } lport = 0; } else { char num[8]; int s2 = privsep_rresvport(&lport), s3; socklen_t len = sizeof(from); /* arg to accept */ if (s2 < 0) goto bad; listen(s2, 1); snprintf(num, sizeof(num), "%d", lport); if (write(s, num, strlen(num) + 1) != strlen(num) + 1) { err("%p: %S: rcmd: write (setting up stderr): %m\n", ahost); (void) close(s2); goto bad; } errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: rcmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: rcmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } s3 = accept(s2, (struct sockaddr *) &from, &len); (void) close(s2); if (s3 < 0) { err("%p: %S: rcmd: accept: %m\n", ahost); lport = 0; goto bad; } *fd2p = s3; from.sin_port = ntohs((u_short) from.sin_port); if (from.sin_family != AF_INET || from.sin_port >= IPPORT_RESERVED || from.sin_port < IPPORT_RESERVED / 2) { err("%p: %S: socket: protocol failure in circuit setup\n", ahost); goto bad2; } } if (write(s, locuser, strlen(locuser) + 1) < 0 || write(s, remuser, strlen(remuser) + 1) < 0 || write(s, cmd, strlen(cmd) + 1) < 0) { err("%p: %S: write (user,cmd): %m\n", ahost); goto bad2; } rv = read(s, &c, 1); if (rv < 0) { if (errno == EINTR) err("%p: %S: read: protocol failure: %s\n", ahost, "timed out"); else err("%p: %S: read: protocol failure: %m\n", ahost); goto bad2; } else if (rv != 1) { err("%p: %S: read: protocol failure: %s\n", ahost, "invalid response"); goto bad2; } if (c != 0) { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; while (read(s, &c, 1) == 1) { *p++ = c; if (c == '\n') break; } if (c != '\n') *p++ = '\n'; *p++ = '\0'; err("%S: %s", ahost, tmpbuf); goto bad2; } pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/genders.c0000664€^–Á €^–Á 0000004212715131211226024546 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "src/common/hostlist.h" #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/split.h" #include "src/common/xstring.h" #include "src/pdsh/mod.h" #include "src/pdsh/rcmd.h" #define ALL_NODES NULL #ifndef GENDERS_ALTNAME_ATTRIBUTE # define GENDERS_ALTNAME_ATTRIBUTE "altname" #endif #if STATIC_MODULES # define pdsh_module_info genders_module_info # define pdsh_module_priority genders_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; /* * Static genders module interface routines: */ static hostlist_t genders_wcoll(opt_t *pdsh_opts); static int genders_process_opt(opt_t *, int, char *); static int genders_init(void); static int genders_fini(void); static int genders_postop(opt_t *); #if !GENDERS_G_ONLY static bool allnodes = false; static bool opt_i = false; #endif /* !GENDERS_G_ONLY */ static bool genders_opt_invoked = false; static bool generate_altnames = false; static genders_t gh = NULL; static char *gfile = NULL; static List attrlist = NULL; static List excllist = NULL; /* * Export pdsh module operations structure */ struct pdsh_module_operations genders_module_ops = { (ModInitF) genders_init, (ModExitF) genders_fini, (ModReadWcollF) genders_wcoll, (ModPostOpF) genders_postop, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations genders_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option genders_module_options[] = { { 'g', "query,...", "target nodes using genders query", DSH | PCP, (optFunc) genders_process_opt }, { 'X', "query,...", "exclude nodes using genders query", DSH | PCP, (optFunc) genders_process_opt }, { 'F', "file", "use alternate genders file `file'", DSH | PCP, (optFunc) genders_process_opt }, #if !GENDERS_G_ONLY { 'i', NULL, "request alternate or canonical hostnames if applicable", DSH | PCP, (optFunc) genders_process_opt }, { 'a', NULL, "target all nodes except those with \"pdsh_all_skip\" attribute", DSH | PCP, (optFunc) genders_process_opt }, { 'A', NULL, "target all nodes listed in genders database", DSH | PCP, (optFunc) genders_process_opt }, #endif /* !GENDERS_G_ONLY */ PDSH_OPT_TABLE_END }; /* * Genders module info */ struct pdsh_module pdsh_module_info = { "misc", #if GENDERS_G_ONLY "genders-g", #else "genders", #endif /* GENDERS_G_ONLY */ "Jim Garlick ", "target nodes using libgenders and genders attributes", DSH | PCP, &genders_module_ops, &genders_rcmd_ops, &genders_module_options[0], }; /* * Static prototypes */ static genders_t _handle_create(); static hostlist_t _genders_to_altnames(genders_t g, hostlist_t hl); static hostlist_t _read_genders(List l); static hostlist_t _read_genders_attr(char *query); static void _genders_opt_verify(opt_t *opt); static int _delete_all (hostlist_t hl, hostlist_t dl); static int register_genders_rcmd_types (opt_t *opt); /* * Functions: */ int genders_process_opt(opt_t *pdsh_opts, int opt, char *arg) { switch (opt) { #if !GENDERS_G_ONLY case 'a': /* For -a, exclude nodes with "pdsh_all_skip" */ excllist = list_split_append (excllist, ",", "pdsh_all_skip"); case 'A': allnodes = true; break; case 'i': opt_i = true; break; #endif /* !GENDERS_G_ONLY */ case 'g': attrlist = list_split_append (attrlist, ",", arg); break; case 'X': excllist = list_split_append (excllist, ",", arg); break; case 'F': gfile = Strdup (arg); break; default: err("%p: genders_process_opt: invalid option `%c'\n", opt); return -1; break; } genders_opt_invoked = true; return 0; } static int genders_init(void) { return 0; } static int genders_fini(void) { if (attrlist) list_destroy (attrlist); if (excllist) list_destroy (excllist); if ((gh != NULL) && (genders_handle_destroy(gh) < 0)) errx("%p: Error destroying genders handle: %s\n", genders_errormsg(gh)); return 0; } static hostlist_t genders_wcoll(opt_t *opt) { _genders_opt_verify(opt); if (opt->wcoll) return (NULL); #if GENDERS_G_ONLY if (!attrlist) return NULL; #else if (!allnodes && !attrlist) return NULL; #endif /* !GENDERS_G_ONLY */ if (gh == NULL) gh = _handle_create(); generate_altnames = true; return _read_genders(attrlist); } static hostlist_t hostlist_intersect (hostlist_t h1, hostlist_t h2) { char *host; hostlist_t r = hostlist_create (NULL); hostlist_iterator_t i = hostlist_iterator_create (h1); while ((host = hostlist_next (i))) { if (hostlist_find (h2, host) >= 0) { hostlist_push_host (r, host); } free (host); } hostlist_iterator_destroy (i); return (r); } static hostlist_t genders_query_with_altnames (char *query) { hostlist_t r = _read_genders_attr (query); hostlist_t altlist = _genders_to_altnames (gh, r); hostlist_push_list (r, altlist); hostlist_destroy (altlist); return (r); } /* * Filter hostlist hl on a list of genders queries in query_list. * Multiple queries are ORed together, so a given host must only * match a single query. */ static hostlist_t genders_filter (hostlist_t hl, List query_list) { char *s; ListIterator i; hostlist_t result; if ((query_list == NULL) || (list_count (query_list) == 0)) return hl; if ((i = list_iterator_create (query_list)) == NULL) { err ("%p: genders: failed to create list or hostlist iterator\n"); return hl; } /* * Result is the union of the intersection of each genders query * with the incoming hostlist [hl] */ result = hostlist_create (NULL); while ((s = list_next (i))) { hostlist_t ghl = genders_query_with_altnames (s); hostlist_t r = hostlist_intersect (hl, ghl); hostlist_destroy (ghl); hostlist_push_list (result, r); } list_iterator_destroy (i); hostlist_uniq (result); hostlist_destroy (hl); return (result); } static int genders_postop(opt_t *opt) { hostlist_t hl = NULL; if (!opt->wcoll) return (0); if (gh == NULL) gh = _handle_create(); if (attrlist) opt->wcoll = genders_filter (opt->wcoll, attrlist); if (excllist && (hl = _read_genders (excllist))) { hostlist_t altlist = _genders_to_altnames (gh, hl); _delete_all (opt->wcoll, hl); _delete_all (opt->wcoll, altlist); hostlist_destroy (altlist); hostlist_destroy (hl); } #if !GENDERS_G_ONLY /* * Genders module returns altnames by default, but only * when genders fills in wcoll or with -i, not when filtering via * -g or -X. */ if ((generate_altnames && !opt_i) || (!generate_altnames && opt_i)) { hostlist_t hl = opt->wcoll; opt->wcoll = _genders_to_altnames(gh, hl); hostlist_destroy(hl); } #endif /* * Apply any pdsh_rcmd_type setting from genders file * based on current opt->wcoll. */ register_genders_rcmd_types (opt); return (0); } /* * Verify options passed to this module */ static void _genders_opt_verify(opt_t *opt) { #if !GENDERS_G_ONLY /* if (altnames && !allnodes && (gend_attr == NULL)) { * err("%p: Warning: Ignoring -i without -a or -g\n"); * altnames = false; * } */ if (allnodes && (attrlist != NULL)) errx("%p: Do not specify -A or -a with -g\n"); #endif /* !GENDERS_G_ONLY */ return; } static int _maxnamelen (genders_t g) { int maxvallen, maxnodelen; if ((maxvallen = genders_getmaxvallen(g)) < 0) errx("%p: genders: getmaxvallen: %s\n", genders_errormsg(g)); if ((maxnodelen = genders_getmaxvallen(g)) < 0) errx("%p: genders: getmaxnodelen: %s\n", genders_errormsg(g)); return (maxvallen > maxnodelen ? maxvallen : maxnodelen); } static hostlist_t _genders_to_altnames(genders_t g, hostlist_t hl) { hostlist_t retlist = NULL; hostlist_iterator_t i = NULL; int maxlen = 0; char *altname = NULL; char *altattr = GENDERS_ALTNAME_ATTRIBUTE; char *host = NULL; int rc; if ((retlist = hostlist_create(NULL)) == NULL) errx("%p: genders: hostlist_create: %m\n"); maxlen = _maxnamelen (g); altname = Malloc (maxlen + 1); if ((i = hostlist_iterator_create(hl)) == NULL) errx("%p: genders: hostlist_iterator_create: %m"); while ((host = hostlist_next (i))) { memset(altname, '\0', maxlen); rc = genders_testattr(g, host, altattr, altname, maxlen + 1); /* * If node not found, attempt to lookup canonical name via * alternate name. */ if ((rc < 0) && (genders_errnum(g) == GENDERS_ERR_NOTFOUND)) rc = genders_getnodes (g, &altname, 1, altattr, host); if (hostlist_push_host(retlist, (rc > 0 ? altname : host)) <= 0) err("%p: genders: warning: target `%s' not parsed: %m", host); free(host); } hostlist_iterator_destroy(i); Free((void **) &altname); return (retlist); } static hostlist_t _genders_to_hostlist(genders_t gh, char **nodes, int nnodes) { hostlist_t hl = NULL; int i; if ((hl = hostlist_create(NULL)) == NULL) errx("%p: genders: hostlist_create failed: %m"); for (i = 0; i < nnodes; i++) { if (hostlist_push_host(hl, nodes[i]) <= 0) err("%p: warning: target `%s' not parsed: %m\n", nodes[i]); } hostlist_uniq(hl); return hl; } static char * genders_filename_create (char *file) { char *genders_file; const char *gdir = getenv ("PDSH_GENDERS_DIR"); /* * Return a copy of filename if user specified an * absolute or relative path: */ if (file[0] == '/' || file[0] == '.') return Strdup (file); /* * Otherwise, append filename to * PDSH_GENDERS_DIR (or /etc by default) */ genders_file = gdir ? Strdup (gdir) : Strdup ("/etc"); xstrcatchar (&genders_file, '/'); xstrcat (&genders_file, file); return (genders_file); } static genders_t _handle_create() { char *gfile_env; char *genders_file = NULL; genders_t gh = NULL; if ((gh = genders_handle_create()) == NULL) errx("%p: Unable to create genders handle: %m\n"); if (gfile) genders_file = genders_filename_create (gfile); else if ((gfile_env = getenv ("PDSH_GENDERS_FILE"))) genders_file = genders_filename_create (gfile_env); else genders_file = genders_filename_create ("genders"); /* * Only exit on error from genders_load_data() if an genders * module option was explicitly invoked: */ if ((genders_load_data(gh, genders_file) < 0) && genders_opt_invoked) errx("%p: %s: %s\n", genders_file, genders_errormsg(gh)); return gh; } /* * Search attr argument for an '=' char indicating an * attr=value pair. If found, nullify '=' and return * pointer to value part. * * Returns NULL if no '=' found. */ #if !HAVE_GENDERS_QUERY static char * _get_val(char *attr) { char *val = NULL; if (attr == NULL) return (NULL); if ((val = strchr(attr, '='))) { *val = '\0'; val++; } return (val); } #endif /* !HAVE_GENDERS_QUERY */ static hostlist_t _read_genders_attr(char *query) { hostlist_t hl = NULL; char **nodes; int len, nnodes; if ((len = genders_nodelist_create(gh, &nodes)) < 0) errx("%p: genders: nodelist_create: %s\n", genders_errormsg(gh)); #if HAVE_GENDERS_QUERY if ((nnodes = genders_query (gh, nodes, len, query)) < 0) { errx("%p: Error querying genders for query \"%s\": %s\n", query ? query : "(all)", genders_errormsg(gh)); } #else /* !HAVE_GENDERS_QUERY */ { /* query defaults to just an attribute or attribute=value pair */ char *val; val = _get_val(query); if ((nnodes = genders_getnodes(gh, nodes, len, query, val)) < 0) { errx("%p: Error querying genders for attr \"%s\": %s\n", query ? query : "(all)", genders_errormsg(gh)); } } #endif /* HAVE_GENDERS_QUERY */ hl = _genders_to_hostlist(gh, nodes, nnodes); if (genders_nodelist_destroy(gh, nodes) < 0) { errx("%p: Error destroying genders node list: %s\n", genders_errormsg(gh)); } return hl; } static hostlist_t _read_genders (List attrs) { hostlist_t hl = NULL; char * query = NULL; if ((attrs == NULL) && (allnodes)) /* Special "all nodes" case */ return _read_genders_attr (ALL_NODES); if ((attrs == NULL) || (list_count (attrs) == 0)) return NULL; while ((query = list_pop (attrs))) { hostlist_t l = _read_genders_attr (query); if (hl == NULL) { hl = l; } else { hostlist_push_list (hl, l); hostlist_destroy (l); } Free ((void **)&query); } hostlist_uniq (hl); return (hl); } static int attrval_by_altname (genders_t g, const char *host, const char *attr, char *val, int len) { char *altname = NULL; char *altattr = GENDERS_ALTNAME_ATTRIBUTE; int maxlen = _maxnamelen (g); int rc = -1; altname = Malloc (maxlen + 1); memset (altname, 0, maxlen); if ((rc = genders_getnodes (g, &altname, 1, altattr, host)) > 0) rc = genders_testattr (g, altname, attr, val, sizeof (val)); Free ((void **) &altname); return rc; } /* * Parse the value of "pdsh_rcmd_type" and split into user and rcmd * strings, passing rcmd name (if any) in *rp, and user name (if any) * in *up. * * Allows pdsh_rcmd_type to be set to [user@][rcmd], where user@ and * rcmd are both optional. (i.e. you can set user or rcmd or both) */ static int rcmd_type_parse (char *val, char **rp, char **up) { char *p; *up = NULL; *rp = NULL; if ((p = strchr (val, '@'))) { *(p)++ = '\0'; *up = val; if (strlen (p) != 0) *rp = p; } else *rp = val; return (0); } static int register_genders_rcmd_types (opt_t *opt) { char *host; char *rcmd; char *user; char val[64]; char rcmd_attr[] = "pdsh_rcmd_type"; hostlist_iterator_t i = NULL; if (!opt->wcoll) return (0); /* * Assume no nodes have "pdsh_rcmd_type" attr if index fails: */ if (genders_index_attrvals (gh, rcmd_attr) < 0) return (0); i = hostlist_iterator_create (opt->wcoll); while ((host = hostlist_next (i))) { int rc; memset (val, 0, sizeof (val)); rc = genders_testattr (gh, host, rcmd_attr, val, sizeof (val)); /* * If host wasn't found, try to see if "host" is the altname * for this node, then lookup with the real name */ if (rc < 0 && (genders_errnum(gh) == GENDERS_ERR_NOTFOUND)) rc = attrval_by_altname (gh, host, rcmd_attr, val, sizeof (val)); rcmd_type_parse (val, &rcmd, &user); if (rc > 0) rcmd_register_defaults (host, rcmd, user); free (host); } hostlist_iterator_destroy (i); return 0; } static int _delete_all (hostlist_t hl, hostlist_t dl) { int rc = 0; char * host = NULL; hostlist_iterator_t i = hostlist_iterator_create (dl); while ((host = hostlist_next (i))) { rc += hostlist_delete_host (hl, host); free (host); } hostlist_iterator_destroy (i); return (rc); } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/slurm.c0000664€^–Á €^–Á 0000002630115131211226024255 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2007 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "src/common/hostlist.h" #include "src/common/split.h" #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/pdsh/mod.h" #include "src/pdsh/opt.h" /* * SLURM headers need to be included after pdsh header files to * avoid possibly conflicts with the definition of "bool" * * Also, Slurm inexplicably exports the "list.h" interface in slurm.h, * and we must define __list_datatypes_defined here to avoid conflict * with our internal List datatype. */ #define __list_datatypes_defined 1 /* * Also, Slurm>=23.x requires that `struct xlist` be typedef'd to list_t: */ typedef struct xlist list_t; typedef struct listIterator list_itr_t; /* * Also, Slurm exports the hostlist_t interface as well: */ #define __hostlist_t_defined 1 #include #include #if STATIC_MODULES # define pdsh_module_info slurm_module_info # define pdsh_module_priority slurm_module_priority #endif /* * Give this module low priority */ int pdsh_module_priority = 10; /* * Call this module after all option processing. The module will only * try to read the SLURM_JOBID if opt->wcoll is not already set. * Calling the module in postop allows us to be sure that all other * modules had a chance to update the wcoll. */ static int mod_slurm_init(void); static int mod_slurm_wcoll(opt_t *opt); static int mod_slurm_exit(void); static hostlist_t _slurm_wcoll(List jobids); static hostlist_t _slurm_wcoll_partition(List partitions); static hostlist_t _slurm_wcoll_constraint(hostlist_t, List constraint); static int slurm_process_opt(opt_t *, int opt, char *arg); static List job_list = NULL; static List partition_list = NULL; static List constraint_list = NULL; /* * Export generic pdsh module options */ struct pdsh_module_operations slurm_module_ops = { (ModInitF) mod_slurm_init, (ModExitF) mod_slurm_exit, (ModReadWcollF) mod_slurm_wcoll, (ModPostOpF) NULL }; /* * Export module options */ struct pdsh_module_option slurm_module_options[] = { { 'j', "jobid,...", "Run on nodes allocated to SLURM job(s) (\"all\" = all jobs)", DSH | PCP, (optFunc) slurm_process_opt }, { 'P', "partition,...", "Run on nodes contained in SLURM partition", DSH | PCP, (optFunc) slurm_process_opt }, { 'C', "feature,...", "Limit to SLURM nodes with any of the specified features", DSH | PCP, (optFunc) slurm_process_opt }, PDSH_OPT_TABLE_END }; /* * SLURM module info */ struct pdsh_module pdsh_module_info = { "misc", "slurm", "Mark Grondona ", "Target nodes contained in SLURM jobs or partitions, read SLURM_JOBID by default", DSH | PCP, &slurm_module_ops, NULL, &slurm_module_options[0], }; static int mod_slurm_init (void) { return (0); } static int32_t str2jobid (char *str) { char *p = NULL; long int jid; if (str == NULL) return (-1); jid = strtoul (str, &p, 10); if (*p != '\0') errx ("%p: invalid setting \"%s\" for -j or SLURM_JOBID\n", str); return ((int32_t) jid); } static int slurm_process_opt(opt_t *pdsh_opts, int opt, char *arg) { switch (opt) { case 'j': job_list = list_split_append (job_list, ",", arg); break; case 'P': partition_list = list_split_append (partition_list, ",", arg); break; case 'C': constraint_list = list_split_append (constraint_list, ",", arg); break; default: break; } return (0); } static int mod_slurm_exit(void) { if (job_list) list_destroy (job_list); if (partition_list) list_destroy (partition_list); if (constraint_list) list_destroy (constraint_list); return (0); } static void _append_hostlist (hostlist_t *hl1, hostlist_t hl2) { if (*hl1 == NULL) *hl1 = hostlist_create (""); hostlist_push_list (*hl1, hl2); } /* * If no wcoll has been established by this time, look for the * SLURM_JOBID env var, and set wcoll to the list of nodes allocated * to that job. */ static int mod_slurm_wcoll(opt_t *opt) { hostlist_t hlp = NULL, hlj = NULL, hlc = NULL; /* * If a list of partitions is supplied gather the hosts */ if (partition_list) hlp = _slurm_wcoll_partition (partition_list); if (job_list) hlj = _slurm_wcoll (job_list); /* * If no partition or job list, and empty wcoll, use * SLURM_JOBID if set. */ if (!hlp && !hlj && !opt->wcoll) hlj = _slurm_wcoll (NULL); if (hlp) { _append_hostlist (&opt->wcoll, hlp); hostlist_destroy (hlp); } if (hlj) { _append_hostlist (&opt->wcoll, hlj); hostlist_destroy (hlj); } if (constraint_list) { hlc = _slurm_wcoll_constraint(opt->wcoll, constraint_list); hostlist_destroy(opt->wcoll); opt->wcoll = hlc; } return 0; } static int32_t _slurm_jobid (void) { return (str2jobid (getenv ("SLURM_JOBID"))); } static int _find_id (char *jobid, uint32_t *id) { return (*id == str2jobid (jobid)); } static int _find_str (char *jobid, char *str) { return (strcmp (jobid, str) == 0); } /* * Return non-zero if jobid is in list of ids requested by user */ static int _jobid_requested (List l, uint32_t jobid) { if (l == NULL) return (0); return (list_delete_all (l, (ListFindF)_find_id, &jobid)); } static int _partition_requested (List l, char *partition) { if (l == NULL) return (0); return (list_delete_all (l, (ListFindF)_find_str, partition)); } static int _alljobids_requested (List l) { char *all = "all"; if (l == NULL) return (0); return (list_delete_all (l, (ListFindF)_find_str, all)); } static int _features_include (const char *f, const char *c) { int l; l = strlen(c); while (f) { while (*f == ',') f++; if (strncmp(f, c, l) == 0 && (f[l] == '\0' || f[l] == ',')) return (1); f = strchr(f, ','); } return (0); } static hostlist_t _hl_append (hostlist_t hl, char *nodes) { if (hl == NULL) return (hostlist_create (nodes)); else hostlist_push (hl, nodes); return (hl); } /* * Make sure, slurm_init() is called before any call to the Slurm API */ static void _slurm_init() { static bool _inited = false; if (_inited) return; #if SLURM_VERSION_NUMBER >= SLURM_VERSION_NUM(20,11,0) slurm_init(NULL); #endif _inited = true; } static hostlist_t _slurm_wcoll (List joblist) { int i; hostlist_t hl = NULL; job_info_msg_t * msg; int32_t envjobid = 0; int alljobids = 0; if ((joblist == NULL) && (envjobid = _slurm_jobid()) < 0) return (NULL); _slurm_init(); if (slurm_load_jobs((time_t) NULL, &msg, SHOW_ALL) < 0) errx ("Unable to contact slurm controller: %s\n", slurm_strerror (errno)); /* * Check for "all" in joblist */ alljobids = _alljobids_requested (joblist); for (i = 0; i < msg->record_count; i++) { job_info_t *j = &msg->job_array[i]; if (alljobids && j->job_state == JOB_RUNNING) hl = _hl_append (hl, j->nodes); else if (!joblist && (j->job_id == envjobid)) { /* * Only use SLURM_JOBID environment variable if user * didn't override with -j option */ hl = hostlist_create (j->nodes); break; } else if (_jobid_requested (joblist, j->job_id)) { hl = _hl_append (hl, j->nodes); /* * Exit when there is no more jobids to search */ if (list_count (joblist) == 0) break; } } slurm_free_job_info_msg (msg); if (hl) hostlist_uniq (hl); return (hl); } static hostlist_t _slurm_wcoll_partition (List partitionlist) { int i; char * str; hostlist_t hl = NULL; partition_info_msg_t * msg; partition_info_t * p; ListIterator li; _slurm_init(); if (slurm_load_partitions((time_t) NULL, &msg, SHOW_ALL) < 0) errx ("Unable to contact slurm controller: %s\n", slurm_strerror (errno)); for (i = 0; i < msg->record_count; i++){ p = &msg->partition_array[i]; if (_partition_requested (partitionlist, p->name)) { hl = _hl_append (hl, p->nodes); /* * Exit when there is no more partitions to search */ if (list_count (partitionlist) == 0) break; } } /* * Anything left in partitionlist wasn't found, emit a warning */ li = list_iterator_create(partitionlist); while ((str = list_next(li))){ err("%p: Warning - partition %s not found\n", str); } slurm_free_partition_info_msg (msg); if (hl) hostlist_uniq (hl); return (hl); } static hostlist_t _slurm_wcoll_constraint (hostlist_t wl, List constraintlist) { int i; hostlist_t hl; node_info_msg_t * msg; node_info_t * n; char *f; char *c; ListIterator li; _slurm_init(); if (slurm_load_node((time_t) NULL, &msg, SHOW_ALL) < 0) errx ("Unable to contact slurm controller: %s\n", slurm_strerror (errno)); li = list_iterator_create(constraintlist); hl = hostlist_create(""); for (i = 0; i < msg->record_count; i++){ n = &msg->node_array[i]; if (hostlist_find(wl, n->name) < 0) continue; f = n->features_act ? n->features_act : n->features; if (!f) continue; list_iterator_reset(li); while ((c = list_next(li))){ if (_features_include(f, c)) { hostlist_push_host(hl, n->name); continue; } } } return (hl); } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/dshgroup.c0000664€^–Á €^–Á 0000001305115131211226024744 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2005-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #ifndef DSHGROUP_PATH #define DSHGROUP_PATH "/etc/dsh/group" #endif #include /* access */ #include /* getenv */ #include #include "src/pdsh/wcoll.h" #include "src/pdsh/mod.h" #include "src/common/hostlist.h" #include "src/common/xmalloc.h" #include "src/common/err.h" #include "src/common/list.h" #include "src/common/split.h" #if STATIC_MODULES # define pdsh_module_info dshgroup_module_info # define pdsh_module_priority dshgroup_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static hostlist_t read_groupfile(opt_t *opt); static int dshgroup_postop (opt_t *); static int dshgroup_process_opt(opt_t *, int, char *); static List groups = NULL; static List exgroups = NULL; /* * Export pdsh module operations structure */ struct pdsh_module_operations dshgroup_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) read_groupfile, (ModPostOpF) dshgroup_postop, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations dshgroup_rcmd_ops = { (RcmdInitF) NULL, (RcmdSigF) NULL, (RcmdF) NULL, }; /* * Export module options */ struct pdsh_module_option dshgroup_module_options[] = { { 'g', "groupname", "target hosts in dsh group \"groupname\"", DSH | PCP, (optFunc) dshgroup_process_opt }, { 'X', "groupname", "exclude hosts in dsh group \"groupname\"", DSH | PCP, (optFunc) dshgroup_process_opt }, PDSH_OPT_TABLE_END }; /* * Machines module info */ struct pdsh_module pdsh_module_info = { "misc", "dshgroup", "Mark Grondona ", "Read list of targets from dsh-style \"group\" files", DSH | PCP, &dshgroup_module_ops, &dshgroup_rcmd_ops, &dshgroup_module_options[0], }; static int dshgroup_process_opt(opt_t *pdsh_opt, int opt, char *arg) { switch (opt) { case 'g': groups = list_split_append (groups, ",", arg); break; case 'X': exgroups = list_split_append (exgroups, ",", arg); break; default: err ("%p: dshgroup_process_opt: invalid option `%c'\n", opt); return -1; break; } return 0; } static hostlist_t _read_groupfile (const char *group) { int maxpathlen; char path [4096]; char *home = getenv("HOME"); char *dshgroup_path = getenv("DSHGROUP_PATH"); maxpathlen = sizeof (path) - 1; if (!dshgroup_path) dshgroup_path = DSHGROUP_PATH; if (home) { int n; n = snprintf (path, maxpathlen,"%s/.dsh/group:%s", home, dshgroup_path); if (n <= 0 || n > maxpathlen) errx ("%p: dshgroup: search path (%s/.dsh/group:%s) overflow\n", home, dshgroup_path); } else { err ("%p: dshgroup: warning: Unable to read $HOME env var\n"); strncpy (path, dshgroup_path, sizeof (path)); } return read_wcoll_path (path, group); } static hostlist_t _read_groups (List grouplist) { ListIterator i = NULL; hostlist_t hl = NULL; char *group; i = list_iterator_create (grouplist); while ((group = list_next (i))) { hostlist_t l = _read_groupfile (group); if (l == NULL) continue; if (hl == NULL) { hl = l; } else { hostlist_push_list (hl, l); hostlist_destroy (l); } } list_iterator_destroy (i); if (hl != NULL) hostlist_uniq (hl); return (hl); } static hostlist_t read_groupfile(opt_t *opt) { if (!groups) return NULL; if (opt->wcoll && groups) errx("Do not specify both -w and -g"); return _read_groups (groups); } static int _delete_all (hostlist_t hl, hostlist_t dl) { int rc = 0; char * host = NULL; hostlist_iterator_t i = hostlist_iterator_create (dl); while ((host = hostlist_next (i))) { rc += hostlist_delete_host (hl, host); free (host); } hostlist_iterator_destroy (i); return (rc); } static int dshgroup_postop (opt_t *opt) { hostlist_t hl = NULL; if (!opt->wcoll || !exgroups) return (0); if ((hl = _read_groups (exgroups)) == NULL) return (0); _delete_all (opt->wcoll, hl); return 0; } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/mcmd.c0000664€^–Á €^–Á 0000004776015131211226024047 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Started with BSD mcmd.c which is: * * Copyright (c) 1983, 1993, 1994, 2003 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 5. This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * 6. This is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 7. You should have received a copy of the GNU General Public License; * if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)mcmd.c Based from: 8.3 (Berkeley) 3/26/94"; #endif /* LIBC_SCCS and not lint */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef HAVE_PTHREAD #include #endif #include #include #include #if HAVE_FCNTL_H #include #endif #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include "src/common/macros.h" /* LINEBUFSIZE && IP_ADDR_LEN */ #include "src/common/err.h" #include "src/common/fd.h" #include "src/common/xpoll.h" #include "src/pdsh/mod.h" #define MRSH_PROTOCOL_VERSION "2.1" #define MRSH_PORT 21212 #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif #define MRSH_LOCALHOST_KEY "LHOST" #define MRSH_LOCALHOST_KEYLEN 5 #ifdef HAVE_PTHREAD #define SET_PTHREAD() pthread_sigmask(SIG_BLOCK, &blockme, &oldset) #define RESTORE_PTHREAD() pthread_sigmask(SIG_SETMASK, &oldset, NULL) #define EXIT_PTHREAD() RESTORE_PTHREAD(); \ return -1 #else #define SET_PTHREAD() #define RESTORE_PTHREAD() #define EXIT_PTHREAD() return -1 #endif #if STATIC_MODULES # define pdsh_module_info mcmd_module_info # define pdsh_module_priority mcmd_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; static int mcmd_init(opt_t *); static int mcmd_signal(int, void *, int); static int mcmd(char *, char *, char *, char *, char *, int, int *, void **); /* random num for all jobs in this group */ static unsigned int randy = -1; /* * Export pdsh module operations structure */ struct pdsh_module_operations mcmd_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations mcmd_rcmd_ops = { (RcmdInitF) mcmd_init, (RcmdSigF) mcmd_signal, (RcmdF) mcmd, }; /* * Export module options */ struct pdsh_module_option mcmd_module_options[] = { PDSH_OPT_TABLE_END }; /* * Mcmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "mrsh", "Al Chu ", "mrsh rcmd connect method", DSH | PCP, &mcmd_module_ops, &mcmd_rcmd_ops, &mcmd_module_options[0], }; static int mcmd_init(opt_t * opt) { int rv, rand_fd; /* * Drop privileges if running setuid root */ if ((geteuid() == 0) && (getuid() != 0)) { if ((setuid (getuid ()) < 0)) { err("%p: mcmd: setuid () failed\n"); return -1; } } /* * Generate a random number to send in our package to the * server. We will see it again and compare it when the * server sets up the stderr socket and sends it to us. * We need to loop for the tiny possibility we read 0 :P */ if ((rand_fd = open ("/dev/urandom", O_RDONLY | O_NONBLOCK)) < 0 ) { err("%p: mcmd: Open of /dev/urandom failed\n"); return -1; } do { if ((rv = read (rand_fd, &randy, sizeof(uint32_t))) < 0) { close(rand_fd); err("%p: mcmd: Read of /dev/urandom failed\n"); return -1; } if (rv < (int) (sizeof(uint32_t))) { close(rand_fd); err("%p: mcmd: Read returned too few bytes\n"); return -1; } } while (randy == 0); close(rand_fd); return 0; } static int mcmd_signal(int fd, void *arg, int signum) { char c; if (fd >= 0) { /* set non-blocking mode for write - just take our best shot */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) err("%p: fcntl: %m\n"); c = (char) signum; if (write(fd, &c, 1) < 0) err("%p: write (signalfd): %m\n"); } return 0; } /* * If `host' corresponds to a standard "locahost" target, then * encode mrsh localhost key and hostname into str, returning * number of bytes written into str. Otherwise do nothing and return 0. */ static int encode_localhost_string (const char *host, char *str, int maxlen) { char hostname[MAXHOSTNAMELEN+1]; if (strcmp (host, "localhost") && strcmp (host, "127.0.0.1")) return (0); if (maxlen < MRSH_LOCALHOST_KEYLEN) return (-1); memset (hostname, '\0', MAXHOSTNAMELEN + 1); if (gethostname (hostname, MAXHOSTNAMELEN) < 0) errx ("mcmd: gethostname: %m\n"); strncpy (str, MRSH_LOCALHOST_KEY, MRSH_LOCALHOST_KEYLEN + 1); strncat (str, hostname, maxlen - MRSH_LOCALHOST_KEYLEN); return (strlen (str)); } /* * Derived from the mcmd() libc call, with modified interface. * This version is MT-safe. Errors are displayed in pdsh-compat format. * Connection can time out. * ahost (IN) target hostname * addr (IN) 4 byte internet address * locuser (IN) local username * remuser (IN) remote username * cmd (IN) remote command to execute under shell * rank (IN) not used * fd2p (IN) if non NULL, return stderr file descriptor here * int (RETURN) -1 on error, socket for I/O on success * * Originally by Mike Haskell for mrsh, modified slightly to work with pdsh by: * - making mcmd always thread safe * - using "err" function output errors. * - passing in address as addr intead of calling gethostbyname * - using default mshell port instead of calling getservbyname * */ static int mcmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **argp) { struct sockaddr m_socket; struct sockaddr_in *getp; struct sockaddr_in sin, from; struct sockaddr_storage ss; struct in_addr m_in; unsigned int rand, randl; unsigned char *hptr; int s, s2, rv, mcount, lport; char c; char num[6] = {0}; char *mptr; char *mbuf; char *tmbuf; char *m; char *mpvers; char num_seq[12] = {0}; socklen_t len; sigset_t blockme; sigset_t oldset; char haddrdot[MAXHOSTNAMELEN + MRSH_LOCALHOST_KEYLEN + 1] = {0}; munge_ctx_t ctx; struct xpollfd xpfds[2]; memset (xpfds, 0, sizeof (xpfds)); memset (&sin, 0, sizeof (sin)); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); sigaddset(&blockme, SIGPIPE); SET_PTHREAD(); /* Convert randy to decimal string, 0 if we dont' want stderr */ if (fd2p != NULL) snprintf(num_seq, sizeof(num_seq),"%d",randy); else snprintf(num_seq, sizeof(num_seq),"%d",0); /* * Start setup of the stdin/stdout socket... */ lport = 0; len = sizeof(struct sockaddr_in); if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { err("%p: %S: mcmd: socket call stdout failed: %m\n", ahost); EXIT_PTHREAD(); } memset (&ss, '\0', sizeof(ss)); ss.ss_family = AF_INET; if (bind(s, (struct sockaddr *)&ss, len) < 0) { err("%p: %S: mcmd: bind failed: %m\n", ahost); goto bad; } sin.sin_family = AF_INET; memcpy(&sin.sin_addr.s_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(MRSH_PORT); if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { err("%p: %S: mcmd: connect failed: %m\n", ahost); goto bad; } lport = 0; s2 = -1; if (fd2p != NULL) { /* * Start the socket setup for the stderr. */ struct sockaddr_in sin2; if ((s2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) { err("%p: %S: mcmd: socket call for stderr failed: %m\n", ahost); goto bad; } memset (&sin2, 0, sizeof(sin2)); sin2.sin_family = AF_INET; sin2.sin_addr.s_addr = htonl(INADDR_ANY); sin2.sin_port = 0; if (bind(s2,(struct sockaddr *)&sin2, sizeof(sin2)) < 0) { err("%p: %S: mcmd: bind failed: %m\n", ahost); close(s2); goto bad; } len = sizeof(struct sockaddr); /* * Retrieve our port number so we can hand it to the server * for the return (stderr) connection... */ /* getsockname is thread safe */ if (getsockname(s2,&m_socket,&len) < 0) { err("%p: %S: mcmd: getsockname failed: %m\n", ahost); close(s2); goto bad; } getp = (struct sockaddr_in *)&m_socket; lport = ntohs(getp->sin_port); if (listen(s2, 5) < 0) { err("%p: %S: mcmd: listen() failed: %m\n", ahost); close(s2); goto bad; } } /* put port in buffer. will be 0 if user didn't want stderr */ snprintf(num,sizeof(num),"%d",lport); /* * Use special keyed string if target is localhost, otherwise, * encode the IP addr string. */ if (!encode_localhost_string (ahost, haddrdot, sizeof (haddrdot))) { /* inet_ntoa is not thread safe, so we use the following, * which is more or less ripped from glibc */ memcpy(&m_in.s_addr, addr, IP_ADDR_LEN); hptr = (unsigned char *)&m_in; sprintf(haddrdot, "%u.%u.%u.%u", hptr[0], hptr[1], hptr[2], hptr[3]); } /* * We call munge_encode which will take what we write in and return a * pointer to an munged buffer. What we get back is a null terminated * string of encrypted characters. * * The format of the unmunged buffer is as follows (each a string terminated * with a '\0' (null): * * stderr_port_number & /dev/urandom_client_produce_number are 0 * if user did not request stderr socket * SIZE EXAMPLE * ========== ============= * remote_user_name variable "mhaskell" * '\0' * protocol version < 12 bytes "1.2" * '\0' * dotted_decimal_address_of_this_server 7-15 bytes "134.9.11.155" * '\0' * stderr_port_number 4-8 bytes "50111" * '\0' * /dev/urandom_client_produced_number 1-8 bytes "1f79ca0e" * '\0' * users_command variable "ls -al" * '\0' '\0' * * (The last extra null is accounted for in the following line's * last strlen() call.) * */ mpvers = MRSH_PROTOCOL_VERSION; mcount = ((strlen(remuser)+1) + (strlen(mpvers)+1) + (strlen(haddrdot)+1) + (strlen(num)+1) + (strlen(num_seq)+1) + strlen(cmd)+2); tmbuf = mbuf = malloc(mcount); if (tmbuf == NULL) { err("%p: %S: mcmd: Error from malloc\n", ahost); close(s2); goto bad; } /* * The following memset() call takes the extra trailing null as * part of its count as well. */ memset(mbuf,0,mcount); mptr = strcpy(mbuf, remuser); mptr += strlen(remuser)+1; mptr = strcpy(mptr, mpvers); mptr += strlen(mpvers)+1; mptr = strcpy(mptr, haddrdot); mptr += strlen(haddrdot)+1; mptr = strcpy(mptr, num); mptr += strlen(num)+1; mptr = strcpy(mptr, num_seq); mptr += strlen(num_seq)+1; mptr = strcpy(mptr, cmd); ctx = munge_ctx_create(); if ((rv = munge_encode(&m,ctx,mbuf,mcount)) != EMUNGE_SUCCESS) { err("%p: %S: mcmd: munge_encode: %s\n", ahost, munge_ctx_strerror(ctx)); munge_ctx_destroy(ctx); if (s2 >= 0) close(s2); free(tmbuf); goto bad; } munge_ctx_destroy(ctx); mcount = (strlen(m)+1); /* * Write stderr port in the clear in case we can't decode for * some reason (i.e. bad credentials). May be 0 if user * doesn't want stderr */ if (fd2p != NULL) { rv = fd_write_n(s, num, strlen(num)+1); if (rv != (strlen(num) + 1)) { free(m); free(tmbuf); if (errno == EPIPE) err("%p: %S: mcmd: Lost connection (EPIPE): %m", ahost); else err("%p: %S: mcmd: Write of stderr port failed: %m\n", ahost); close(s2); goto bad; } } else { if (write(s, "", 1) != 1) { err ("%p: %S: mcmd: write NUL stderr port: %m\n", ahost); goto bad; } lport = 0; } /* * Write the munge_encoded blob to the socket. */ rv = fd_write_n(s, m, mcount); if (rv != mcount) { free(m); free(tmbuf); if (errno == EPIPE) err("%p: %S: mcmd: Lost connection: %m\n", ahost); else err("%p: %S: mcmd: Write to socket failed: %m\n", ahost); close(s2); goto bad; } free(m); free(tmbuf); if (fd2p != NULL) { /* * Wait for stderr connection from daemon. */ int s3; errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if ( ((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: mcmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: mcmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } errno = 0; len = sizeof(from); /* arg to accept */ if ((s3 = accept(s2, (struct sockaddr *)&from, &len)) < 0) { err("%p: %S: mcmd: accept (stderr) failed: %m\n", ahost); close(s2); goto bad; } if (from.sin_family != AF_INET) { err("%p: %S: mcmd: bad family type: %d\n", ahost, from.sin_family); goto bad2; } close(s2); /* * The following fixes a race condition between the daemon * and the client. The daemon is waiting for a null to * proceed. We do this to make sure that we have our * socket is up prior to the daemon running the command. */ if (write(s,"",1) < 0) { err("%p: %S: mcmd: Could not communicate to daemon to proceed: %m\n", ahost); close(s3); goto bad; } /* * Read from our stderr. The server should have placed our * random number we generated onto this socket. */ rv = fd_read_n(s3, &rand, sizeof(rand)); if (rv != (ssize_t) (sizeof(rand))) { err("%p: %S: mcmd: Bad read of expected verification " "number off of stderr socket: %m\n", ahost); close(s3); goto bad; } randl = ntohl(rand); if (randl != randy) { char tmpbuf[LINEBUFSIZE] = {0}; char *tptr = &tmpbuf[0]; memcpy(tptr,(char *) &rand,sizeof(rand)); tptr += sizeof(rand); if (fd_read_line (s3, tptr, LINEBUFSIZE - sizeof(rand)) < 0) err("%p: %S: mcmd: Read error from remote host: %m\n", ahost); else err("%p: %S: mcmd: Error: %s\n", ahost, &tmpbuf[0]); close(s3); goto bad; } /* * Set the stderr file descriptor for the user... */ *fd2p = s3; } if ((rv = read(s, &c, 1)) < 0) { err("%p: %S: mcmd: read: protocol failure: %m\n", ahost); goto bad2; } if (rv != 1) { err("%p: %S: mcmd: read: protocol failure: invalid response\n", ahost); goto bad2; } if (c != '\0') { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; if (fd_read_line (s, &tmpbuf[0], LINEBUFSIZE) < 0) err("%p: %S: mcmd: Error from remote host\n", ahost); else err("%p: %S: mcmd: Error: %s\n", ahost, tmpbuf); goto bad2; } RESTORE_PTHREAD(); return (s); bad2: if (lport) close(*fd2p); bad: close(s); EXIT_PTHREAD(); } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/modules/k4cmd.c0000664€^–Á €^–Á 0000002621115131211226024115 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * This code is based on the MIT Kerberos IV kcmd.c with some athena hacks * removed and MT safety added, and the interface changed. Original UC regents * header from BSD included below. * * XXX for some reason it takes a really long time (several seconds per node) * to acquire service tickets! Why? */ /* * Copyright (c) 1983 Regents of the University of California. All rights * reserved. * * Redistribution and use in source and binary forms are permitted provided that * the above copyright notice and this paragraph are duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed by the University of California, Berkeley. The * name of the University may not be used to endorse or promote products * derived from this software without specific prior written permission. THIS * SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_PTHREAD_H #include #endif #include #include #include #include #include #include #if HAVE_SYS_FILE_H #include #endif #include #include #include #include #include #include #include #include #include /* for F_SETOWN */ #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/macros.h" #include "src/common/err.h" #include "src/common/list.h" #include "src/common/xpoll.h" #include "src/pdsh/privsep.h" #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif #define KCMD_PORT 544 #if STATIC_MODULES # define pdsh_module_info k4cmd_module_info # define pdsh_module_priority k4cmd_module_priority #endif int pdsh_module_priority = DEFAULT_MODULE_PRIORITY; extern errno; extern char *inet_ntoa(); static int k4cmd_init(opt_t *); static int k4cmd_signal(int, void *, int); static int k4cmd(char *, char *, char *, char *, char *, int, int *, void **); /* * Export pdsh module operations structure */ struct pdsh_module_operations k4cmd_module_ops = { (ModInitF) NULL, (ModExitF) NULL, (ModReadWcollF) NULL, (ModPostOpF) NULL, }; /* * Export rcmd module operations */ struct pdsh_rcmd_operations k4cmd_rcmd_ops = { (RcmdInitF) k4cmd_init, (RcmdSigF) k4cmd_signal, (RcmdF) k4cmd, }; /* * Export module options */ struct pdsh_module_option k4cmd_module_options[] = { PDSH_OPT_TABLE_END }; /* * k4cmd module info */ struct pdsh_module pdsh_module_info = { "rcmd", "k4", "Jim Garlick ", "kerberos based rcmd connect method", DSH | PCP, &k4cmd_module_ops, &k4cmd_rcmd_ops, &k4cmd_module_options[0], }; static int k4cmd_init(opt_t * opt) { /* not implemented */ return 0; } /* * Use rcmd backchannel to propagate signals. * efd (IN) file descriptor connected socket (-1 if not used) * signum (IN) signal number to send */ static int k4cmd_signal(int efd, void *arg, int signum) { char c; if (efd >= 0) { /* set non-blocking mode for write - just take our best shot */ if (fcntl(efd, F_SETFL, O_NONBLOCK) < 0) err("%p: fcntl: %m\n"); c = (char) signum; write(efd, &c, 1); } return 0; } /* * The rcmd call itself. * ahost (IN) remote hostname * addr (IN) IP address * locuser (IN) local username * remuser (IN) remote username * cmd (IN) command to execute * rank (IN) MPI rank * fd2p (IN/OUT) if non-NULL, open stderr backchannel on this fd * s (RETURN) socket for stdout/sdin or -1 on failure */ static int k4cmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **arg) { KTEXT_ST ticket; /* kerberos IV context */ CREDENTIALS cred; Key_schedule schedule; MSG_DAT msg_data; struct sockaddr_in faddr; struct sockaddr_in laddr; int s, pid; sigset_t oldset, blockme; struct sockaddr_in sin, from; char c; int lport = IPPORT_RESERVED - 1; unsigned long krb_options = 0L; static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; int status; int rc, rv; struct xpollfd xpfds[2]; pid = getpid(); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); pthread_sigmask(SIG_BLOCK, &blockme, &oldset); for (;;) { s = privsep_rresvport(&lport); if (s < 0) { if (errno == EAGAIN) err("%p: %S: socket: All ports in use\n", ahost); else err("%p: %S: k4cmd: socket: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } fcntl(s, F_SETOWN, pid); sin.sin_family = AF_INET; memcpy((caddr_t) & sin.sin_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(KCMD_PORT); rv = connect(s, (struct sockaddr *) &sin, sizeof(sin)); if (rv >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == EINTR) err("%p: %S: connect timed out\n", ahost); else err("%p: %S: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } if (fd2p == 0) { write(s, "", 1); lport = 0; } else { char num[8]; int s2, s3; socklen_t len = sizeof(from); s2 = privsep_rresvport(&lport); if (s2 < 0) { goto bad; } listen(s2, 1); (void) snprintf(num, sizeof(num), "%d", lport); if (write(s, num, strlen(num) + 1) != strlen(num) + 1) { err("%p: %S: write: setting up stderr: %m\n", ahost); (void) close(s2); goto bad; } errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: k4cmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: k4cmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } s3 = accept(s2, (struct sockaddr *) &from, &len); (void) close(s2); if (s3 < 0) { err("%p: %S: accept: %m\n", ahost); lport = 0; goto bad; } *fd2p = s3; from.sin_port = ntohs((u_short) from.sin_port); } /* * Kerberos-authenticated service. Don't have to send locuser, since * its already in the ticket, and we'll extract it on the other side. */ /*krb_options |= KOPT_DONT_CANON; */ pthread_mutex_lock(&mylock); status = krb_sendauth(krb_options, s, &ticket, "rcmd", ahost, NULL, (unsigned long) pid, &msg_data, &cred, schedule, &laddr, &faddr, "KCMDV0.1"); if (status != KSUCCESS) { /* * this part involves some very intimate knowledge of a * particular sendauth implementation to pry out the old * bits. This only catches the case of total failure -- but * that's the one where we get useful data from the remote * end. If we even get an authenticator back, then the * problem gets diagnosed locally anyhow. */ extern KRB_INT32 __krb_sendauth_hidden_tkt_len; char *old_data = (char *) &__krb_sendauth_hidden_tkt_len; char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; if ((status == KFAILURE) && (*old_data == 1)) { strncpy(tmpbuf, old_data + 1, 3); tmpbuf[3] = '\0'; err("%p: %S: %s", ahost, tmpbuf); *old_data = (-1); } if ((status == KFAILURE) && (*old_data == (char) -1)) { while (read(s, &c, 1) == 1) { /*(void) write(2, &c, 1); */ *p++ = c; if (c == '\n') break; } *p++ = '\0'; err("%p: %S: %s", ahost, tmpbuf); status = -1; } switch (status) { case KDC_PR_UNKNOWN: err("%p: %S: not registered for kerberos\n", ahost); break; case NO_TKT_FIL: err("%p: %S: no tickets file found\n", ahost); break; default: err("%p: %S: k4cmd failed: %s\n", ahost, (status == -1) ? "k4cmd protocol failure" : krb_get_err_text(status)); } pthread_mutex_unlock(&mylock); goto bad2; } pthread_mutex_unlock(&mylock); (void) write(s, remuser, strlen(remuser) + 1); (void) write(s, cmd, strlen(cmd) + 1); if ((rc = read(s, &c, 1)) != 1) { if (rc == -1) { err("%p: %S: read: %m\n", ahost); } else { err("%p: %S: k4cmd: bad connection with remote host\n", ahost); } goto bad2; } if (c != 0) { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; while (read(s, &c, 1) == 1) { *p++ = c; if (c == '\n') break; } if (c != '\n') *p++ = '\n'; *p++ = '\0'; err("%S: %s", ahost, tmpbuf); goto bad2; } pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/0000775€^–Á €^–Á 0000000000015131211226022565 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/src/common/Makefile.am0000664€^–Á €^–Á 0000000132415131211226024621 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** ## Process this file with automake to produce Makefile.in. ##***************************************************************************** include $(top_srcdir)/config/Make-inc.mk AM_CPPFLAGS = \ -I$(top_srcdir) noinst_LTLIBRARIES = \ libcommon.la libcommon_la_SOURCES = \ macros.h \ err.c \ err.h \ fd.c \ fd.h \ hostlist.c \ hostlist.h \ list.c \ list.h \ split.c \ split.h \ xmalloc.c \ xmalloc.h \ xpoll.c \ xpoll.h \ xstring.c \ xstring.h \ pipecmd.c \ pipecmd.h pdsh-2.36/src/common/pipecmd.c0000664€^–Á €^–Á 0000002173715131211226024364 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2007 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/err.h" #include "src/common/list.h" #include "src/common/split.h" #include "src/pdsh/dsh.h" #include "src/pdsh/mod.h" #include "src/common/pipecmd.h" struct pipe_info_struct { pid_t pid; /* pid of child command */ char *path; /* path to executable */ char *cmd; /* basename of path */ char *target; /* Target host of this instance */ char *username; /* Username for this instance */ char **args; /* Cmd arguments */ int rank; /* Rank 1-N of this instance */ int fd; /* stdin/out fd */ int efd; /* stderr fd */ }; static int _pipecmd (char *path, char *args[], int *fd2p, pid_t *ppid); pipecmd_t pipe_info_create (const char *path, const char *target, const char *user, int rank) { struct pipe_info_struct *e = Malloc (sizeof (*e)); e->path = Strdup (path); e->cmd = Strdup (xbasename (e->path)); e->target = Strdup (target); e->username = Strdup (user); e->rank = rank; e->pid = (pid_t) 0; e->args = NULL; e->fd = -1; e->efd = -1; return (e); } static void pipe_info_destroy (struct pipe_info_struct *e) { if (e == NULL) return; Free ((void **) &e->path); Free ((void **) &e->cmd); Free ((void **) &e->target); Free ((void **) &e->username); Free ((void **) &e); } static char * pipecmd_format_arg (pipecmd_t e, const char *arg) { char buf [64]; const char *p; char *str = NULL; p = arg; while (*p != '\0') { if (*p == '%') { p++; switch (*p) { case 'h' : /* '%n' => target name */ xstrcat (&str, e->target); break; case 'u': xstrcat (&str, e->username); break; case 'n': snprintf (buf, sizeof (buf) - 1, "%d", e->rank); xstrcat (&str, buf); break; case '%': xstrcatchar (&str, '%'); break; default: xstrcatchar (&str, '%'); xstrcatchar (&str, *p); break; } } else xstrcatchar (&str, *p); p++; } return (str); } /* * Loop through argv expanding any format characters */ static char ** cmd_args_create (pipecmd_t e, const char **argv) { int i = 0; int n = 0; char **args = NULL; while (argv[n]) n++; args = Malloc ((n + 2) * sizeof (char *)); /* * Cmd should be args[0]: */ args [0] = Strdup (e->cmd); for (i = 1; i < n+1; i++) args[i] = pipecmd_format_arg (e, argv[i-1]); args[i] = NULL; return (args); } static void cmd_args_destroy (char **args) { int i = 0; if (args == NULL) return; while (args[i]) Free ((void **) &args[i++]); Free ((void **) &args); } void pipecmd_destroy (pipecmd_t p) { cmd_args_destroy (p->args); pipe_info_destroy (p); return; } pipecmd_t pipecmd (const char *path, const char **args, const char *target, const char *user, int rank) { pipecmd_t p = pipe_info_create (path, target, user, rank); p->args = cmd_args_create (p, args); if ((p->fd = _pipecmd (p->path, p->args, &p->efd, &p->pid)) < 0) { err ("%p: exec cmd %s failed for host %S\n", path, target); pipecmd_destroy (p); return (NULL); } return (p); } int pipecmd_stdoutfd (pipecmd_t p) { if (p == NULL) return (-1); return (p->fd); } int pipecmd_stderrfd (pipecmd_t p) { if (p == NULL) return (-1); return (p->efd); } int pipecmd_signal (pipecmd_t p, int signo) { char *cmd; if (p == NULL) return (-1); cmd = xbasename (p->path); err ("sending signal %d to %s [%s] pid %d\n", signo, p->target, cmd, p->pid); return (kill (p->pid, signo)); } int pipecmd_wait (pipecmd_t p, int *pstatus) { int status = 0; if (p == NULL) return (-1); if (waitpid (p->pid, &status, 0) < 0) err ("%p: %S: %s pid %ld: waitpid: %m\n", p->target, xbasename (p->path), p->pid); if (status != 0) { if (WIFEXITED (status)) err ("%p: %S: %s exited with exit code %d\n", p->target, xbasename (p->path), WEXITSTATUS (status)); else if (WIFSIGNALED (status)) err ("%p: %S: %s killed by signal %d\n", p->target, xbasename (p->path), WTERMSIG (status)); else err ("%p: %S: %s exited with nonzero status 0x%04x\n", p->target, xbasename (p->path), status); } if (pstatus) *pstatus = status; return (0); } const char * pipecmd_target (pipecmd_t p) { return (p->target); } static void closeall (int fd) { #ifdef __linux struct dirent *d; DIR *dir; dir = opendir("/proc/self/fd"); if (dir) { int dfd = dirfd(dir); while ((d = readdir(dir))) { char * end; unsigned int ufd; int cfd; if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; errno = 0; ufd = strtoul(d->d_name, &end, 10); if (errno || end == d->d_name || !end || *end) continue; cfd = (int) ufd; if (cfd == dfd || cfd < fd) continue; close(cfd); } closedir(dir); return; } #endif int fdlimit = sysconf (_SC_OPEN_MAX); while (fd < fdlimit) close (fd++); return; } static int _pipecmd (char *path, char *args[], int *fd2p, pid_t *ppid) { int sp[2], esp[2]; /* * Get socketpair for stdin/out */ if (socketpair (AF_UNIX, SOCK_STREAM, 0, sp) < 0) { err ("%p: pipecmd: socketpair: %m\n"); return (-1); } if (fd2p && socketpair (AF_UNIX, SOCK_STREAM, 0, esp) < 0) { err ("%p: pipecmd: socketpair: %m\n"); return (-1); } if ((*ppid = fork ()) < 0) { err ("%p: pipecmd: fork: %m\n"); return (-1); } if (*ppid == 0) { /* * Child. We use sp[1] for stdin/out, and close sp[0] */ (void) close (sp[0]); if ((dup2 (sp[1], 0) < 0) || (dup2 (0, 1) < 0)) { err ("%p: pipecmd (in child): dup2: %m"); _exit (255); } /* * Dup seperate stderr socketpair if fd2p was passed in. * Otherwise dup stdin/out onto stderr. */ if (dup2 ((fd2p ? esp[1] : 0), 2) < 0) { err ("%p: pipecmd (in child): dup2: %m"); _exit (255); } if (fd2p) (void) close (esp[0]); /* Try to close all stray file descriptors before * invocation of cmd to ensure that cmd stdin is closed * when pdsh/pdcp close their end of the socketpair. */ closeall (3); setsid (); execvp (path, args); err ("%p: execvp %s failed: %m\n", path); _exit (255); } /* * Parent continues */ (void) close (sp[1]); if (fd2p) { (void) close (esp[1]); *fd2p = esp[0]; } return (sp[0]); } /* * vi: ts=4 sw=4 expandtab */ pdsh-2.36/src/common/split.c0000664€^–Á €^–Á 0000001035115131211226024064 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include #endif #include #include #include #include "xmalloc.h" #include "split.h" /* * Helper function for list_split(). Extract tokens from str. * Return a pointer to the next token; at the same time, advance * *str to point to the next separator. * sep (IN) string containing list of separator characters * str (IN) double-pointer to string containing tokens and separators * RETURN next token */ static char *_next_tok(char *sep, char **str) { char *tok; int level = 0; /* push str past any leading separators */ while (**str != '\0' && strchr(sep, **str) != NULL) (*str)++; if (**str == '\0') return NULL; /* assign token pointer */ tok = *str; /* push str past token and leave pointing to first separator, ignoring separators between any '[]' */ while (**str != '\0' && (level != 0 || strchr(sep, **str) == NULL)) { if (**str == '[') level++; else if (**str == ']') level--; (*str)++; } /* nullify consecutive separators and push str beyond them */ while (**str != '\0' && strchr(sep, **str) != NULL) *(*str)++ = '\0'; return tok; } static void free_f (char *str) { Free ((void **) &str); } /* * Given a list of separators and a string, generate a list * sep (IN) string containing separater characters * str (IN) string containing tokens and separators * RETURN new list containing all tokens */ List list_split(char *sep, char *str) { List new = list_create((ListDelF) free_f); char *tok; if (sep == NULL) sep = " \t"; while ((tok = _next_tok(sep, &str)) != NULL) { if (strlen(tok) > 0) list_append(new, Strdup(tok)); } return new; } List list_split_append (List l, char *sep, char *str) { char *tok; if (l == NULL) return (list_split (sep, str)); if (sep == NULL) sep = " \t"; while ((tok = _next_tok(sep, &str)) != NULL) { if (strlen(tok) > 0) list_append(l, Strdup(tok)); } return l; } int list_join (char *result, size_t len, const char *sep, List l) { char *str = NULL; int n = 0; int truncated = 0; ListIterator i; memset (result, 0, len); if (list_count(l) == 0) return (0); i = list_iterator_create(l); while ((str = list_next(i))) { int count; if (!truncated) { count = snprintf(result + n, len - n, "%s%s", str, sep); if ((count >= (len - n)) || (count < 0)) truncated = 1; else n += count; } else n += strlen (str) + strlen (sep); } list_iterator_destroy(i); if (truncated) result [len - 1] = '\0'; else { /* * Delete final separator */ result[strlen(result) - strlen(sep)] = '\0'; } return (n); } /* vi: ts=4 sw=4 expandtab */ pdsh-2.36/src/common/fd.c0000664€^–Á €^–Á 0000001325315131211226023326 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id$ ***************************************************************************** * This file is part of the Munge Uid 'N' Gid Emporium (MUNGE). * For details, see . * UCRL-CODE-2003-???. * * Copyright (C) 2001-2003 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA 02111-1307 USA. ***************************************************************************** * Refer to "fd.h" for documentation on public functions. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include "fd.h" static int _fd_get_lock (int fd, int cmd, int type); static pid_t _fd_test_lock (int fd, int type); int fd_set_close_on_exec (int fd) { assert (fd >= 0); if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) return (-1); return (0); } int fd_set_nonblocking (int fd) { int fval; assert (fd >= 0); if ((fval = fcntl (fd, F_GETFL, 0)) < 0) return (-1); if (fcntl (fd, F_SETFL, fval | O_NONBLOCK) < 0) return (-1); return (0); } int fd_get_read_lock (int fd) { return (_fd_get_lock (fd, F_SETLK, F_RDLCK)); } int fd_get_readw_lock (int fd) { return (_fd_get_lock (fd, F_SETLKW, F_RDLCK)); } int fd_get_write_lock (int fd) { return (_fd_get_lock (fd, F_SETLK, F_WRLCK)); } int fd_get_writew_lock (int fd) { return (_fd_get_lock (fd, F_SETLKW, F_WRLCK)); } int fd_release_lock (int fd) { return (_fd_get_lock (fd, F_SETLK, F_UNLCK)); } pid_t fd_is_read_lock_blocked (int fd) { return (_fd_test_lock (fd, F_RDLCK)); } pid_t fd_is_write_lock_blocked (int fd) { return (_fd_test_lock (fd, F_WRLCK)); } static int _fd_get_lock (int fd, int cmd, int type) { struct flock lock; assert (fd >= 0); lock.l_type = type; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; return (fcntl (fd, cmd, &lock)); } static pid_t _fd_test_lock (int fd, int type) { struct flock lock; assert (fd >= 0); lock.l_type = type; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl (fd, F_GETLK, &lock) < 0) return (-1); if (lock.l_type == F_UNLCK) return (0); return (lock.l_pid); } ssize_t fd_read_n (int fd, void *buf, size_t n) { size_t nleft; ssize_t nread; unsigned char *p; p = buf; nleft = n; while (nleft > 0) { if ((nread = read (fd, p, nleft)) < 0) { if (errno == EINTR) continue; else return (-1); } else if (nread == 0) { /* EOF */ break; } nleft -= nread; p += nread; } return (n - nleft); } ssize_t fd_write_n (int fd, void *buf, size_t n) { size_t nleft; ssize_t nwritten; unsigned char *p; p = buf; nleft = n; while (nleft > 0) { if ((nwritten = write (fd, p, nleft)) < 0) { if (errno == EINTR) continue; else return (-1); } nleft -= nwritten; p += nwritten; } return (n); } ssize_t fd_read_line (int fd, void *buf, size_t maxlen) { ssize_t n, rc; unsigned char c, *p; n = 0; p = buf; while (n < (ssize_t) maxlen - 1) { /* reserve space for NUL-termination */ if ((rc = read (fd, &c, 1)) == 1) { n++; *p++ = c; if (c == '\n') break; /* store newline, like fgets() */ } else if (rc == 0) { if (n == 0) /* EOF, no data read */ return (0); else /* EOF, some data read */ break; } else { if (errno == EINTR) continue; return (-1); } } *p = '\0'; /* NUL-terminate, like fgets() */ return (n); } /* * Following added by Mike Haskell */ ssize_t fd_null_read_n (int fd, void *buf, size_t n) { unsigned char *mp; size_t nleft; ssize_t nread; unsigned char *p; unsigned char *q; q = p = (unsigned char *)buf; nleft = n; while (nleft > 0) { if ((nread = read (fd, p, nleft)) < 0) { if (errno == EINTR) continue; else return (-1); } else if (nread == 0) { /* EOF */ break; } nleft -= nread; p += nread; mp = (unsigned char *) memchr(q, '\0', (n - nleft)); if (mp <= &q[ (n - nleft - 1)]) { if (mp != NULL) break; } } return (n - nleft); } pdsh-2.36/src/common/fd.h0000664€^–Á €^–Á 0000001005215131211226023325 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id$ ***************************************************************************** * This file is part of the Munge Uid 'N' Gid Emporium (MUNGE). * For details, see . * UCRL-CODE-2003-???. * * Copyright (C) 2001-2003 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #ifndef FD_H #define FD_H #if HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #include #include int fd_set_close_on_exec (int fd); /* * Sets the file descriptor [fd] to be closed on exec(). * Returns 0 on success, or -1 on error. */ int fd_set_nonblocking (int fd); /* * Sets the file descriptor [fd] for non-blocking I/O. * Returns 0 on success, or -1 on error. */ int fd_get_read_lock (int fd); /* * Obtain a read lock on the file specified by [fd]. * Returns 0 on success, or -1 if prevented from obtaining the lock. */ int fd_get_readw_lock (int fd); /* * Obtain a read lock on the file specified by [fd], * blocking until one becomes available. * Returns 0 on success, or -1 on error. */ int fd_get_write_lock (int fd); /* * Obtain a write lock on the file specified by [fd]. * Returns 0 on success, or -1 if prevented from obtaining the lock. */ int fd_get_writew_lock (int fd); /* * Obtain a write lock on the file specified by [fd], * blocking until one becomes available. * Returns 0 on success, or -1 on error. */ int fd_release_lock (int fd); /* * Release a lock held on the file specified by [fd]. * Returns 0 on success, or -1 on error. */ pid_t fd_is_read_lock_blocked (int fd); /* * Checks to see if a lock exists on [fd] that would block a request for a * read-lock (ie, if a write-lock is already being held on the file). * Returns the pid of the process holding the lock, 0 if no lock exists, * or -1 on error. */ pid_t fd_is_write_lock_blocked (int fd); /* * Checks to see if a lock exists on [fd] that would block a request for a * write-lock (ie, if any lock is already being held on the file). * Returns the pid of the process holding the lock, 0 if no lock exists, * or -1 on error. */ ssize_t fd_read_n (int fd, void *buf, size_t n); /* * Reads up to [n] bytes from [fd] into [buf]. * Returns the number of bytes read, 0 on EOF, or -1 on error. */ ssize_t fd_write_n (int fd, void *buf, size_t n); /* * Writes [n] bytes from [buf] to [fd]. * Returns the number of bytes written, or -1 on error. */ ssize_t fd_read_line (int fd, void *buf, size_t maxlen); /* * Reads at most [maxlen-1] bytes up to a newline from [fd] into [buf]. * The [buf] is guaranteed to be NUL-terminated and will contain the * newline if it is encountered within [maxlen-1] bytes. * Returns the number of bytes read, 0 on EOF, or -1 on error. */ ssize_t fd_null_read_n (int fd, void *buf, size_t maxlen); /* * Reads up to [n] bytes from [fd] into [buf]. * Returns the number of bytes read, 0 on EOF, or -1 on error. * Differs from fd_read_n() in that it checks for the presence * a null along the partial read and breaks out if it does. * Added by Mike Haskell */ #endif /* !FD_H */ pdsh-2.36/src/common/xmalloc.h0000664€^–Á €^–Á 0000000312615131211226024377 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _XMALLOC_INCLUDED #define _XMALLOC_INCLUDED #include void *Malloc(size_t); void Realloc(void **, size_t); void Free(void **); int Size(void *); char *Strdup(const char *); #define XMALLOC_MAGIC 0x42 #endif /* _XMALLOC_INCLUDED */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/hostlist.h0000664€^–Á €^–Á 0000002725615131211226024623 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $LSDId: commit c08d251f3cc9b1a5b69a268f952d64f990366835 $ ***************************************************************************** * Copyright (C) 2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona * UCRL-CODE-2002-040. * * This file is part of SLURM, a resource management program. * For details, see . * * SLURM is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with SLURM; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _HOSTLIST_H #define _HOSTLIST_H #include /* Notes: * * If WITH_LSD_FATAL_ERROR_FUNC is defined, the linker will expect to * find and external lsd_fatal_error(file,line,mesg) function. By default, * lsd_fatal_error(file,line,mesg) is a macro definition that outputs an * error message to stderr. This macro may be redefined to invoke another * routine instead. e.g.: * * #define lsd_fatal_error(file,line,mesg) \ * error("%s:%s %s\n",file,line,mesg); * * If WITH_LSD_NOMEM_ERROR_FUNC is defined, the linker will expect to * find an external lsd_nomem_error(file,line,mesg) function. By default, * lsd_nomem_error(file,line,mesg) is a macro definition that returns NULL. * This macro may be redefined to invoke another routine instead. * * If WITH_PTHREADS is defined, these routines will be thread-safe. * */ /* The hostlist opaque data type * * A hostlist is a list of hostnames optimized for a prefixXXXX style * naming convention, where XXXX is a decimal, numeric suffix. */ typedef struct hostlist * hostlist_t; /* A hostset is a special case of a hostlist. It: * * 1. never contains duplicates * 2. is always sorted * (Note: sort occurs first on alphanumeric prefix -- where prefix * matches, numeric suffixes will be sorted *by value*) */ typedef struct hostset * hostset_t; /* The hostlist iterator type (may be used with a hostset as well) * used for non-destructive access to hostlist members. * */ typedef struct hostlist_iterator * hostlist_iterator_t; /* ----[ hostlist_t functions: ]---- */ /* ----[ hostlist creation and destruction ]---- */ /* * hostlist_create(): * * Create a new hostlist from a string representation. * * The string representation (str) may contain one or more hostnames or * bracketed hostlists separated by either `,' or whitespace. A bracketed * hostlist is denoted by a common prefix followed by a list of numeric * ranges contained within brackets: e.g. "tux[0-5,12,20-25]" * * Note: if this module is compiled with WANT_RECKLESS_HOSTRANGE_EXPANSION * defined, a much more loose interpretation of host ranges is used. * Reckless hostrange expansion allows all of the following (in addition to * bracketed hostlists): * * o tux0-5,tux12,tux20-25 * o tux0-tux5,tux12,tux20-tux25 * o tux0-5,12,20-25 * * If str is NULL, and empty hostlist is created and returned. * * If the create fails, hostlist_create() returns NULL. * * The returned hostlist must be freed with hostlist_destroy() * */ hostlist_t hostlist_create(const char *hostlist); /* hostlist_copy(): * * Allocate a copy of a hostlist object. Returned hostlist must be freed * with hostlist_destroy. */ hostlist_t hostlist_copy(const hostlist_t hl); /* hostlist_destroy(): * * Destroy a hostlist object. Frees all memory allocated to the hostlist. */ void hostlist_destroy(hostlist_t hl); /* ----[ hostlist list operations ]---- */ /* hostlist_push(): * * push a string representation of hostnames onto a hostlist. * * The hosts argument may take the same form as in hostlist_create() * * Returns the number of hostnames inserted into the list, * or 0 on failure. */ int hostlist_push(hostlist_t hl, const char *hosts); /* hostlist_push_host(): * * Push a single host onto the hostlist hl. * This function is more efficient than hostlist_push() for a single * hostname, since the argument does not need to be checked for ranges. * * return value is 1 for success, 0 for failure. */ int hostlist_push_host(hostlist_t hl, const char *host); /* hostlist_push_list(): * * Push a hostlist (hl2) onto another list (hl1) * * Returns 1 for success, 0 for failure. * */ int hostlist_push_list(hostlist_t hl1, hostlist_t hl2); /* hostlist_pop(): * * Returns the string representation of the last host pushed onto the list * or NULL if hostlist is empty or there was an error allocating memory. * The host is removed from the hostlist. * * Note: Caller is responsible for freeing the returned memory. */ char * hostlist_pop(hostlist_t hl); char * hostlist_nth(hostlist_t hl, int n); /* hostlist_shift(): * * Returns the string representation of the first host in the hostlist * or NULL if the hostlist is empty or there was an error allocating memory. * The host is removed from the hostlist. * * Note: Caller is responsible for freeing the returned memory. */ char * hostlist_shift(hostlist_t hl); /* hostlist_pop_range(): * * Pop the last bracketed list of hosts of the hostlist hl. * Returns the string representation in bracketed list form. * All hosts associated with the returned list are removed * from hl. * * Caller is responsible for freeing returned memory */ char * hostlist_pop_range(hostlist_t hl); /* hostlist_shift_range(): * * Shift the first bracketed hostlist (improperly: range) off the * hostlist hl. Returns the string representation in bracketed list * form. All hosts associated with the list are removed from the * hostlist. * * Caller is responsible for freeing returned memory. */ char * hostlist_shift_range(hostlist_t hl); /* hostlist_find(): * * Searches hostlist hl for the first host matching hostname * and returns position in list if found. * * Returns -1 if host is not found. * */ int hostlist_find(hostlist_t hl, const char *hostname); /* hostlist_delete(): * * Deletes all hosts in the list represented by `hosts' * * Returns the number of hosts successfully deleted */ int hostlist_delete(hostlist_t hl, const char *hosts); /* hostlist_delete_host(): * * Deletes the first host that matches `hostname' from the hostlist hl. * Note: "hostname" argument cannot contain a range of hosts * (see hostlist_delete() for this functionality.) * * Returns 1 if successful, 0 if hostname is not found in list. */ int hostlist_delete_host(hostlist_t hl, const char *hostname); /* hostlist_delete_nth(): * * Deletes the host from position n in the hostlist. * * Returns 1 if successful 0 on error. * */ int hostlist_delete_nth(hostlist_t hl, int n); /* hostlist_count(): * * Return the number of hosts in hostlist hl. */ int hostlist_count(hostlist_t hl); /* hostlist_is_empty(): return true if hostlist is empty. */ #define hostlist_is_empty(__hl) ( hostlist_count(__hl) == 0 ) /* ----[ Other hostlist operations ]---- */ /* hostlist_sort(): * * Sort the hostlist hl. * */ void hostlist_sort(hostlist_t hl); /* hostlist_uniq(): * * Sort the hostlist hl and remove duplicate entries. * */ void hostlist_uniq(hostlist_t hl); /* ----[ hostlist print functions ]---- */ /* hostlist_ranged_string(): * * Write the string representation of the hostlist hl into buf, * writing at most n chars. Returns the number of bytes written, * or -1 if truncation occurred. * * The result will be NULL terminated. * * hostlist_ranged_string() will write a bracketed hostlist representation * where possible. */ ssize_t hostlist_ranged_string(hostlist_t hl, size_t n, char *buf); ssize_t hostset_ranged_string(hostset_t hs, size_t n, char *buf); /* hostlist_deranged_string(): * * Writes the string representation of the hostlist hl into buf, * writing at most n chars. Returns the number of bytes written, * or -1 if truncation occurred. * * hostlist_deranged_string() will not attempt to write a bracketed * hostlist representation. Every hostname will be explicitly written. */ ssize_t hostlist_deranged_string(hostlist_t hl, size_t n, char *buf); ssize_t hostset_deranged_string(hostset_t hs, size_t n, char *buf); /* ----[ hostlist utility functions ]---- */ /* hostlist_nranges(): * * Return the number of ranges currently held in hostlist hl. */ int hostlist_nranges(hostlist_t hl); /* ----[ hostlist iterator functions ]---- */ /* hostlist_iterator_create(): * * Creates and returns a hostlist iterator used for non destructive * access to a hostlist or hostset. Returns NULL on failure. */ hostlist_iterator_t hostlist_iterator_create(hostlist_t hl); /* hostset_iterator_create(): * * Same as hostlist_iterator_create(), but creates a hostlist_iterator * from a hostset. */ hostlist_iterator_t hostset_iterator_create(hostset_t set); /* hostlist_iterator_destroy(): * * Destroys a hostlist iterator. */ void hostlist_iterator_destroy(hostlist_iterator_t i); /* hostlist_iterator_reset(): * * Reset an iterator to the beginning of the list. */ void hostlist_iterator_reset(hostlist_iterator_t i); /* hostlist_next(): * * Returns a pointer to the next hostname on the hostlist * or NULL at the end of the list * * The caller is responsible for freeing the returned memory. */ char * hostlist_next(hostlist_iterator_t i); /* hostlist_next_range(): * * Returns the next bracketed hostlist or NULL if the iterator i is * at the end of the list. * * The caller is responsible for freeing the returned memory. * */ char * hostlist_next_range(hostlist_iterator_t i); /* hostlist_remove(): * Removes the last host returned by hostlist iterator i * * Returns 1 for success, 0 for failure. */ int hostlist_remove(hostlist_iterator_t i); /* ----[ hostset operations ]---- */ /* hostset_create(): * * Create a new hostset object from a string representation of a list of * hosts. See hostlist_create() for valid hostlist forms. */ hostset_t hostset_create(const char *hostlist); /* hostset_copy(): * * Copy a hostset object. Returned set must be freed with hostset_destroy(). */ hostset_t hostset_copy(hostset_t set); /* hostset_destroy(): */ void hostset_destroy(hostset_t set); /* hostset_insert(): * Add a host or list of hosts into hostset "set." * * Returns number of hosts successfully added to "set" * (insertion of a duplicate is not considered successful) */ int hostset_insert(hostset_t set, const char *hosts); /* hostset_delete(): * Delete a host or list of hosts from hostset "set." * Returns number of hosts deleted from set. */ int hostset_delete(hostset_t set, const char *hosts); /* hostset_within(): * Return 1 if all hosts specified by "hosts" are within the hostset "set" * Retrun 0 if every host in "hosts" is not in the hostset "set" */ int hostset_within(hostset_t set, const char *hosts); /* hostset_shift(): * hostset equivalent to hostlist_shift() */ char * hostset_shift(hostset_t set); /* hostset_shift_range(): * hostset eqivalent to hostlist_shift_range() */ char * hostset_shift_range(hostset_t set); /* hostset_count(): * Count the number of hosts currently in hostset */ int hostset_count(hostset_t set); #endif /* !_HOSTLIST_H */ pdsh-2.36/src/common/err.h0000664€^–Á €^–Á 0000000311615131211226023527 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _ERR_INCLUDED #define _ERR_INCLUDED #include #include void err_init(char *); void err_no_strip_domain(); void err(char *, ...); void out(char *, ...); void errx(char *, ...); void errf(FILE *, char *, va_list); void err_cleanup(void); #endif /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/xpoll.c0000664€^–Á €^–Á 0000001352115131211226024071 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include #endif #if HAVE_POLL_H #include #else #if HAVE_SYS_POLL_H #include #endif /* HAVE_SYS_POLL_H */ #endif /* HAVE_POLL_H */ #include #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include "xpoll.h" #include "xmalloc.h" #if HAVE_POLL static int _poll(struct xpollfd *xfds, unsigned int nfds, int timeout) { int i, rv; struct pollfd *pfds = Malloc(nfds * sizeof(struct pollfd)); for (i = 0; i < nfds; i++) { pfds[i].fd = xfds[i].fd; pfds[i].events = 0; pfds[i].revents = 0; if (xfds[i].events & XPOLLREAD) pfds[i].events |= POLLIN; if (xfds[i].events & XPOLLWRITE) pfds[i].events |= POLLOUT; } if ((rv = poll(pfds, nfds, timeout)) < 0) { Free((void **)&pfds); return -1; } for (i = 0; i < nfds; i++) { if (pfds[i].revents & POLLIN) xfds[i].revents |= XPOLLREAD; if (pfds[i].revents & POLLOUT) xfds[i].revents |= XPOLLWRITE; if (pfds[i].revents & POLLERR || pfds[i].revents & POLLHUP) xfds[i].revents |= XPOLLERR; if (pfds[i].revents & POLLNVAL) xfds[i].revents |= XPOLLINVAL; } Free((void **)&pfds); errno = 0; return rv; } #else /* !HAVE_POLL */ static int _select(struct xpollfd *xfds, unsigned int nfds, int timeout) { int i, maxfd = -1, inval = 0, rv = -1; struct timeval tv; struct timeval *tptr = &tv; fd_set reads, writes; if (timeout < 0) tptr = NULL; else { tv.tv_sec = timeout; tv.tv_usec = 0; } /* setup for select() */ FD_ZERO(&reads); FD_ZERO(&writes); for (i = 0; i < nfds; i++) { if (xfds[i].fd >= FD_SETSIZE || xfds[i].fd < 0) { xfds[i].revents |= XPOLLINVAL; inval++; continue; } if (xfds[i].events & XPOLLREAD) FD_SET(xfds[i].fd, &reads); if (xfds[i].events & XPOLLWRITE) FD_SET(xfds[i].fd, &writes); if (xfds[i].fd > maxfd) maxfd = xfds[i].fd; } while (rv == -1) { if ((rv = select(maxfd + 1, &reads, &writes, NULL, tptr)) < 0) { if (errno != EBADF) return -1; else { /* check for and remove bad fds */ struct timeval ttv = {0, 1}; /* very very short timeout */ fd_set rds, wrs; maxfd = -1; FD_ZERO(&reads); FD_ZERO(&writes); for (i = 0; i < nfds; i++) { if (xfds[i].revents & XPOLLINVAL) continue; FD_ZERO(&rds); FD_ZERO(&wrs); if (xfds[i].events & XPOLLREAD) FD_SET(xfds[i].fd, &rds); if (xfds[i].events & XPOLLWRITE) FD_SET(xfds[i].fd, &wrs); if (select(xfds[i].fd + 1, &rds, &wrs, NULL, &ttv) < 0) { if (errno != EBADF) return -1; else { xfds[i].revents |= XPOLLINVAL; inval++; } } else { /* prepare for next select */ if (xfds[i].events & XPOLLREAD) FD_SET(xfds[i].fd, &reads); if (xfds[i].events & XPOLLWRITE) FD_SET(xfds[i].fd, &writes); if (xfds[i].fd > maxfd) maxfd = xfds[i].fd; } } } } } for (i = 0; i < nfds; i++) { /* protect segfault prone FD_ISSET */ if (xfds[i].revents & XPOLLINVAL) continue; if (FD_ISSET(xfds[i].fd, &reads)) xfds[i].revents |= XPOLLREAD; if (FD_ISSET(xfds[i].fd, &writes)) xfds[i].revents |= XPOLLWRITE; } errno = 0; return (rv + inval); } #endif /* HAVE_POLL */ int xpoll(struct xpollfd *xfds, int nfds, int timeout) { int i; errno = 0; if (xfds == NULL || nfds <= 0) { errno = EINVAL; return -1; } for (i = 0; i < nfds; i++) { xfds[i].revents = 0; } #if HAVE_POLL return _poll(xfds, nfds, timeout); #else return _select(xfds, nfds, timeout); #endif } /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/xmalloc.c0000664€^–Á €^–Á 0000000774315131211226024403 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include /* for INT_MAX */ #include #include "xmalloc.h" #if HAVE_UNSAFE_MALLOC #include static pthread_mutex_t malloc_lock = PTHREAD_MUTEX_INITIALIZER; #define MALLOC_LOCK() pthread_mutex_lock(&malloc_lock) #define MALLOC_UNLOCK() pthread_mutex_unlock(&malloc_lock) #else #define MALLOC_LOCK() #define MALLOC_UNLOCK() #endif /* * "Safe" version of malloc(). * size (IN) number of bytes to malloc * RETURN pointer to allocate heap space */ void *Malloc(size_t size) { void *new; int *p; assert(size > 0 && size <= INT_MAX); MALLOC_LOCK(); p = (int *) malloc(size + 2 * sizeof(int)); MALLOC_UNLOCK(); if (!p) { fprintf(stderr, "Malloc(%ld) failed\n", (long) size); exit(1); } p[0] = XMALLOC_MAGIC; /* add "secret" magic cookie */ p[1] = size; /* store size in buffer */ new = &p[2]; memset(new, 0, size); return new; } /* * "Safe" version of realloc(). Args are different: pass in a pointer to * the object to be realloced instead of the object itself. * item (IN/OUT) double-pointer to allocated space * newsize (IN) requested size */ void Realloc(void **item, size_t newsize) { int *p = (int *) *item - 2; assert(*item != NULL); assert(newsize > 0 && newsize <= INT_MAX); assert(p[0] == XMALLOC_MAGIC); /* magic cookie still there? */ MALLOC_LOCK(); p = (int *) realloc(p, newsize + 2 * sizeof(int)); MALLOC_UNLOCK(); if (!p) { fprintf(stderr, "Realloc(%ld) failed\n", (long) newsize); exit(1); } assert(p[0] == XMALLOC_MAGIC); p[1] = newsize; *item = &p[2]; } /* * Duplicate a string. * str (IN) string to duplicate * RETURN copy of string */ char *Strdup(const char *str) { char *result; if (str == NULL) return NULL; else result = Malloc(strlen(str) + 1); return strcpy(result, str); } /* * Return the size of a buffer. * item (IN) pointer to allocated space */ int Size(void *item) { int *p = (int *) item - 2; assert(item != NULL); assert(p[0] == XMALLOC_MAGIC); return p[1]; } /* * Free which takes a pointer to object to free, which it turns into a null * object. * item (IN/OUT) double-pointer to allocated space */ void Free(void **item) { int *p = (int *) *item - 2; if (*item != NULL) { assert(p[0] == XMALLOC_MAGIC); /* magic cookie still there? */ p[0] = 0; MALLOC_LOCK(); free(p); MALLOC_UNLOCK(); *item = NULL; } } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/list.h0000664€^–Á €^–Á 0000002201615131211226023712 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id$ ***************************************************************************** * $LSDId: list.h,v 1.14 2002/12/11 19:00:36 dun Exp $ ***************************************************************************** * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * * This file is from LSD-Tools, the LLNL Software Development Toolbox. * * LSD-Tools is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * LSD-Tools is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with LSD-Tools; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #ifndef LSD_LIST_H #define LSD_LIST_H /*********** * Notes * ***********/ /* * If NDEBUG is not defined, internal debug code will be enabled. This is * intended for development use only and production code should define NDEBUG. * * If WITH_LSD_FATAL_ERROR_FUNC is defined, the linker will expect to * find an external lsd_fatal_error(file,line,mesg) function. By default, * lsd_fatal_error(file,line,mesg) is a macro definition that outputs an * error message to stderr. This macro may be redefined to invoke another * routine instead. * * If WITH_LSD_NOMEM_ERROR_FUNC is defined, the linker will expect to * find an external lsd_nomem_error(file,line,mesg) function. By default, * lsd_nomem_error(file,line,mesg) is a macro definition that returns NULL. * This macro may be redefined to invoke another routine instead. * * If WITH_PTHREADS is defined, these routines will be thread-safe. */ /**************** * Data Types * ****************/ typedef struct list * List; /* * List opaque data type. */ typedef struct listIterator * ListIterator; /* * List Iterator opaque data type. */ typedef void (*ListDelF) (void *x); /* * Function prototype to deallocate data stored in a list. * This function is responsible for freeing all memory associated * with an item, including all subordinate items (if applicable). */ typedef int (*ListCmpF) (void *x, void *y); /* * Function prototype for comparing two items in a list. * Returns less-than-zero if (xy). */ typedef int (*ListFindF) (void *x, void *key); /* * Function prototype for matching items in a list. * Returns non-zero if (x==key); o/w returns zero. */ typedef int (*ListForF) (void *x, void *arg); /* * Function prototype for operating on each item in a list. * Returns less-than-zero on error. */ /******************************* * General-Purpose Functions * *******************************/ List list_create (ListDelF f); /* * Creates and returns a new empty list, or lsd_nomem_error() on failure. * The deletion function [f] is used to deallocate memory used by items * in the list; if this is NULL, memory associated with these items * will not be freed when the list is destroyed. * Note: Abandoning a list without calling list_destroy() will result * in a memory leak. */ void list_destroy (List l); /* * Destroys list [l], freeing memory used for list iterators and the * list itself; if a deletion function was specified when the list * was created, it will be called for each item in the list. */ int list_is_empty (List l); /* * Returns non-zero if list [l] is empty; o/w returns zero. */ int list_count (List l); /* * Returns the number of items in list [l]. */ /*************************** * List Access Functions * ***************************/ void * list_append (List l, void *x); /* * Inserts data [x] at the end of list [l]. * Returns the data's ptr, or lsd_nomem_error() if insertion failed. */ void * list_prepend (List l, void *x); /* * Inserts data [x] at the beginning of list [l]. * Returns the data's ptr, or lsd_nomem_error() if insertion failed. */ void * list_find_first (List l, ListFindF f, void *key); /* * Traverses list [l] using [f] to match each item with [key]. * Returns a ptr to the first item for which the function [f] * returns non-zero, or NULL if no such item is found. * Note: This function differs from list_find() in that it does not require * a list iterator; it should only be used when all list items are known * to be unique (according to the function [f]). */ int list_delete_all (List l, ListFindF f, void *key); /* * Traverses list [l] using [f] to match each item with [key]. * Removes all items from the list for which the function [f] returns * non-zero; if a deletion function was specified when the list was * created, it will be called to deallocate each item being removed. * Returns a count of the number of items removed from the list. */ int list_for_each (List l, ListForF f, void *arg); /* * For each item in list [l], invokes the function [f] with [arg]. * Returns a count of the number of items on which [f] was invoked. * If [f] returns <0 for a given item, the iteration is aborted and the * function returns the negative of that item's position in the list. */ void list_sort (List l, ListCmpF f); /* * Sorts list [l] into ascending order according to the function [f]. * Note: Sorting a list resets all iterators associated with the list. * Note: The sort algorithm is stable. */ /**************************** * Stack Access Functions * ****************************/ void * list_push (List l, void *x); /* * Pushes data [x] onto the top of stack [l]. * Returns the data's ptr, or lsd_nomem_error() if insertion failed. */ void * list_pop (List l); /* * Pops the data item at the top of the stack [l]. * Returns the data's ptr, or NULL if the stack is empty. */ void * list_peek (List l); /* * Peeks at the data item at the top of the stack (or head of the queue) [l]. * Returns the data's ptr, or NULL if the stack (or queue) is empty. * Note: The item is not removed from the list. */ /**************************** * Queue Access Functions * ****************************/ void * list_enqueue (List l, void *x); /* * Enqueues data [x] at the tail of queue [l]. * Returns the data's ptr, or lsd_nomem_error() if insertion failed. */ void * list_dequeue (List l); /* * Dequeues the data item at the head of the queue [l]. * Returns the data's ptr, or NULL if the queue is empty. */ /***************************** * List Iterator Functions * *****************************/ ListIterator list_iterator_create (List l); /* * Creates and returns a list iterator for non-destructively traversing * list [l], or lsd_nomem_error() on failure. */ void list_iterator_reset (ListIterator i); /* * Resets the list iterator [i] to start traversal at the beginning * of the list. */ void list_iterator_destroy (ListIterator i); /* * Destroys the list iterator [i]; list iterators not explicitly destroyed * in this manner will be destroyed when the list is deallocated via * list_destroy(). */ void * list_next (ListIterator i); /* * Returns a ptr to the next item's data, * or NULL once the end of the list is reached. * Example: i=list_iterator_create(i); while ((x=list_next(i))) {...} */ void * list_insert (ListIterator i, void *x); /* * Inserts data [x] immediately before the last item returned via list * iterator [i]; once the list iterator reaches the end of the list, * insertion is made at the list's end. * Returns the data's ptr, or lsd_nomem_error() if insertion failed. */ void * list_find (ListIterator i, ListFindF f, void *key); /* * Traverses the list from the point of the list iterator [i] * using [f] to match each item with [key]. * Returns a ptr to the next item for which the function [f] * returns non-zero, or NULL once the end of the list is reached. * Example: i=list_iterator_reset(i); while ((x=list_find(i,f,k))) {...} */ void * list_remove (ListIterator i); /* * Removes from the list the last item returned via list iterator [i] * and returns the data's ptr. * Note: The client is responsible for freeing the returned data. */ int list_delete (ListIterator i); /* * Removes from the list the last item returned via list iterator [i]; * if a deletion function was specified when the list was created, * it will be called to deallocate the item being removed. * Returns a count of the number of items removed from the list * (ie, '1' if the item was removed, and '0' otherwise). */ #endif /* !LSD_LIST_H */ pdsh-2.36/src/common/err.c0000664€^–Á €^–Á 0000001337315131211226023530 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Error printing routines with variable arguments. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #if HAVE_UNISTD_H #include /* gethostname */ #endif #include #include /* exit */ #include "xstring.h" #include "xmalloc.h" #include "macros.h" static char *prog = NULL; static char *host = NULL; /* * Optionally leave domain on hostnames with _verr's %S */ static bool keep_host_domain = false; /* * Call this before calling err() or errx(). Sets hostname and program name * for %H, %p, and %P. * str (IN) program name */ void err_init(char *str) { char thishost[MAXHOSTNAMELEN]; char *p; gethostname(thishost, MAXHOSTNAMELEN); if ((p = strchr(thishost, '.')) != NULL) *p = '\0'; host = Strdup(thishost); prog = Strdup(str); } void err_no_strip_domain () { keep_host_domain = true; } /* * Free heap storage allocated by err_init() */ void err_cleanup(void) { Free((void **) &prog); Free((void **) &host); } /* * _verr() is like vfprintf, but handles (only) the following formats: * following formats: * %s string * %S string, but treat as hostname and truncate after dot * %c character * %m string (sys_errlist[errno]) * %d int * %z equivalent to %.3d * %p program name with @host attached * %P program name * %H hostname for this host */ static void _verr(FILE * stream, char *format, va_list ap) { char *buf = NULL; char *q; int percent = 0; char tmpstr[LINEBUFSIZE]; /* Note: snprintf silently truncates if argument exceeds size of tmpstr */ assert(prog != NULL && host != NULL); while (format && *format) { /* iterate thru chars */ if (percent == 1) { percent = 0; if (*format == 's') { /* %s - string */ xstrcat(&buf, va_arg(ap, char *)); } else if (*format == 'S') { /* %S - string, trunc */ snprintf(tmpstr, sizeof(tmpstr), "%s", va_arg(ap, char *)); if ( !isdigit(*tmpstr) && !keep_host_domain && (q = strchr(tmpstr, '.'))) *q = '\0'; xstrcat(&buf, tmpstr); } else if (*format == 'z') { /* %z - same as %.3d */ snprintf(tmpstr, sizeof(tmpstr), "%.3d", va_arg(ap, int)); xstrcat(&buf, tmpstr); } else if (*format == 'c') { /* %c - character */ xstrcatchar(&buf, va_arg(ap, int)); } else if (*format == 'd') { /* %d - integer */ snprintf(tmpstr, sizeof(tmpstr), "%d", va_arg(ap, int)); xstrcat(&buf, tmpstr); } else if (*format == 'm') { /* %m - error code */ xstrerrorcat(&buf); } else if (*format == 'P') { /* %P - prog name */ assert(prog != NULL); xstrcat(&buf, prog); } else if (*format == 'H') { /* %H - this host */ assert(host != NULL); xstrcat(&buf, host); } else if (*format == 'p') { /* %p - prog@host */ assert(prog != NULL); assert(host != NULL); snprintf(tmpstr, sizeof(tmpstr), "%s@%s", prog, host); xstrcat(&buf, tmpstr); } else /* pass thru */ xstrcatchar(&buf, *format); } else if (*format == '%') percent = 1; /* remember % */ else xstrcatchar(&buf, *format); /* pass thru */ format++; } fputs(buf, stream); /* print it */ Free((void **) &buf); /* clean up */ } void err(char *format, ...) { va_list ap; va_start(ap, format); _verr(stderr, format, ap); va_end(ap); } void errx(char *format, ...) { va_list ap; va_start(ap, format); _verr(stderr, format, ap); va_end(ap); exit(1); } void out(char *format, ...) { va_list ap; va_start(ap, format); _verr(stdout, format, ap); va_end(ap); } void errf(FILE *stream, char *format, va_list ap) { if (!stream) return; _verr(stream, format, ap); } void lsd_fatal_error(char *file, int line, char *mesg) { errx ("%p: %s:%d: %s\n", file, line, mesg); } void lsd_nomem_error(char *file, int line, char *mesg) { errx ("%p: %s:%d: %s: Out of memory\n", file, line, mesg); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/pipecmd.h0000664€^–Á €^–Á 0000000462015131211226024361 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2007 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _HAVE_PIPECMD_H #define _HAVE_PIPECMD_H typedef struct pipe_info_struct * pipecmd_t; /* * Run a command with stdout/in and stderr connected to * pipes available to pdsh. * Args are re-formatted replacing %h, %u, and %n with * remote "host", user, and "rank" respectively. * * Returns NULL pipecmd object on failure. */ pipecmd_t pipecmd (const char *path, const char **args, const char *target, const char *user, int rank); /* * Destroy pipecmd object - freeing associated memory */ void pipecmd_destroy (pipecmd_t p); /* * Return file descriptors for pipecmd object */ int pipecmd_stdoutfd (pipecmd_t p); int pipecmd_stderrfd (pipecmd_t p); /* * Send signal [signo] to child process associtated with given * pipecmd object */ int pipecmd_signal (pipecmd_t p, int signo); /* * Wait for and reap pipecmd child process. Returns exit status * in status if it is non-NULL */ int pipecmd_wait (pipecmd_t p, int *status); /* * Return target name of pipecmd process (i.e. target host) */ const char * pipecmd_target (pipecmd_t p); #endif /* !_HAVE_PIPECMD_H */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/hostlist.c0000664€^–Á €^–Á 0000021261115131211226024605 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $LSDId: commit c08d251f3cc9b1a5b69a268f952d64f990366835 $ ***************************************************************************** * Copyright (C) 2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona * UCRL-CODE-2002-040. * * This file is part of SLURM, a resource management program. * For details, see . * * SLURM is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with SLURM; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" # if HAVE_STRING_H # include # endif # if HAVE_PTHREAD_H # include # endif #else /* !HAVE_CONFIG_H */ # include # include #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include #include #include "hostlist.h" /* * lsd_fatal_error : fatal error macro */ #ifdef WITH_LSD_FATAL_ERROR_FUNC # undef lsd_fatal_error extern void lsd_fatal_error(char *file, int line, char *mesg); #else /* !WITH_LSD_FATAL_ERROR_FUNC */ # ifndef lsd_fatal_error # define lsd_fatal_error(file, line, mesg) \ do { \ fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \ file, line, mesg, strerror(errno)); \ } while (0) # endif /* !lsd_fatal_error */ #endif /* !WITH_LSD_FATAL_ERROR_FUNC */ /* * lsd_nomem_error */ #ifdef WITH_LSD_NOMEM_ERROR_FUNC # undef lsd_nomem_error extern void * lsd_nomem_error(char *file, int line, char *mesg); #else /* !WITH_LSD_NOMEM_ERROR_FUNC */ # ifndef lsd_nomem_error # define lsd_nomem_error(file, line, mesg) (NULL) # endif /* !lsd_nomem_error */ #endif /* !WITH_LSD_NOMEM_ERROR_FUNC */ /* * OOM helper function * Automatically call lsd_nomem_error with appropriate args * and set errno to ENOMEM */ #define out_of_memory(mesg) \ do { \ errno = ENOMEM; \ return(lsd_nomem_error(__FILE__, __LINE__, mesg)); \ } while (0) /* * Some constants and tunables: */ /* number of elements to allocate when extending the hostlist array */ #define HOSTLIST_CHUNK 16 /* max host range: anything larger will be assumed to be an error */ #define MAX_RANGE 16384 /* 16K Hosts */ /* max host suffix value */ #define MAX_HOST_SUFFIX 1<<25 /* max number of ranges that will be processed between brackets */ #define MAX_RANGES 10240 /* 10K Ranges */ /* size of internal hostname buffer (+ some slop), hostnames will probably * be truncated if longer than MAXHOSTNAMELEN */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* max size of internal hostrange buffer */ #define MAXHOSTRANGELEN 1024 /* ----[ Internal Data Structures ]---- */ /* hostname type: A convenience structure used in parsing single hostnames */ struct hostname_components { char *hostname; /* cache of initialized hostname */ char *prefix; /* hostname prefix */ unsigned long num; /* numeric suffix */ /* string representation of numeric suffix * points into `hostname' */ char *suffix; }; typedef struct hostname_components *hostname_t; /* hostrange type: A single prefix with `hi' and `lo' numeric suffix values */ struct hostrange_components { char *prefix; /* alphanumeric prefix: */ /* beginning (lo) and end (hi) of suffix range */ unsigned long lo, hi; /* width of numeric output format * (pad with zeros up to this width) */ int width; /* If singlehost is 1, `lo' and `hi' are invalid */ unsigned singlehost:1; }; typedef struct hostrange_components *hostrange_t; /* The hostlist type: An array based list of hostrange_t's */ struct hostlist { #ifndef NDEBUG #define HOSTLIST_MAGIC 57005 int magic; #endif #if WITH_PTHREADS pthread_mutex_t mutex; #endif /* WITH_PTHREADS */ /* current number of elements available in array */ int size; /* current number of ranges stored in array */ int nranges; /* current number of hosts stored in hostlist */ int nhosts; /* pointer to hostrange array */ hostrange_t *hr; /* list of iterators */ struct hostlist_iterator *ilist; }; /* a hostset is a wrapper around a hostlist */ struct hostset { hostlist_t hl; }; struct hostlist_iterator { #ifndef NDEBUG int magic; #endif /* hostlist we are traversing */ hostlist_t hl; /* current index of iterator in hl->hr[] */ int idx; /* current hostrange object in list hl, i.e. hl->hr[idx] */ hostrange_t hr; /* current depth we've traversed into range hr */ int depth; /* next ptr for lists of iterators */ struct hostlist_iterator *next; }; /* ---- ---- */ /* ------[ static function prototypes ]------ */ static void _error(char *file, int line, char *mesg, ...); static char * _next_tok(char *, char **); static int _zero_padded(unsigned long, int); static int _width_equiv(unsigned long, int *, unsigned long, int *); static int host_prefix_end(const char *); static hostname_t hostname_create(const char *); static void hostname_destroy(hostname_t); static int hostname_suffix_is_valid(hostname_t); static int hostname_suffix_width(hostname_t); static hostrange_t hostrange_new(void); static hostrange_t hostrange_create_single(const char *); static hostrange_t hostrange_create(char *, unsigned long, unsigned long, int); static unsigned long hostrange_count(hostrange_t); static hostrange_t hostrange_copy(hostrange_t); static void hostrange_destroy(hostrange_t); static hostrange_t hostrange_delete_host(hostrange_t, unsigned long); static int hostrange_cmp(hostrange_t, hostrange_t); static int hostrange_prefix_cmp(hostrange_t, hostrange_t); static int hostrange_within_range(hostrange_t, hostrange_t); static int hostrange_width_combine(hostrange_t, hostrange_t); static int hostrange_empty(hostrange_t); static char * hostrange_pop(hostrange_t); static char * hostrange_shift(hostrange_t); static int hostrange_join(hostrange_t, hostrange_t); static hostrange_t hostrange_intersect(hostrange_t, hostrange_t); static int hostrange_hn_within(hostrange_t, hostname_t); static size_t hostrange_to_string(hostrange_t hr, size_t, char *, char *); static size_t hostrange_numstr(hostrange_t, size_t, char *); static hostlist_t hostlist_new(void); static hostlist_t _hostlist_create_bracketed(const char *, char *, char *); static int hostlist_resize(hostlist_t, size_t); static int hostlist_expand(hostlist_t); static int hostlist_push_range(hostlist_t, hostrange_t); static int hostlist_push_hr(hostlist_t, char *, unsigned long, unsigned long, int); static int hostlist_insert_range(hostlist_t, hostrange_t, int); static void hostlist_delete_range(hostlist_t, int n); static void hostlist_coalesce(hostlist_t hl); static void hostlist_collapse(hostlist_t hl); static hostlist_t _hostlist_create(const char *, char *, char *); static void hostlist_shift_iterators(hostlist_t, int, int, int); static int _attempt_range_join(hostlist_t, int); static int _is_bracket_needed(hostlist_t, int); static hostlist_iterator_t hostlist_iterator_new(void); static void _iterator_advance(hostlist_iterator_t); static void _iterator_advance_range(hostlist_iterator_t); static int hostset_find_host(hostset_t, const char *); /* ------[ macros ]------ */ #ifdef WITH_PTHREADS # define mutex_init(mutex) \ do { \ int e = pthread_mutex_init(mutex, NULL); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex init:"); \ abort(); \ } \ } while (0) # define mutex_lock(mutex) \ do { \ int e = pthread_mutex_lock(mutex); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex lock:"); \ abort(); \ } \ } while (0) # define mutex_unlock(mutex) \ do { \ int e = pthread_mutex_unlock(mutex); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex unlock:"); \ abort(); \ } \ } while (0) # define mutex_destroy(mutex) \ do { \ int e = pthread_mutex_destroy(mutex); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex destroy:"); \ abort(); \ } \ } while (0) #else /* !WITH_PTHREADS */ # define mutex_init(mutex) # define mutex_lock(mutex) # define mutex_unlock(mutex) # define mutex_destroy(mutex) #endif /* WITH_PTHREADS */ #define LOCK_HOSTLIST(_hl) \ do { \ assert(_hl != NULL); \ mutex_lock(&(_hl)->mutex); \ assert((_hl)->magic == HOSTLIST_MAGIC); \ } while (0) #define UNLOCK_HOSTLIST(_hl) \ do { \ mutex_unlock(&(_hl)->mutex); \ } while (0) #define seterrno_ret(_errno, _rc) \ do { \ errno = _errno; \ return _rc; \ } while (0) /* ------[ Function Definitions ]------ */ /* ----[ general utility functions ]---- */ /* * Varargs capable error reporting via lsd_fatal_error() */ static void _error(char *file, int line, char *msg, ...) { va_list ap; char buf[1024]; int len = 0; va_start(ap, msg); len = vsnprintf(buf, 1024, msg, ap); if ((len < 0) || (len > 1024)) buf[1023] = '\0'; lsd_fatal_error(file, line, buf); va_end(ap); return; } /* * Helper function for host list string parsing routines * Returns a pointer to the next token; additionally advance *str * to the next separator. * * next_tok was taken directly from pdsh courtesy of Jim Garlick. * (with modifications to support bracketed hostlists, i.e.: * xxx[xx,xx,xx] is a single token) * * _next_tok now handles multiple brackets within the same token, * e.g. node[01-30]-[1-2,6]. */ static char * _next_tok(char *sep, char **str) { char *tok; int level = 0; /* push str past any leading separators */ while (**str != '\0' && strchr(sep, **str)) (*str)++; if (**str == '\0') return NULL; /* assign token ptr */ tok = *str; while ( **str != '\0' && (level != 0 || strchr(sep, **str) == NULL) ) { if ( **str == '[' ) level++; else if ( **str == ']' ) level--; (*str)++; } /* nullify consecutive separators and push str beyond them */ while (**str != '\0' && strchr(sep, **str) != NULL) *(*str)++ = '\0'; return tok; } /* return the number of zeros needed to pad "num" to "width" */ static int _zero_padded(unsigned long num, int width) { int n = 1; while (num /= 10L) n++; return width > n ? width - n : 0; } /* test whether two format `width' parameters are "equivalent" * The width arguments "wn" and "wm" for integers "n" and "m" * are equivalent if: * * o wn == wm OR * * o applying the same format width (either wn or wm) to both of * 'n' and 'm' will not change the zero padding of *either* 'm' nor 'n'. * * If this function returns 1 (or true), the appropriate width value * (either 'wm' or 'wn') will have been adjusted such that both format * widths are equivalent. */ static int _width_equiv(unsigned long n, int *wn, unsigned long m, int *wm) { int npad, nmpad, mpad, mnpad; if (wn == wm) return 1; npad = _zero_padded(n, *wn); nmpad = _zero_padded(n, *wm); mpad = _zero_padded(m, *wm); mnpad = _zero_padded(m, *wn); if (npad != nmpad && mpad != mnpad) return 0; if (npad != nmpad) { if (mpad == mnpad) { *wm = *wn; return 1; } else return 0; } else { /* mpad != mnpad */ if (npad == nmpad) { *wn = *wm; return 1; } else return 0; } /* not reached */ } /* ----[ hostname_t functions ]---- */ /* * return the location of the last char in the hostname prefix */ static int host_prefix_end(const char *hostname) { int idx = strlen(hostname) - 1; while (idx >= 0 && isdigit((char) hostname[idx])) idx--; return idx; } static hostname_t hostname_create_with_suffix (const char *hostname, int idx) { hostname_t hn = NULL; char *p = "\0"; assert(hostname != NULL); if (!(hn = (hostname_t) malloc(sizeof(*hn)))) out_of_memory("hostname create"); if (!(hn->hostname = strdup(hostname))) { free(hn); out_of_memory("hostname create"); } hn->num = 0; hn->prefix = NULL; hn->suffix = NULL; if (idx == strlen(hostname) - 1) { if ((hn->prefix = strdup(hostname)) == NULL) { hostname_destroy(hn); out_of_memory("hostname prefix create"); } return hn; } hn->suffix = hn->hostname + idx + 1; hn->num = strtoul(hn->suffix, &p, 10); if ((*p == '\0') && (hn->num <= MAX_HOST_SUFFIX)) { if (!(hn->prefix = malloc((idx + 2) * sizeof(char)))) { hostname_destroy(hn); out_of_memory("hostname prefix create"); } memcpy(hn->prefix, hostname, idx + 1); hn->prefix[idx + 1] = '\0'; } else { if (!(hn->prefix = strdup(hostname))) { hostname_destroy(hn); out_of_memory("hostname prefix create"); } hn->suffix = NULL; } return hn; } /* * create a hostname_t object from a string hostname */ static hostname_t hostname_create(const char *hostname) { int idx = host_prefix_end (hostname); return hostname_create_with_suffix (hostname, idx); } /* free a hostname object */ static void hostname_destroy(hostname_t hn) { if (hn == NULL) return; hn->suffix = NULL; if (hn->hostname) free(hn->hostname); if (hn->prefix) free(hn->prefix); free(hn); } /* return true if the hostname has a valid numeric suffix */ static int hostname_suffix_is_valid(hostname_t hn) { return hn->suffix != NULL; } /* return the width (in characters) of the numeric part of the hostname */ static int hostname_suffix_width(hostname_t hn) { assert(hn->suffix != NULL); return (int) strlen(hn->suffix); } /* ----[ hostrange_t functions ]---- */ /* allocate a new hostrange object */ static hostrange_t hostrange_new(void) { hostrange_t new = (hostrange_t) malloc(sizeof(*new)); if (!new) out_of_memory("hostrange create"); return new; } /* Create a hostrange_t containing a single host without a valid suffix * hr->prefix will represent the entire hostname. */ static hostrange_t hostrange_create_single(const char *prefix) { hostrange_t new; assert(prefix != NULL); if ((new = hostrange_new()) == NULL) goto error1; if ((new->prefix = strdup(prefix)) == NULL) goto error2; new->singlehost = 1; new->lo = 0L; new->hi = 0L; new->width = 0; return new; error2: free(new); error1: out_of_memory("hostrange create single"); } /* Create a hostrange object with a prefix, hi, lo, and format width */ static hostrange_t hostrange_create(char *prefix, unsigned long lo, unsigned long hi, int width) { hostrange_t new; assert(prefix != NULL); if ((new = hostrange_new()) == NULL) goto error1; if ((new->prefix = strdup(prefix)) == NULL) goto error2; new->lo = lo; new->hi = hi; new->width = width; new->singlehost = 0; return new; error2: free(new); error1: out_of_memory("hostrange create"); } /* Return the number of hosts stored in the hostrange object */ static unsigned long hostrange_count(hostrange_t hr) { assert(hr != NULL); if (hr->singlehost) return 1; else return hr->hi - hr->lo + 1; } /* Copy a hostrange object */ static hostrange_t hostrange_copy(hostrange_t hr) { assert(hr != NULL); if (hr->singlehost) return hostrange_create_single(hr->prefix); else return hostrange_create(hr->prefix, hr->lo, hr->hi, hr->width); } /* free memory allocated by the hostrange object */ static void hostrange_destroy(hostrange_t hr) { if (hr == NULL) return; if (hr->prefix) free(hr->prefix); free(hr); } /* hostrange_delete_host() deletes a specific host from the range. * If the range is split into two, the greater range is returned, * and `hi' of the lesser range is adjusted accordingly. If the * highest or lowest host is deleted from a range, NULL is returned * and the hostrange hr is adjusted properly. */ static hostrange_t hostrange_delete_host(hostrange_t hr, unsigned long n) { hostrange_t new = NULL; assert(hr != NULL); assert(n >= hr->lo && n <= hr->hi); if (n == hr->lo) hr->lo++; else if (n == hr->hi) hr->hi--; else { if (!(new = hostrange_copy(hr))) out_of_memory("hostrange copy"); hr->hi = n - 1; new->lo = n + 1; } return new; } /* hostrange_cmp() is used to sort hostrange objects. It will * sort based on the following (in order): * o result of strcmp on prefixes * o if widths are compatible, then: * sort based on lowest suffix in range * else * sort based on width */ static int hostrange_cmp(hostrange_t h1, hostrange_t h2) { int retval; assert(h1 != NULL); assert(h2 != NULL); if ((retval = hostrange_prefix_cmp(h1, h2)) == 0) retval = hostrange_width_combine(h1, h2) ? h1->lo - h2->lo : h1->width - h2->width; return retval; } /* compare the prefixes of two hostrange objects. * returns: * < 0 if h1 prefix is less than h2 OR h1 == NULL. * * 0 if h1's prefix and h2's prefix match, * UNLESS, either h1 or h2 (NOT both) do not have a valid suffix. * * > 0 if h1's prefix is greater than h2's OR h2 == NULL. */ static int hostrange_prefix_cmp(hostrange_t h1, hostrange_t h2) { int retval; if (h1 == NULL) return 1; if (h2 == NULL) return -1; retval = strcmp(h1->prefix, h2->prefix); return retval == 0 ? h2->singlehost - h1->singlehost : retval; } /* returns true if h1 and h2 would be included in the same bracketed hostlist. * h1 and h2 will be in the same bracketed list iff: * * 1. h1 and h2 have same prefix * 2. neither h1 nor h2 are singlet hosts (i.e. invalid suffix) * * (XXX: Should incompatible widths be placed in the same bracketed list? * There's no good reason not to, except maybe aesthetics) */ static int hostrange_within_range(hostrange_t h1, hostrange_t h2) { if (hostrange_prefix_cmp(h1, h2) == 0) return h1->singlehost || h2->singlehost ? 0 : 1; else return 0; } /* compare two hostrange objects to determine if they are width * compatible, returns: * 1 if widths can safely be combined * 0 if widths cannot be safely combined */ static int hostrange_width_combine(hostrange_t h0, hostrange_t h1) { assert(h0 != NULL); assert(h1 != NULL); return _width_equiv(h0->lo, &h0->width, h1->lo, &h1->width); } /* Return true if hostrange hr contains no hosts, i.e. hi < lo */ static int hostrange_empty(hostrange_t hr) { assert(hr != NULL); return ((hr->hi < hr->lo) || (hr->hi == (unsigned long) -1)); } /* return the string representation of the last host in hostrange hr * and remove that host from the range (i.e. decrement hi if possible) * * Returns NULL if malloc fails OR there are no more hosts left */ static char *hostrange_pop(hostrange_t hr) { size_t size = 0; char *host = NULL; assert(hr != NULL); if (hr->singlehost) { hr->lo++; /* effectively set count == 0 */ host = strdup(hr->prefix); } else if (hostrange_count(hr) > 0) { size = strlen(hr->prefix) + hr->width + 16; if (!(host = (char *) malloc(size * sizeof(char)))) out_of_memory("hostrange pop"); snprintf(host, size, "%s%0*lu", hr->prefix, hr->width, hr->hi--); } return host; } /* Same as hostrange_pop(), but remove host from start of range */ static char *hostrange_shift(hostrange_t hr) { size_t size = 0; char *host = NULL; assert(hr != NULL); if (hr->singlehost) { hr->lo++; if (!(host = strdup(hr->prefix))) out_of_memory("hostrange shift"); } else if (hostrange_count(hr) > 0) { size = strlen(hr->prefix) + hr->width + 16; if (!(host = (char *) malloc(size * sizeof(char)))) out_of_memory("hostrange shift"); snprintf(host, size, "%s%0*lu", hr->prefix, hr->width, hr->lo++); } return host; } /* join two hostrange objects. * * returns: * * -1 if ranges do not overlap (including incompatible zero padding) * 0 if ranges join perfectly * >0 number of hosts that were duplicated in h1 and h2 * * h2 will be coalesced into h1 if rc >= 0 * * it is assumed that h1->lo <= h2->lo, i.e. hr1 <= hr2 * */ static int hostrange_join(hostrange_t h1, hostrange_t h2) { int duplicated = -1; assert(h1 != NULL); assert(h2 != NULL); assert(hostrange_cmp(h1, h2) <= 0); if (hostrange_prefix_cmp(h1, h2) == 0 && hostrange_width_combine(h1, h2)) { if (h1->singlehost && h2->singlehost) { /* matching singlets */ duplicated = 1; } else if (h1->hi == h2->lo - 1) { /* perfect join */ h1->hi = h2->hi; duplicated = 0; } else if (h1->hi >= h2->lo) { /* some duplication */ if (h1->hi < h2->hi) { duplicated = h1->hi - h2->lo + 1; h1->hi = h2->hi; } else duplicated = hostrange_count(h2); } } return duplicated; } /* hostrange intersect returns the intersection (common hosts) * of hostrange objects h1 and h2. If there is no intersection, * NULL is returned. * * It is assumed that h1 <= h2 (i.e. h1->lo <= h2->lo) */ static hostrange_t hostrange_intersect(hostrange_t h1, hostrange_t h2) { hostrange_t new = NULL; assert(h1 != NULL); assert(h2 != NULL); if (h1->singlehost || h2->singlehost) return NULL; assert(hostrange_cmp(h1, h2) <= 0); if ((hostrange_prefix_cmp(h1, h2) == 0) && (h1->hi > h2->lo) && (hostrange_width_combine(h1, h2))) { if (!(new = hostrange_copy(h1))) return NULL; new->lo = h2->lo; new->hi = h2->hi < h1->hi ? h2->hi : h1->hi; } return new; } /* return offset of hn if it is in the hostlist or * -1 if not. */ static int hostrange_hn_within(hostrange_t hr, hostname_t hn) { int len_hr; int len_hn; int width; if (hr->singlehost) { /* * If the current hostrange [hr] is a `singlehost' (no valid * numeric suffix (lo and hi)), then the hostrange [hr] * stores just one host with name == hr->prefix. * * Thus the full hostname in [hn] must match hr->prefix, in * which case we return true. Otherwise, there is no * possibility that [hn] matches [hr]. */ if (strcmp (hn->hostname, hr->prefix) == 0) return 0; else return -1; } /* * Now we know [hr] is not a "singlehost", so hostname * better have a valid numeric suffix, or there is no * way we can match */ if (!hostname_suffix_is_valid (hn)) return -1; len_hn = strlen (hn->prefix); /* * If hostrange and hostname prefixes don't match to at least * the length of the hostname object (which will have the min * possible prefix length), then there is no way the hostname * falls within the range [hr]. */ if (strncmp (hr->prefix, hn->prefix, len_hn) != 0) return -1; /* * Now we know hostrange and hostname prefixes match up to the * length of the hostname prefix. If the hostrange and hostname * prefix lengths do not match (specifically if the hostname prefix * length is less than the hostrange prefix length) and the * hostrange prefix contains trailing digits, then it might be * the case that the hostrange was created by forcing the prefix * to contain digits a la f00[1-2]. So we try adjusting the * hostname with the longer prefix and calling this function * again with the new hostname. (Yes, this is ugly, sorry) */ len_hr = strlen (hr->prefix); width = hostname_suffix_width (hn); if ((len_hn < len_hr) && (width > 1) && (isdigit (hr->prefix [len_hr - 1])) && (hr->prefix [len_hn] == hn->suffix[0]) ) { int rc; /* * Create new hostname object with its prefix offset by one */ hostname_t h = hostname_create_with_suffix (hn->hostname, len_hn); /* * Recursive call :-o */ rc = hostrange_hn_within (hr, h); hostname_destroy (h); return rc; } /* * Finally, check whether [hn], with a valid numeric suffix, * falls within the range of [hr] if [hn] and [hr] prefix are * identical. */ if ((len_hr == len_hn) && (strcmp (hn->prefix, hr->prefix) == 0) && (hn->num <= hr->hi) && (hn->num >= hr->lo)) { int width = hostname_suffix_width (hn); if (!_width_equiv(hr->lo, &hr->width, hn->num, &width)) return -1; return (hn->num - hr->lo); } return -1; } /* copy a string representation of the hostrange hr into buffer buf, * writing at most n chars including NUL termination */ static size_t hostrange_to_string(hostrange_t hr, size_t n, char *buf, char *separator) { unsigned long i; int truncated = 0; int len = 0; char sep = separator == NULL ? ',' : separator[0]; if (n == 0) return 0; if (hr->singlehost) return snprintf(buf, n, "%s", hr->prefix); for (i = hr->lo; i <= hr->hi; i++) { size_t m = (n - len) <= n ? n - len : 0; /* check for < 0 */ int ret = snprintf(buf + len, m, "%s%0*lu", hr->prefix, hr->width, i); if (ret < 0 || ret >= m) { len = n; truncated = 1; break; } len+=ret; buf[len++] = sep; } if (truncated) { buf[n-1] = '\0'; return -1; } else { /* back up over final separator */ buf[--len] = '\0'; return len; } } /* Place the string representation of the numeric part of hostrange into buf * writing at most n chars including NUL termination. */ static size_t hostrange_numstr(hostrange_t hr, size_t n, char *buf) { int len = 0; assert(buf != NULL); if (hr->singlehost || n == 0) return 0; len = snprintf(buf, n, "%0*lu", hr->width, hr->lo); if ((len >= 0) && (len < n) && (hr->lo < hr->hi)) { int len2 = snprintf(buf+len, n-len, "-%0*lu", hr->width, hr->hi); if (len2 < 0) len = -1; else len += len2; } return len; } /* ----[ hostlist functions ]---- */ /* Create a new hostlist object. * Returns an empty hostlist, or NULL if memory allocation fails. */ static hostlist_t hostlist_new(void) { int i; hostlist_t new = (hostlist_t) malloc(sizeof(*new)); if (!new) goto fail1; assert((new->magic = HOSTLIST_MAGIC)); mutex_init(&new->mutex); new->hr = (hostrange_t *) malloc(HOSTLIST_CHUNK * sizeof(hostrange_t)); if (!new->hr) goto fail2; /* set entries in hostrange array to NULL */ for (i = 0; i < HOSTLIST_CHUNK; i++) new->hr[i] = NULL; new->size = HOSTLIST_CHUNK; new->nranges = 0; new->nhosts = 0; new->ilist = NULL; return new; fail2: free(new); fail1: out_of_memory("hostlist_create"); } /* Resize the internal array used to store the list of hostrange objects. * * returns 1 for a successful resize, * 0 if call to _realloc fails * * It is assumed that the caller has the hostlist hl locked */ static int hostlist_resize(hostlist_t hl, size_t newsize) { int i; size_t oldsize; assert(hl != NULL); assert((hl->magic == HOSTLIST_MAGIC)); oldsize = hl->size; hl->size = newsize; hl->hr = realloc((void *) hl->hr, hl->size*sizeof(hostrange_t)); if (!(hl->hr)) return 0; for (i = oldsize; i < newsize; i++) hl->hr[i] = NULL; return 1; } /* Resize hostlist by one HOSTLIST_CHUNK * Assumes that hostlist hl is locked by caller */ static int hostlist_expand(hostlist_t hl) { if (!hostlist_resize(hl, hl->size + HOSTLIST_CHUNK)) return 0; else return 1; } /* Push a hostrange object onto hostlist hl * Returns the number of hosts successfully pushed onto hl * or -1 if there was an error allocating memory */ static int hostlist_push_range(hostlist_t hl, hostrange_t hr) { hostrange_t tail; int retval; assert(hr != NULL); LOCK_HOSTLIST(hl); tail = (hl->nranges > 0) ? hl->hr[hl->nranges-1] : hl->hr[0]; if (hl->size == hl->nranges && !hostlist_expand(hl)) goto error; if (hl->nranges > 0 && hostrange_prefix_cmp(tail, hr) == 0 && tail->hi == hr->lo - 1 && hostrange_width_combine(tail, hr)) { tail->hi = hr->hi; } else { if ((hl->hr[hl->nranges++] = hostrange_copy(hr)) == NULL) goto error; } retval = hl->nhosts += hostrange_count(hr); UNLOCK_HOSTLIST(hl); return retval; error: UNLOCK_HOSTLIST(hl); return -1; } /* Same as hostlist_push_range() above, but prefix, lo, hi, and width * are passed as args */ static int hostlist_push_hr(hostlist_t hl, char *prefix, unsigned long lo, unsigned long hi, int width) { hostrange_t hr = hostrange_create(prefix, lo, hi, width); int retval = hostlist_push_range(hl, hr); hostrange_destroy(hr); return retval; } /* Insert a range object hr into position n of the hostlist hl * Assumes that hl->mutex is already held by calling process */ static int hostlist_insert_range(hostlist_t hl, hostrange_t hr, int n) { int i; hostrange_t tmp; hostlist_iterator_t hli; assert(hl != NULL); assert((hl->magic == HOSTLIST_MAGIC)); assert(hr != NULL); if (n > hl->nranges) return 0; if (hl->size == hl->nranges && !hostlist_expand(hl)) return 0; /* copy new hostrange into slot "n" in array */ tmp = hl->hr[n]; hl->hr[n] = hostrange_copy(hr); /* push remaining hostrange entries up */ for (i = n + 1; i < hl->nranges + 1; i++) { hostrange_t last = hl->hr[i]; hl->hr[i] = tmp; tmp = last; } hl->nranges++; /* adjust hostlist iterators if needed */ for (hli = hl->ilist; hli; hli = hli->next) { if (hli->idx >= n) hli->hr = hli->hl->hr[++hli->idx]; } return 1; } /* Delete the range at position n in the range array * Assumes the hostlist lock is already held. */ static void hostlist_delete_range(hostlist_t hl, int n) { int i; hostrange_t old; assert(hl != NULL); assert((hl->magic == HOSTLIST_MAGIC)); assert(n < hl->nranges && n >= 0); old = hl->hr[n]; for (i = n; i < hl->nranges - 1; i++) hl->hr[i] = hl->hr[i + 1]; hl->nranges--; hl->hr[hl->nranges] = NULL; hostlist_shift_iterators(hl, n, 0, 1); /* XXX caller responsible for adjusting nhosts */ /* hl->nhosts -= hostrange_count(old) */ hostrange_destroy(old); } #if WANT_RECKLESS_HOSTRANGE_EXPANSION /* The reckless hostrange expansion function. * See comment in hostlist.h:hostlist_create() for more info on * the different choices for hostlist notation. */ hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op) { char *str, *orig; char *tok, *cur; int high, low, fmt = 0; char prefix[256] = ""; int pos = 0; int error = 0; char range_op = r_op[0];/* XXX support > 1 char range ops in future? */ hostlist_t new = hostlist_new(); orig = str = strdup(hostlist); /* return an empty list if an empty string was passed in */ if (str == NULL || strlen(str) == 0) goto done; /* Use hostlist_create_bracketed if we see "[" */ if (strchr(str, '[') != NULL) return _hostlist_create_bracketed(hostlist, sep, r_op); while ((tok = _next_tok(sep, &str)) != NULL) { /* save the current string for error messages */ cur = tok; high = low = 0; /* find end of alpha part * do this by finding last occurence of range_op in str */ pos = strlen(tok) - 1; if (strstr(tok, r_op) != NULL) { while (pos >= 0 && (char) tok[pos] != range_op) pos--; } /* now back up past any digits */ while (pos >= 0 && isdigit((char) tok[--pos])) {;} /* Check for valid x-y range (x must be a digit) * Reset pos if the range is not valid */ if (!isdigit((char) tok[++pos])) pos = strlen(tok) - 1; /* create prefix string * if prefix will be zero length, but prefix already exists * use the previous prefix and fmt */ if ((pos > 0) || (prefix[0] == '\0')) { memcpy(prefix, tok, (size_t) pos * sizeof(char)); prefix[pos] = '\0'; /* push pointer past prefix */ tok += pos; /* count number of digits for ouput fmt */ for (fmt = 0; isdigit(tok[fmt]); ++fmt) {;} if (fmt == 0) error = 1; } else tok += pos; /* get lower bound */ low = strtoul(tok, (char **) &tok, 10); if (*tok == range_op) { /* now get range upper bound */ /* push pointer past range op */ ++tok; /* find length of alpha part */ for (pos = 0; tok[pos] && !isdigit(tok[pos]); ++pos) {;} /* alpha part must match prefix or error * this could mean we've got something like "rtr1-a2" * so just record an error */ if (pos > 0) { if (pos != strlen(prefix) || strncmp(prefix, tok, pos) != 0) error = 1; } if (*tok != '\0') tok += pos; /* make sure we have digits to the end */ for (pos = 0; tok[pos] && isdigit((char) tok[pos]); ++pos) {;} if (pos > 0) { /* we have digits to process */ high = strtoul(tok, (char **) &tok, 10); } else { /* bad boy, no digits */ error = 1; } if ((low > high) || (high - low > MAX_RANGE)) error = 1; } else { /* single value */ high = 0; /* special case, ugh. */ } /* error if: * 1. we are not at end of string * 2. upper bound equals lower bound */ if (*tok != '\0' || high == low) error = 1; if (error) { /* assume this is not a range on any error */ hostlist_push_host(new, cur); } else { if (high < low) high = low; hostlist_push_hr(new, prefix, low, high, fmt); } error = 0; } done: free(orig); return new; } #else /* !WANT_RECKLESS_HOSTRANGE_EXPANSION */ hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op) { return _hostlist_create_bracketed(hostlist, sep, r_op); } #endif /* WANT_RECKLESS_HOSTRANGE_EXPANSION */ struct _range { unsigned long lo, hi; int width; }; /* Grab a single range from str * returns 1 if str contained a valid number or range, * 0 if conversion of str to a range failed. */ static int _parse_single_range(const char *str, struct _range *range) { char *p, *q; char *orig = strdup(str); if (!orig) seterrno_ret(ENOMEM, 0); if ((p = strchr(str, '-'))) { *p++ = '\0'; if (*p == '-') /* do NOT allow negative numbers */ goto error; } range->lo = strtoul(str, &q, 10); if (q == str) goto error; range->hi = (p && *p) ? strtoul(p, &q, 10) : range->lo; if (q == p || *q != '\0') goto error; if (range->lo > range->hi) goto error; if (range->hi - range->lo + 1 > MAX_RANGE ) { _error(__FILE__, __LINE__, "Too many hosts in range `%s'", orig); free(orig); seterrno_ret(ERANGE, 0); } free(orig); range->width = strlen(str); return 1; error: _error(__FILE__, __LINE__, "Invalid range: `%s'", orig); free(orig); seterrno_ret(EINVAL, 0); } /* * Convert 'str' containing comma separated digits and ranges into an array * of struct _range types (max 'len' elements). * * Return number of ranges created, or -1 on error. */ static int _parse_range_list(char *str, struct _range *ranges, int len) { char *p; int count = 0; while (str) { if (count == len) return -1; if ((p = strchr(str, ','))) *p++ = '\0'; if (!_parse_single_range(str, &ranges[count++])) return -1; str = p; } return count; } static void _push_range_list(hostlist_t hl, char *pfx, struct _range *rng, int n) { int i; for (i = 0; i < n; i++) { hostlist_push_hr(hl, pfx, rng->lo, rng->hi, rng->width); rng++; } } static void _push_range_list_with_suffix(hostlist_t hl, char *pfx, char *sfx, struct _range *rng, int n) { int i; unsigned long j; for (i = 0; i < n; i++) { for (j = rng->lo; j <= rng->hi; j++) { char host[4096]; hostrange_t hr; snprintf (host, 4096, "%s%0*lu%s", pfx, rng->width, j, sfx); hr = hostrange_create_single (host); hostlist_push_range (hl, hr); /* * hr is copied in hostlist_push_range. Need to free here. */ hostrange_destroy (hr); } rng++; } } /* * Create a hostlist from a string with brackets '[' ']' to aid * detection of ranges and compressed lists */ static hostlist_t _hostlist_create_bracketed(const char *hostlist, char *sep, char *r_op) { hostlist_t new = hostlist_new(); struct _range ranges[MAX_RANGES]; int nr, err; char *p, *tok, *str, *orig; char cur_tok[1024]; if (hostlist == NULL) return new; if (!(orig = str = strdup(hostlist))) { hostlist_destroy(new); return NULL; } while ((tok = _next_tok(sep, &str)) != NULL) { strncpy(cur_tok, tok, sizeof (cur_tok) - 1); if ((p = strchr(tok, '[')) != NULL) { char *q, *prefix = tok; *p++ = '\0'; if ((q = strchr(p, ']'))) { *q = '\0'; nr = _parse_range_list(p, ranges, MAX_RANGES); if (nr < 0) goto error; if (*(++q) != '\0') _push_range_list_with_suffix (new, prefix, q, ranges, nr); else _push_range_list(new, prefix, ranges, nr); } else /* Error: brackets must be balanced */ goto error_unmatched; } else if (strchr(tok, ']')) /* Error: brackets must be balanced */ goto error_unmatched; else /* Ok: No brackets found, single host */ hostlist_push_host(new, cur_tok); } free(orig); return new; error_unmatched: errno = EINVAL; error: err = errno; hostlist_destroy(new); free(orig); seterrno_ret(err, NULL); } hostlist_t hostlist_create(const char *str) { return _hostlist_create(str, "\t, ", "-"); } hostlist_t hostlist_copy(const hostlist_t hl) { int i; hostlist_t new; if (hl == NULL) return NULL; LOCK_HOSTLIST(hl); if (!(new = hostlist_new())) goto done; new->nranges = hl->nranges; new->nhosts = hl->nhosts; if (new->nranges > new->size) hostlist_resize(new, new->nranges); for (i = 0; i < hl->nranges; i++) new->hr[i] = hostrange_copy(hl->hr[i]); done: UNLOCK_HOSTLIST(hl); return new; } void hostlist_destroy(hostlist_t hl) { int i; if (hl == NULL) return; LOCK_HOSTLIST(hl); while (hl->ilist) { mutex_unlock(&hl->mutex); hostlist_iterator_destroy(hl->ilist); mutex_lock(&hl->mutex); } for (i = 0; i < hl->nranges; i++) hostrange_destroy(hl->hr[i]); free(hl->hr); assert((hl->magic = 0x1)); UNLOCK_HOSTLIST(hl); mutex_destroy(&hl->mutex); free(hl); } int hostlist_push(hostlist_t hl, const char *hosts) { hostlist_t new; int retval; if (hosts == NULL) return 0; new = hostlist_create(hosts); if (!new) return 0; mutex_lock(&new->mutex); retval = new->nhosts; mutex_unlock(&new->mutex); hostlist_push_list(hl, new); hostlist_destroy(new); return retval; } int hostlist_push_host(hostlist_t hl, const char *str) { hostrange_t hr; hostname_t hn; if (str == NULL) return 0; hn = hostname_create(str); if (hostname_suffix_is_valid(hn)) { hr = hostrange_create(hn->prefix, hn->num, hn->num, hostname_suffix_width(hn)); } else hr = hostrange_create_single(str); hostlist_push_range(hl, hr); hostrange_destroy(hr); hostname_destroy(hn); return 1; } int hostlist_push_list(hostlist_t h1, hostlist_t h2) { int i, n = 0; if (h2 == NULL) return 0; LOCK_HOSTLIST(h2); for (i = 0; i < h2->nranges; i++) n += hostlist_push_range(h1, h2->hr[i]); UNLOCK_HOSTLIST(h2); return n; } char *hostlist_pop(hostlist_t hl) { char *host = NULL; LOCK_HOSTLIST(hl); if (hl->nhosts > 0) { hostrange_t hr = hl->hr[hl->nranges - 1]; host = hostrange_pop(hr); hl->nhosts--; if (hostrange_empty(hr)) { hostrange_destroy(hl->hr[--hl->nranges]); hl->hr[hl->nranges] = NULL; } } UNLOCK_HOSTLIST(hl); return host; } /* find all iterators affected by a shift (or deletion) at * hl->hr[idx], depth, with the deletion of n ranges */ static void hostlist_shift_iterators(hostlist_t hl, int idx, int depth, int n) { hostlist_iterator_t i; for (i = hl->ilist; i; i = i->next) { if (n == 0) { if (i->idx == idx && i->depth >= depth) i->depth = i->depth > -1 ? i->depth - 1 : -1; } else { if (i->idx >= idx) { if ((i->idx -= n) >= 0) i->hr = i->hl->hr[i->idx]; else hostlist_iterator_reset(i); } } } } char *hostlist_shift(hostlist_t hl) { char *host = NULL; LOCK_HOSTLIST(hl); if (hl->nhosts > 0) { hostrange_t hr = hl->hr[0]; host = hostrange_shift(hr); hl->nhosts--; if (hostrange_empty(hr)) { hostlist_delete_range(hl, 0); /* hl->nranges--; */ } else hostlist_shift_iterators(hl, 0, 0, 0); } UNLOCK_HOSTLIST(hl); return host; } char *hostlist_pop_range(hostlist_t hl) { int i; char buf[MAXHOSTRANGELEN + 1]; hostlist_t hltmp; hostrange_t tail; LOCK_HOSTLIST(hl); if (hl->nranges < 1 || !(hltmp = hostlist_new())) { UNLOCK_HOSTLIST(hl); return NULL; } i = hl->nranges - 2; tail = hl->hr[hl->nranges - 1]; while (i >= 0 && hostrange_within_range(tail, hl->hr[i])) i--; for (i++; i < hl->nranges; i++) { hostlist_push_range(hltmp, hl->hr[i]); hostrange_destroy(hl->hr[i]); hl->hr[i] = NULL; } hl->nhosts -= hltmp->nhosts; hl->nranges -= hltmp->nranges; UNLOCK_HOSTLIST(hl); hostlist_ranged_string(hltmp, MAXHOSTRANGELEN, buf); hostlist_destroy(hltmp); return strdup(buf); } char *hostlist_shift_range(hostlist_t hl) { int i; char buf[1024]; hostlist_t hltmp = hostlist_new(); if (!hltmp) return NULL; LOCK_HOSTLIST(hl); if (hl->nranges == 0) { hostlist_destroy(hltmp); UNLOCK_HOSTLIST(hl); return NULL; } i = 0; do { hostlist_push_range(hltmp, hl->hr[i]); hostrange_destroy(hl->hr[i]); } while ( (++i < hl->nranges) && hostrange_within_range(hltmp->hr[0], hl->hr[i]) ); hostlist_shift_iterators(hl, i, 0, hltmp->nranges); /* shift rest of ranges back in hl */ for (; i < hl->nranges; i++) { hl->hr[i - hltmp->nranges] = hl->hr[i]; hl->hr[i] = NULL; } hl->nhosts -= hltmp->nhosts; hl->nranges -= hltmp->nranges; UNLOCK_HOSTLIST(hl); hostlist_ranged_string(hltmp, 1024, buf); hostlist_destroy(hltmp); return strdup(buf); } /* XXX: Note: efficiency improvements needed */ int hostlist_delete(hostlist_t hl, const char *hosts) { int n = 0; char *hostname = NULL; hostlist_t hltmp; if (!(hltmp = hostlist_create(hosts))) seterrno_ret(EINVAL, 0); while ((hostname = hostlist_pop(hltmp)) != NULL) { n += hostlist_delete_host(hl, hostname); free(hostname); } hostlist_destroy(hltmp); return n; } /* XXX watch out! poor implementation follows! (fix it at some point) */ int hostlist_delete_host(hostlist_t hl, const char *hostname) { int n = hostlist_find(hl, hostname); if (n >= 0) hostlist_delete_nth(hl, n); return n >= 0 ? 1 : 0; } static char * _hostrange_string(hostrange_t hr, int depth) { char buf[MAXHOSTNAMELEN + 16]; int len = snprintf(buf, MAXHOSTNAMELEN + 15, "%s", hr->prefix); if (!hr->singlehost) snprintf(buf+len, MAXHOSTNAMELEN+15 - len, "%0*lu", hr->width, hr->lo + depth); return strdup(buf); } char * hostlist_nth(hostlist_t hl, int n) { char *host = NULL; int i, count; LOCK_HOSTLIST(hl); count = 0; for (i = 0; i < hl->nranges; i++) { int num_in_range = hostrange_count(hl->hr[i]); if (n <= (num_in_range - 1 + count)) { host = _hostrange_string(hl->hr[i], n - count); break; } else count += num_in_range; } UNLOCK_HOSTLIST(hl); return host; } int hostlist_delete_nth(hostlist_t hl, int n) { int i, count; LOCK_HOSTLIST(hl); assert(n >= 0 && n <= hl->nhosts); count = 0; for (i = 0; i < hl->nranges; i++) { int num_in_range = hostrange_count(hl->hr[i]); hostrange_t hr = hl->hr[i]; if (n <= (num_in_range - 1 + count)) { unsigned long num = hr->lo + n - count; hostrange_t new; if (hr->singlehost) { /* this wasn't a range */ hostlist_delete_range(hl, i); } else if ((new = hostrange_delete_host(hr, num))) { hostlist_insert_range(hl, new, i + 1); hostrange_destroy(new); } else if (hostrange_empty(hr)) hostlist_delete_range(hl, i); goto done; } else count += num_in_range; } done: hl->nhosts--; UNLOCK_HOSTLIST(hl); return 1; } int hostlist_count(hostlist_t hl) { int retval; LOCK_HOSTLIST(hl); retval = hl->nhosts; UNLOCK_HOSTLIST(hl); return retval; } int hostlist_find(hostlist_t hl, const char *hostname) { int i, count, ret = -1; hostname_t hn; if (!hostname) return -1; hn = hostname_create(hostname); LOCK_HOSTLIST(hl); for (i = 0, count = 0; i < hl->nranges; i++) { int offset = hostrange_hn_within(hl->hr[i], hn); if (offset >= 0) { ret = count + offset; break; } else count += hostrange_count(hl->hr[i]); } UNLOCK_HOSTLIST(hl); hostname_destroy(hn); return ret; } /* hostrange compare with void * arguments to allow use with * libc qsort() */ int _cmp(const void *hr1, const void *hr2) { hostrange_t *h1 = (hostrange_t *) hr1; hostrange_t *h2 = (hostrange_t *) hr2; return hostrange_cmp((hostrange_t) * h1, (hostrange_t) * h2); } void hostlist_sort(hostlist_t hl) { hostlist_iterator_t i; LOCK_HOSTLIST(hl); if (hl->nranges <= 1) { UNLOCK_HOSTLIST(hl); return; } qsort(hl->hr, hl->nranges, sizeof(hostrange_t), &_cmp); /* reset all iterators */ for (i = hl->ilist; i; i = i->next) hostlist_iterator_reset(i); UNLOCK_HOSTLIST(hl); hostlist_coalesce(hl); } /* search through hostlist for ranges that can be collapsed * does =not= delete any hosts */ static void hostlist_collapse(hostlist_t hl) { int i; LOCK_HOSTLIST(hl); for (i = hl->nranges - 1; i > 0; i--) { hostrange_t hprev = hl->hr[i - 1]; hostrange_t hnext = hl->hr[i]; if (hostrange_prefix_cmp(hprev, hnext) == 0 && hprev->hi == hnext->lo - 1 && hostrange_width_combine(hprev, hnext)) { hprev->hi = hnext->hi; hostlist_delete_range(hl, i); } } UNLOCK_HOSTLIST(hl); } /* search through hostlist (hl) for intersecting ranges * split up duplicates and coalesce ranges where possible */ static void hostlist_coalesce(hostlist_t hl) { int i, j; hostrange_t new; LOCK_HOSTLIST(hl); for (i = hl->nranges - 1; i > 0; i--) { new = hostrange_intersect(hl->hr[i - 1], hl->hr[i]); if (new) { hostrange_t hprev = hl->hr[i - 1]; hostrange_t hnext = hl->hr[i]; j = i; if (new->hi < hprev->hi) hnext->hi = hprev->hi; hprev->hi = new->lo; hnext->lo = new->hi; if (hostrange_empty(hprev)) hostlist_delete_range(hl, i); while (new->lo <= new->hi) { hostrange_t hr = hostrange_create( new->prefix, new->lo, new->lo, new->width ); if (new->lo > hprev->hi) hostlist_insert_range(hl, hr, j++); if (new->lo < hnext->lo) hostlist_insert_range(hl, hr, j++); hostrange_destroy(hr); new->lo++; } i = hl->nranges; hostrange_destroy(new); } } UNLOCK_HOSTLIST(hl); hostlist_collapse(hl); } /* attempt to join ranges at loc and loc-1 in a hostlist */ /* delete duplicates, return the number of hosts deleted */ /* assumes that the hostlist hl has been locked by caller */ /* returns -1 if no range join occurred */ static int _attempt_range_join(hostlist_t hl, int loc) { int ndup; assert(hl != NULL); assert(hl->magic == HOSTLIST_MAGIC); assert(loc > 0); assert(loc < hl->nranges); ndup = hostrange_join(hl->hr[loc - 1], hl->hr[loc]); if (ndup >= 0) { hostlist_delete_range(hl, loc); hl->nhosts -= ndup; } return ndup; } void hostlist_uniq(hostlist_t hl) { int i = 1; hostlist_iterator_t hli; LOCK_HOSTLIST(hl); if (hl->nranges <= 1) { UNLOCK_HOSTLIST(hl); return; } qsort(hl->hr, hl->nranges, sizeof(hostrange_t), &_cmp); while (i < hl->nranges) { if (_attempt_range_join(hl, i) < 0) /* No range join occurred */ i++; } /* reset all iterators */ for (hli = hl->ilist; hli; hli = hli->next) hostlist_iterator_reset(hli); UNLOCK_HOSTLIST(hl); } ssize_t hostlist_deranged_string(hostlist_t hl, size_t n, char *buf) { int i; int len = 0; int truncated = 0; LOCK_HOSTLIST(hl); for (i = 0; i < hl->nranges; i++) { size_t m = (n - len) <= n ? n - len : 0; int ret = hostrange_to_string(hl->hr[i], m, buf + len, ","); if (ret < 0 || ret > m) { len = n; truncated = 1; break; } len+=ret; buf[len++] = ','; } UNLOCK_HOSTLIST(hl); buf[len > 0 ? --len : 0] = '\0'; if (len == n) truncated = 1; return truncated ? -1 : len; } /* return true if a bracket is needed for the range at i in hostlist hl */ static int _is_bracket_needed(hostlist_t hl, int i) { hostrange_t h1 = hl->hr[i]; hostrange_t h2 = i < hl->nranges - 1 ? hl->hr[i + 1] : NULL; return hostrange_count(h1) > 1 || hostrange_within_range(h1, h2); } /* write the next bracketed hostlist, i.e. prefix[n-m,k,...] * into buf, writing at most n chars including the terminating '\0' * * leaves start pointing to one past last range object in bracketed list, * and returns the number of bytes written into buf. * * Assumes hostlist is locked. */ static int _get_bracketed_list(hostlist_t hl, int *start, const size_t n, char *buf) { hostrange_t *hr = hl->hr; int i = *start; int m, len = 0; int bracket_needed = _is_bracket_needed(hl, i); len = snprintf(buf, n, "%s", hr[i]->prefix); if ((len < 0) || (len > n)) return n; /* truncated, buffer filled */ if (bracket_needed && len < n) buf[len++] = '['; do { m = (n - len) <= n ? n - len : 0; len += hostrange_numstr(hr[i], m, buf + len); if (len >= n) break; if (bracket_needed) /* Only need commas inside brackets */ buf[len++] = ','; } while (++i < hl->nranges && hostrange_within_range(hr[i], hr[i-1])); if (bracket_needed && len < n && len > 0) { /* Add trailing bracket (change trailing "," from above to "]" */ buf[len - 1] = ']'; /* NUL terminate for safety, but do not add terminator to len */ buf[len] = '\0'; } else if (len >= n) { if (n > 0) buf[n-1] = '\0'; } else { /* If len is > 0, NUL terminate (but do not add to len) */ buf[len > 0 ? len : 0] = '\0'; } *start = i; return len; } ssize_t hostlist_ranged_string(hostlist_t hl, size_t n, char *buf) { int i = 0; int len = 0; int truncated = 0; LOCK_HOSTLIST(hl); while (i < hl->nranges && len < n) { len += _get_bracketed_list(hl, &i, n - len, buf + len); if ((len > 0) && (len < n) && (i < hl->nranges)) buf[len++] = ','; } UNLOCK_HOSTLIST(hl); /* NUL terminate */ if (len >= n) { truncated = 1; if (n > 0) buf[n-1] = '\0'; } else buf[len > 0 ? len : 0] = '\0'; return truncated ? -1 : len; } /* ----[ hostlist iterator functions ]---- */ static hostlist_iterator_t hostlist_iterator_new(void) { hostlist_iterator_t i = (hostlist_iterator_t) malloc(sizeof(*i)); if (!i) return NULL; i->hl = NULL; i->hr = NULL; i->idx = 0; i->depth = -1; i->next = i; assert((i->magic = HOSTLIST_MAGIC)); return i; } hostlist_iterator_t hostlist_iterator_create(hostlist_t hl) { hostlist_iterator_t i; if (!(i = hostlist_iterator_new())) out_of_memory("hostlist_iterator_create"); LOCK_HOSTLIST(hl); i->hl = hl; i->hr = hl->hr[0]; i->next = hl->ilist; hl->ilist = i; UNLOCK_HOSTLIST(hl); return i; } hostlist_iterator_t hostset_iterator_create(hostset_t set) { return hostlist_iterator_create(set->hl); } void hostlist_iterator_reset(hostlist_iterator_t i) { assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); i->idx = 0; i->hr = i->hl->hr[0]; i->depth = -1; return; } void hostlist_iterator_destroy(hostlist_iterator_t i) { hostlist_iterator_t *pi; if (i == NULL) return; assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); LOCK_HOSTLIST(i->hl); for (pi = &i->hl->ilist; *pi; pi = &(*pi)->next) { assert((*pi)->magic == HOSTLIST_MAGIC); if (*pi == i) { *pi = (*pi)->next; break; } } UNLOCK_HOSTLIST(i->hl); assert((i->magic = 0x1)); free(i); } static void _iterator_advance(hostlist_iterator_t i) { assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); if (i->idx > i->hl->nranges - 1) return; if (++(i->depth) > (i->hr->hi - i->hr->lo)) { i->depth = 0; if (++i->idx >= i->hl->size) { i->hr = NULL; return; } i->hr = i->hl->hr[i->idx]; } } /* advance iterator to end of current range (meaning within "[" "]") * i.e. advance iterator past all range objects that could be represented * in on bracketed hostlist. */ static void _iterator_advance_range(hostlist_iterator_t i) { int nr, j; hostrange_t *hr; assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); nr = i->hl->nranges; hr = i->hl->hr; j = i->idx; if (++i->depth > 0) { while (++j < nr && hostrange_within_range(i->hr, hr[j])) {;} i->idx = j; i->hr = i->hl->hr[i->idx]; i->depth = 0; } } char *hostlist_next(hostlist_iterator_t i) { char *buf = NULL; char suffix[16]; int len = 0; assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); LOCK_HOSTLIST(i->hl); _iterator_advance(i); if (i->idx > i->hl->nranges - 1) { UNLOCK_HOSTLIST(i->hl); return NULL; } suffix[0] = '\0'; if (!i->hr->singlehost) snprintf (suffix, 15, "%0*lu", i->hr->width, i->hr->lo + i->depth); len = strlen (i->hr->prefix) + strlen (suffix) + 1; if (!(buf = malloc (len))) out_of_memory("hostlist_next"); buf[0] = '\0'; strcat (buf, i->hr->prefix); strcat (buf, suffix); UNLOCK_HOSTLIST(i->hl); return (buf); } char *hostlist_next_range(hostlist_iterator_t i) { char buf[MAXHOSTRANGELEN + 1]; int j; assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); LOCK_HOSTLIST(i->hl); _iterator_advance_range(i); if (i->idx > i->hl->nranges - 1) { UNLOCK_HOSTLIST(i->hl); return NULL; } j = i->idx; _get_bracketed_list(i->hl, &j, MAXHOSTRANGELEN, buf); UNLOCK_HOSTLIST(i->hl); return strdup(buf); } int hostlist_remove(hostlist_iterator_t i) { hostrange_t new; assert(i != NULL); assert(i->magic == HOSTLIST_MAGIC); LOCK_HOSTLIST(i->hl); new = hostrange_delete_host(i->hr, i->hr->lo + i->depth); if (new) { hostlist_insert_range(i->hl, new, i->idx + 1); hostrange_destroy(new); i->hr = i->hl->hr[++i->idx]; i->depth = -1; } else if (hostrange_empty(i->hr)) { hostlist_delete_range(i->hl, i->idx); } else i->depth--; i->hl->nhosts--; UNLOCK_HOSTLIST(i->hl); return 1; } /* ----[ hostset functions ]---- */ hostset_t hostset_create(const char *hostlist) { hostset_t new; if (!(new = (hostset_t) malloc(sizeof(*new)))) goto error1; if (!(new->hl = hostlist_create(hostlist))) goto error2; hostlist_uniq(new->hl); return new; error2: free(new); error1: return NULL; } hostset_t hostset_copy(const hostset_t set) { hostset_t new; if (!(new = (hostset_t) malloc(sizeof(*new)))) goto error1; if (!(new->hl = hostlist_copy(set->hl))) goto error2; return new; error2: free(new); error1: return NULL; } void hostset_destroy(hostset_t set) { if (set == NULL) return; hostlist_destroy(set->hl); free(set); } /* inserts a single range object into a hostset * Assumes that the set->hl lock is already held * Updates hl->nhosts */ static int hostset_insert_range(hostset_t set, hostrange_t hr) { int i = 0; int inserted = 0; int nhosts = 0; int ndups = 0; hostlist_t hl; hl = set->hl; if (hl->size == hl->nranges && !hostlist_expand(hl)) return 0; nhosts = hostrange_count(hr); for (i = 0; i < hl->nranges; i++) { if (hostrange_cmp(hr, hl->hr[i]) <= 0) { if ((ndups = hostrange_join(hr, hl->hr[i])) >= 0) hostlist_delete_range(hl, i); else if (ndups < 0) ndups = 0; hostlist_insert_range(hl, hr, i); /* now attempt to join hr[i] and hr[i-1] */ if (i > 0) { int m; if ((m = _attempt_range_join(hl, i)) > 0) ndups += m; } hl->nhosts += nhosts - ndups; inserted = 1; break; } } if (inserted == 0) { hl->hr[hl->nranges++] = hostrange_copy(hr); hl->nhosts += nhosts; if (hl->nranges > 1) { if ((ndups = _attempt_range_join(hl, hl->nranges - 1)) <= 0) ndups = 0; } } /* * Return the number of unique hosts inserted */ return nhosts - ndups; } int hostset_insert(hostset_t set, const char *hosts) { int i, n = 0; hostlist_t hl = hostlist_create(hosts); if (!hl) return 0; hostlist_uniq(hl); LOCK_HOSTLIST(set->hl); for (i = 0; i < hl->nranges; i++) n += hostset_insert_range(set, hl->hr[i]); UNLOCK_HOSTLIST(set->hl); hostlist_destroy(hl); return n; } /* linear search through N ranges for hostname "host" * */ static int hostset_find_host(hostset_t set, const char *host) { int i; int retval = 0; hostname_t hn; LOCK_HOSTLIST(set->hl); hn = hostname_create(host); for (i = 0; i < set->hl->nranges; i++) { if (hostrange_hn_within(set->hl->hr[i], hn) >= 0) { retval = 1; goto done; } } done: UNLOCK_HOSTLIST(set->hl); hostname_destroy(hn); return retval; } int hostset_within(hostset_t set, const char *hosts) { int nhosts, nfound; hostlist_t hl; char *hostname; assert(set->hl->magic == HOSTLIST_MAGIC); if (!(hl = hostlist_create(hosts))) return (0); nhosts = hostlist_count(hl); nfound = 0; while ((hostname = hostlist_pop(hl)) != NULL) { nfound += hostset_find_host(set, hostname); free(hostname); } hostlist_destroy(hl); return (nhosts == nfound); } int hostset_delete(hostset_t set, const char *hosts) { return hostlist_delete(set->hl, hosts); } int hostset_delete_host(hostset_t set, const char *hostname) { return hostlist_delete_host(set->hl, hostname); } char *hostset_shift(hostset_t set) { return hostlist_shift(set->hl); } char *hostset_pop(hostset_t set) { return hostlist_pop(set->hl); } char *hostset_shift_range(hostset_t set) { return hostlist_shift_range(set->hl); } char *hostset_pop_range(hostset_t set) { return hostlist_pop_range(set->hl); } int hostset_count(hostset_t set) { return hostlist_count(set->hl); } ssize_t hostset_ranged_string(hostset_t set, size_t n, char *buf) { return hostlist_ranged_string(set->hl, n, buf); } ssize_t hostset_deranged_string(hostset_t set, size_t n, char *buf) { return hostlist_deranged_string(set->hl, n, buf); } #if TEST_MAIN int hostlist_nranges(hostlist_t hl) { return hl->nranges; } int hostset_nranges(hostset_t set) { return set->hl->nranges; } /* test iterator functionality on the list of hosts represented * by list */ int iterator_test(char *list) { int j; char buf[1024]; hostlist_t hl = hostlist_create(list); hostset_t set = hostset_create(list); hostlist_iterator_t i = hostlist_iterator_create(hl); hostlist_iterator_t seti = hostset_iterator_create(set); hostlist_iterator_t i2 = hostlist_iterator_create(hl); char *host; hostlist_ranged_string(hl, 1024, buf); printf("iterator_test: hl = `%s' passed in `%s'\n", buf, list); host = hostlist_next(i); printf("first host in list hl = `%s'\n", host); free(host); /* forge ahead three hosts with i2 */ for (j = 0; j < 4; j++) { host = hostlist_next(i2); free(host); } host = hostlist_shift(hl); printf("result of shift(hl) = `%s'\n", host); free(host); host = hostlist_next(i); printf("next host in list hl = `%s'\n", host); free(host); host = hostlist_next(i2); printf("next host for i2 = `%s'\n", host); free(host); hostlist_iterator_destroy(i); hostlist_destroy(hl); hostset_destroy(set); return 1; } int main(int ac, char **av) { char buf[1024000]; int i; char *str; hostlist_t hl1, hl2, hl3; hostset_t set, set1; hostlist_iterator_t iter, iter2; if (!(hl1 = hostlist_create(ac > 1 ? av[1] : NULL))) perror("hostlist_create"); if (!(set = hostset_create(ac > 1 ? av[1] : NULL))) perror("hostlist_create"); hl3 = hostlist_create("f[0-5]"); hostlist_delete(hl3, "f[1-3]"); hostlist_ranged_string(hl3, 102400, buf); printf("after delete = `%s'\n", buf); hostlist_destroy(hl3); for (i = 2; i < ac; i++) { hostlist_push(hl1, av[i]); hostset_insert(set, av[i]); } hostlist_ranged_string(hl1, 102400, buf); printf("ranged = `%s'\n", buf); iterator_test(buf); hostlist_deranged_string(hl1, 10240, buf); printf("deranged = `%s'\n", buf); hostset_ranged_string(set, 1024, buf); printf("hostset = `%s'\n", buf); hostlist_sort(hl1); hostlist_ranged_string(hl1, 1024, buf); printf("sorted = `%s'\n", buf); hostlist_uniq(hl1); hostlist_ranged_string(hl1, 1024, buf); printf("uniqed = `%s'\n", buf); hl2 = hostlist_copy(hl1); printf("pop_range: "); while ((str = hostlist_pop_range(hl2))) { printf("`%s' ", str); free(str); } hostlist_destroy(hl2); printf("\n"); hl2 = hostlist_copy(hl1); printf("shift_range: "); while ((str = hostlist_shift_range(hl2))) { printf("`%s' ", str); free(str); } hostlist_destroy(hl2); printf("\n"); iter = hostset_iterator_create(set); iter2 = hostset_iterator_create(set); hostlist_iterator_destroy(iter2); printf("next: "); while ((str = hostlist_next(iter))) { printf("`%s' ", str); free(str); } printf("\n"); hostlist_iterator_reset(iter); printf("next_range: "); while ((str = hostlist_next_range(iter))) { printf("`%s' ", str); free(str); } printf("\n"); printf("nranges = %d\n", hostset_nranges(set)); hostset_ranged_string(set, 1024, buf); printf("set = %s\n", buf); hostset_destroy(set); hostlist_destroy(hl1); return 0; } #endif /* TEST_MAIN */ /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/xstring.c0000664€^–Á €^–Á 0000001126715131211226024436 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Heap-oriented string functions. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #if HAVE_STRERROR_R && !HAVE_DECL_STRERROR_R char *strerror_r(int, char *, int); #endif #include #if HAVE_UNISTD_H #include #endif #include #include "xmalloc.h" #include "xstring.h" #define SPACES "\n\t " #define XFGETS_CHUNKSIZE 32 /* * Zap leading and trailing occurrences of characters in 'verboten'. * str (IN/OUT) string * verboten (IN) list of characters to be zapped (if NULL, zap spaces) */ void xstrcln(char *str, char *verboten) { char *p; char *base = str; if (verboten == NULL) verboten = SPACES; /* move pointer past initial 'verboten' characters */ while (str != NULL && *str != '\0' && strchr(verboten, *str) != NULL) str++; /* overwrite trailing 'verboten' characters with nulls */ if (str != NULL && strlen(str) > 0) { p = str + strlen(str) - 1; while (p > str && *p != '\0' && strchr(verboten, *p) != NULL) *p-- = '\0'; } /* move string */ assert(str >= base); memmove(base, str, strlen(str)); while (str-- > base) base[strlen(base) - 1] = '\0'; } /* * Ensure that a string has enough space to add 'needed' characters. * If the string is uninitialized, it should be NULL. */ static void _makespace(char **str, int needed) { int used; if (*str == NULL) *str = Malloc(needed + 1); else { used = strlen(*str) + 1; while (used + needed > Size(*str)) { int newsize = Size(*str) + XFGETS_CHUNKSIZE; Realloc((void **) str, newsize); assert(Size(*str) == newsize); } } } /* * Concatenate str2 onto str1, expanding str1 as needed. * str1 (IN/OUT) target string (pointer to in case of expansion) * str2 (IN) source string */ void xstrcat(char **str1, char *str2) { _makespace(str1, strlen(str2)); strcat(*str1, str2); } /* * Copy str2 to str1, expanding str1 as needed. * str1 (IN/OUT) target string (pointer to in case of expansion) * str2 (IN) source string */ void xstrcpy(char **str1, char *str2) { _makespace(str1, strlen(str2)); strcpy(*str1, str2); } static void _strcatchar(char *str, char c) { int len = strlen(str); str[len++] = c; str[len] = '\0'; } /* * Add a character to str, expanding str1 as needed. * str1 (IN/OUT) target string (pointer to in case of expansion) * size (IN/OUT) size of str1 (pointer to in case of expansion) * c (IN) character to add */ void xstrcatchar(char **str, char c) { _makespace(str, 1); _strcatchar(*str, c); } void xstrerrorcat(char **buf) { #if HAVE_STRERROR_R # if HAVE_WORKING_STRERROR_R || STRERROR_R_CHAR_P char errbuf[64]; char *err = strerror_r(errno, errbuf, 64); # else char err[64]; int e = errno; if (strerror_r(e, err, 64) < 0) { if (errno == EINVAL) snprintf (err, 64, "Unknown error %d", e); err[63] = '\0'; } # endif #elif HAVE_STRERROR char *err = strerror(errno); #else extern char *sys_errlist[]; char *err = sys_errlist[errno]; #endif xstrcat(buf, err); } /* * Replacement for libc basename * path (IN) path possibly containing '/' characters * RETURN last component of path */ char *xbasename(char *path) { char *p; p = strrchr(path, '/'); return (p ? (p + 1) : path); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/split.h0000664€^–Á €^–Á 0000000276715131211226024105 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _SPLIT_H #define _SPLIT_H #include "list.h" List list_split (char *sep, char *str); List list_split_append (List l, char *sep, char *str); int list_join (char *result, size_t len, const char *sep, List l); #endif /* !_SPLIT_H */ pdsh-2.36/src/common/list.c0000664€^–Á €^–Á 0000005135215131211226023712 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id$ ***************************************************************************** * $LSDId: list.c,v 1.28 2003/05/20 23:53:22 dun Exp $ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * * This file is from LSD-Tools, the LLNL Software Development Toolbox. * * LSD-Tools is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * LSD-Tools is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with LSD-Tools; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***************************************************************************** * Refer to "list.h" for documentation on public functions. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef WITH_PTHREADS # include #endif /* WITH_PTHREADS */ #include #include #include #include #include "list.h" /********************* * lsd_fatal_error * *********************/ #ifdef WITH_LSD_FATAL_ERROR_FUNC # undef lsd_fatal_error extern void lsd_fatal_error(char *file, int line, char *mesg); #else /* !WITH_LSD_FATAL_ERROR_FUNC */ # ifndef lsd_fatal_error # include # include # include # define lsd_fatal_error(file, line, mesg) \ do { \ fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \ file, line, mesg, strerror(errno)); \ } while (0) # endif /* !lsd_fatal_error */ #endif /* !WITH_LSD_FATAL_ERROR_FUNC */ /********************* * lsd_nomem_error * *********************/ #ifdef WITH_LSD_NOMEM_ERROR_FUNC # undef lsd_nomem_error extern void * lsd_nomem_error(char *file, int line, char *mesg); #else /* !WITH_LSD_NOMEM_ERROR_FUNC */ # ifndef lsd_nomem_error # define lsd_nomem_error(file, line, mesg) (NULL) # endif /* !lsd_nomem_error */ #endif /* !WITH_LSD_NOMEM_ERROR_FUNC */ /*************** * Constants * ***************/ #define LIST_ALLOC 32 #define LIST_MAGIC 0xDEADBEEF /**************** * Data Types * ****************/ struct listNode { void *data; /* node's data */ struct listNode *next; /* next node in list */ }; struct listIterator { struct list *list; /* the list being iterated */ struct listNode *pos; /* the next node to be iterated */ struct listNode **prev; /* addr of 'next' ptr to prv It node */ struct listIterator *iNext; /* iterator chain for list_destroy() */ #ifndef NDEBUG unsigned int magic; /* sentinel for asserting validity */ #endif /* !NDEBUG */ }; struct list { struct listNode *head; /* head of the list */ struct listNode **tail; /* addr of last node's 'next' ptr */ struct listIterator *iNext; /* iterator chain for list_destroy() */ ListDelF fDel; /* function to delete node data */ int count; /* number of nodes in list */ #ifdef WITH_PTHREADS pthread_mutex_t mutex; /* mutex to protect access to list */ #endif /* WITH_PTHREADS */ #ifndef NDEBUG unsigned int magic; /* sentinel for asserting validity */ #endif /* !NDEBUG */ }; typedef struct listNode * ListNode; /**************** * Prototypes * ****************/ static void * list_node_create (List l, ListNode *pp, void *x); static void * list_node_destroy (List l, ListNode *pp); static List list_alloc (void); static void list_free (List l); static ListNode list_node_alloc (void); static void list_node_free (ListNode p); static ListIterator list_iterator_alloc (void); static void list_iterator_free (ListIterator i); static void * list_alloc_aux (int size, void *pfreelist); static void list_free_aux (void *x, void *pfreelist); /*************** * Variables * ***************/ static List list_free_lists = NULL; static ListNode list_free_nodes = NULL; static ListIterator list_free_iterators = NULL; #ifdef WITH_PTHREADS static pthread_mutex_t list_free_lock = PTHREAD_MUTEX_INITIALIZER; #endif /* WITH_PTHREADS */ /************ * Macros * ************/ #ifdef WITH_PTHREADS # define list_mutex_init(mutex) \ do { \ int e = pthread_mutex_init(mutex, NULL); \ if (e != 0) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "list mutex init"); \ abort(); \ } \ } while (0) # define list_mutex_lock(mutex) \ do { \ int e = pthread_mutex_lock(mutex); \ if (e != 0) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "list mutex lock"); \ abort(); \ } \ } while (0) # define list_mutex_unlock(mutex) \ do { \ int e = pthread_mutex_unlock(mutex); \ if (e != 0) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "list mutex unlock"); \ abort(); \ } \ } while (0) # define list_mutex_destroy(mutex) \ do { \ int e = pthread_mutex_destroy(mutex); \ if (e != 0) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "list mutex destroy"); \ abort(); \ } \ } while (0) # ifndef NDEBUG static int list_mutex_is_locked (pthread_mutex_t *mutex); # endif /* !NDEBUG */ #else /* !WITH_PTHREADS */ # define list_mutex_init(mutex) # define list_mutex_lock(mutex) # define list_mutex_unlock(mutex) # define list_mutex_destroy(mutex) # define list_mutex_is_locked(mutex) (1) #endif /* !WITH_PTHREADS */ /*************** * Functions * ***************/ List list_create (ListDelF f) { List l; if (!(l = list_alloc())) return(lsd_nomem_error(__FILE__, __LINE__, "list create")); l->head = NULL; l->tail = &l->head; l->iNext = NULL; l->fDel = f; l->count = 0; list_mutex_init(&l->mutex); assert(l->magic = LIST_MAGIC); /* set magic via assert abuse */ return(l); } void list_destroy (List l) { ListIterator i, iTmp; ListNode p, pTmp; assert(l != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); i = l->iNext; while (i) { assert(i->magic == LIST_MAGIC); iTmp = i->iNext; assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ list_iterator_free(i); i = iTmp; } p = l->head; while (p) { pTmp = p->next; if (p->data && l->fDel) l->fDel(p->data); list_node_free(p); p = pTmp; } assert(l->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ list_mutex_unlock(&l->mutex); list_mutex_destroy(&l->mutex); list_free(l); return; } int list_is_empty (List l) { int n; assert(l != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); n = l->count; list_mutex_unlock(&l->mutex); return(n == 0); } int list_count (List l) { int n; assert(l != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); n = l->count; list_mutex_unlock(&l->mutex); return(n); } void * list_append (List l, void *x) { void *v; assert(l != NULL); assert(x != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = list_node_create(l, l->tail, x); list_mutex_unlock(&l->mutex); return(v); } void * list_prepend (List l, void *x) { void *v; assert(l != NULL); assert(x != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = list_node_create(l, &l->head, x); list_mutex_unlock(&l->mutex); return(v); } void * list_find_first (List l, ListFindF f, void *key) { ListNode p; void *v = NULL; assert(l != NULL); assert(f != NULL); assert(key != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); for (p=l->head; p; p=p->next) { if (f(p->data, key)) { v = p->data; break; } } list_mutex_unlock(&l->mutex); return(v); } int list_delete_all (List l, ListFindF f, void *key) { ListNode *pp; void *v; int n = 0; assert(l != NULL); assert(f != NULL); assert(key != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); pp = &l->head; while (*pp) { if (f((*pp)->data, key)) { if ((v = list_node_destroy(l, pp))) { if (l->fDel) l->fDel(v); n++; } } else { pp = &(*pp)->next; } } list_mutex_unlock(&l->mutex); return(n); } int list_for_each (List l, ListForF f, void *arg) { ListNode p; int n = 0; assert(l != NULL); assert(f != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); for (p=l->head; p; p=p->next) { n++; if (f(p->data, arg) < 0) { n = -n; break; } } list_mutex_unlock(&l->mutex); return(n); } void list_sort (List l, ListCmpF f) { /* Note: Time complexity O(n^2). */ ListNode *pp, *ppPrev, *ppPos, pTmp; ListIterator i; assert(l != NULL); assert(f != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); if (l->count > 1) { ppPrev = &l->head; pp = &(*ppPrev)->next; while (*pp) { if (f((*pp)->data, (*ppPrev)->data) < 0) { ppPos = &l->head; while (f((*pp)->data, (*ppPos)->data) >= 0) ppPos = &(*ppPos)->next; pTmp = (*pp)->next; (*pp)->next = *ppPos; *ppPos = *pp; *pp = pTmp; if (ppPrev == ppPos) ppPrev = &(*ppPrev)->next; } else { ppPrev = pp; pp = &(*pp)->next; } } l->tail = pp; for (i=l->iNext; i; i=i->iNext) { assert(i->magic == LIST_MAGIC); i->pos = i->list->head; i->prev = &i->list->head; } } list_mutex_unlock(&l->mutex); return; } void * list_push (List l, void *x) { void *v; assert(l != NULL); assert(x != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = list_node_create(l, &l->head, x); list_mutex_unlock(&l->mutex); return(v); } void * list_pop (List l) { void *v; assert(l != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = list_node_destroy(l, &l->head); list_mutex_unlock(&l->mutex); return(v); } void * list_peek (List l) { void *v; assert(l != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = (l->head) ? l->head->data : NULL; list_mutex_unlock(&l->mutex); return(v); } void * list_enqueue (List l, void *x) { void *v; assert(l != NULL); assert(x != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = list_node_create(l, l->tail, x); list_mutex_unlock(&l->mutex); return(v); } void * list_dequeue (List l) { void *v; assert(l != NULL); list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); v = list_node_destroy(l, &l->head); list_mutex_unlock(&l->mutex); return(v); } ListIterator list_iterator_create (List l) { ListIterator i; assert(l != NULL); if (!(i = list_iterator_alloc())) return(lsd_nomem_error(__FILE__, __LINE__, "list iterator create")); i->list = l; list_mutex_lock(&l->mutex); assert(l->magic == LIST_MAGIC); i->pos = l->head; i->prev = &l->head; i->iNext = l->iNext; l->iNext = i; assert(i->magic = LIST_MAGIC); /* set magic via assert abuse */ list_mutex_unlock(&l->mutex); return(i); } void list_iterator_reset (ListIterator i) { assert(i != NULL); assert(i->magic == LIST_MAGIC); list_mutex_lock(&i->list->mutex); assert(i->list->magic == LIST_MAGIC); i->pos = i->list->head; i->prev = &i->list->head; list_mutex_unlock(&i->list->mutex); return; } void list_iterator_destroy (ListIterator i) { ListIterator *pi; assert(i != NULL); assert(i->magic == LIST_MAGIC); list_mutex_lock(&i->list->mutex); assert(i->list->magic == LIST_MAGIC); for (pi=&i->list->iNext; *pi; pi=&(*pi)->iNext) { assert((*pi)->magic == LIST_MAGIC); if (*pi == i) { *pi = (*pi)->iNext; break; } } list_mutex_unlock(&i->list->mutex); assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ list_iterator_free(i); return; } void * list_next (ListIterator i) { ListNode p; assert(i != NULL); assert(i->magic == LIST_MAGIC); list_mutex_lock(&i->list->mutex); assert(i->list->magic == LIST_MAGIC); if ((p = i->pos)) i->pos = p->next; if (*i->prev != p) i->prev = &(*i->prev)->next; list_mutex_unlock(&i->list->mutex); return(p ? p->data : NULL); } void * list_insert (ListIterator i, void *x) { void *v; assert(i != NULL); assert(x != NULL); assert(i->magic == LIST_MAGIC); list_mutex_lock(&i->list->mutex); assert(i->list->magic == LIST_MAGIC); v = list_node_create(i->list, i->prev, x); list_mutex_unlock(&i->list->mutex); return(v); } void * list_find (ListIterator i, ListFindF f, void *key) { void *v; assert(i != NULL); assert(f != NULL); assert(key != NULL); assert(i->magic == LIST_MAGIC); while ((v=list_next(i)) && !f(v,key)) {;} return(v); } void * list_remove (ListIterator i) { void *v = NULL; assert(i != NULL); assert(i->magic == LIST_MAGIC); list_mutex_lock(&i->list->mutex); assert(i->list->magic == LIST_MAGIC); if (*i->prev != i->pos) v = list_node_destroy(i->list, i->prev); list_mutex_unlock(&i->list->mutex); return(v); } int list_delete (ListIterator i) { void *v; assert(i != NULL); assert(i->magic == LIST_MAGIC); if ((v = list_remove(i))) { if (i->list->fDel) i->list->fDel(v); return(1); } return(0); } static void * list_node_create (List l, ListNode *pp, void *x) { /* Inserts data pointed to by [x] into list [l] after [pp], * the address of the previous node's "next" ptr. * Returns a ptr to data [x], or NULL if insertion fails. * This routine assumes the list is already locked upon entry. */ ListNode p; ListIterator i; assert(l != NULL); assert(l->magic == LIST_MAGIC); assert(list_mutex_is_locked(&l->mutex)); assert(pp != NULL); assert(x != NULL); if (!(p = list_node_alloc())) return(lsd_nomem_error(__FILE__, __LINE__, "list node create")); p->data = x; if (!(p->next = *pp)) l->tail = &p->next; *pp = p; l->count++; for (i=l->iNext; i; i=i->iNext) { assert(i->magic == LIST_MAGIC); if (i->prev == pp) i->prev = &p->next; else if (i->pos == p->next) i->pos = p; assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next)); } return(x); } static void * list_node_destroy (List l, ListNode *pp) { /* Removes the node pointed to by [*pp] from from list [l], * where [pp] is the address of the previous node's "next" ptr. * Returns the data ptr associated with list item being removed, * or NULL if [*pp] points to the NULL element. * This routine assumes the list is already locked upon entry. */ void *v; ListNode p; ListIterator i; assert(l != NULL); assert(l->magic == LIST_MAGIC); assert(list_mutex_is_locked(&l->mutex)); assert(pp != NULL); if (!(p = *pp)) return(NULL); v = p->data; if (!(*pp = p->next)) l->tail = pp; l->count--; for (i=l->iNext; i; i=i->iNext) { assert(i->magic == LIST_MAGIC); if (i->pos == p) i->pos = p->next, i->prev = pp; else if (i->prev == &p->next) i->prev = pp; assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next)); } list_node_free(p); return(v); } static List list_alloc (void) { return(list_alloc_aux(sizeof(struct list), &list_free_lists)); } static void list_free (List l) { list_free_aux(l, &list_free_lists); return; } static ListNode list_node_alloc (void) { return(list_alloc_aux(sizeof(struct listNode), &list_free_nodes)); } static void list_node_free (ListNode p) { list_free_aux(p, &list_free_nodes); return; } static ListIterator list_iterator_alloc (void) { return(list_alloc_aux(sizeof(struct listIterator), &list_free_iterators)); } static void list_iterator_free (ListIterator i) { list_free_aux(i, &list_free_iterators); return; } static void * list_alloc_aux (int size, void *pfreelist) { /* Allocates an object of [size] bytes from the freelist [*pfreelist]. * Memory is added to the freelist in chunks of size LIST_ALLOC. * Returns a ptr to the object, or NULL if the memory request fails. */ void **px; void **pfree = pfreelist; void **plast; assert(sizeof(char) == 1); assert(size >= sizeof(void *)); assert(pfreelist != NULL); assert(LIST_ALLOC > 0); list_mutex_lock(&list_free_lock); if (!*pfree) { if ((*pfree = malloc(LIST_ALLOC * size))) { px = *pfree; plast = (void **) ((char *) *pfree + ((LIST_ALLOC - 1) * size)); while (px < plast) *px = (char *) px + size, px = *px; *plast = NULL; } } if ((px = *pfree)) *pfree = *px; else errno = ENOMEM; list_mutex_unlock(&list_free_lock); return(px); } static void list_free_aux (void *x, void *pfreelist) { /* Frees the object [x], returning it to the freelist [*pfreelist]. */ void **px = x; void **pfree = pfreelist; assert(x != NULL); assert(pfreelist != NULL); list_mutex_lock(&list_free_lock); *px = *pfree; *pfree = px; list_mutex_unlock(&list_free_lock); return; } #ifndef NDEBUG #ifdef WITH_PTHREADS static int list_mutex_is_locked (pthread_mutex_t *mutex) { /* Returns true if the mutex is locked; o/w, returns false. */ int rc; assert(mutex != NULL); rc = pthread_mutex_trylock(mutex); return(rc == EBUSY ? 1 : 0); } #endif /* WITH_PTHREADS */ #endif /* !NDEBUG */ pdsh-2.36/src/common/xpoll.h0000664€^–Á €^–Á 0000000503215131211226024074 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _XPOLL_H #define _XPOLL_H /* Input & Result events */ #define XPOLLREAD 0x0001 /* fd can be read */ #define XPOLLWRITE 0x0002 /* fd can be written to */ /* Result only events */ #define XPOLLINVAL 0x0010 /* invalid fd passed in */ #define XPOLLERR 0x0020 /* error occurred on this fd */ struct xpollfd { int fd; /* file descriptor to check */ short events; /* events to check for */ short revents; /* resulting events occuring on this fd */ }; /* * xpoll() * - Wrapper API around poll() and select(). * * Input: * xfds - pointer to array of xfds structures * nfds - number of structures in xfds array * timeout - timeout length to poll or select. * if timeout < 0 - poll infinitely * if timeout == 0 - return immediately * if timeout > 0 - poll this number of seconds * * Output: * Number of file descriptors in which revents is modified. On error, * -1 is returned and errno set to the appropriate error * * Additional Notes: * - Any invalid bits stored in events are ignored * - revents is cleared before any revents are set * - XPOLLINVAL and XPOLLERR are not mutually exclusive with * XPOLLREAD and XPOLLWRITE. */ int xpoll(struct xpollfd *xfds, int nfds, int timeout); #endif /* _XPOLL_H */ pdsh-2.36/src/common/xstring.h0000664€^–Á €^–Á 0000000325715131211226024443 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _XSTRING_INCLUDED #define _XSTRING_INCLUDED #include void xstrcln(char *, char *); char *xstrduplicate(char *str); void xstrcat(char **str1, char *str2); void xstrcpy(char **str1, char *str2); void xstrcatchar(char **str1, char c); void xstrerrorcat(char **str1); char *xbasename(char *path); #endif /* _XSTRING_INCLUDED */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/common/macros.h0000664€^–Á €^–Á 0000000407015131211226024223 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _MACROS_INCLUDED #define _MACROS_INCLUDED #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_PTHREAD_H #include #endif #include #define LINEBUFSIZE 2048 #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif #ifndef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #define IP_ADDR_LEN 4 /* XXX */ #if !HAVE_PTHREAD_SIGMASK && HAVE_SIGTHREADMASK #define pthread_sigmask(x, y, z) sigthreadmask(x, y, z) #endif #ifndef _BOOL_DEFINED # define _BOOL_DEFINED # if __STDC_VERSION__ < 202311L # if !defined (true) && !defined (false) typedef enum { false, true } bool; # endif # endif #endif #endif /* !_MACROS_INCLUDED */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/0000775€^–Á €^–Á 0000000000015131211226022233 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/src/pdsh/Makefile.am0000664€^–Á €^–Á 0000000451015131211226024267 0ustar arif.ali@canonical.comarif.ali@canonical.com##***************************************************************************** ## $Id$ ##***************************************************************************** ## Process this file with automake to produce Makefile.in. ##***************************************************************************** include $(top_srcdir)/config/Make-inc.mk AM_CPPFLAGS = -I$(top_srcdir) noinst_PROGRAMS = pdsh bin_PROGRAMS = pdsh.inst if WITH_STATIC_MODULES MODULE_LIBS = $(top_builddir)/src/modules/libmods.la else MODULE_FLAGS = -export-dynamic $(AIX_PDSH_LDFLAGS) -ldl endif pdsh_LDADD = $(READLINE_LIBS) \ $(top_builddir)/src/common/libcommon.la pdsh_LDFLAGS = $(MODULE_LIBS) $(MODULE_FLAGS) pdsh_inst_LDADD = $(pdsh_LDADD) pdsh_inst_LDFLAGS = $(pdsh_LDFLAGS) pdsh_SOURCES = $(PDSH_SOURCES) pdsh_inst_SOURCES = $(pdsh_SOURCES) nodist_pdsh_SOURCES = testconfig.c nodist_pdsh_inst_SOURCES = config.c PDSH_SOURCES = \ main.c \ dsh.c \ dsh.h \ mod.c \ mod.h \ rcmd.c \ rcmd.h \ opt.c \ opt.h \ privsep.c \ privsep.h \ pcp_server.c \ pcp_server.h \ pcp_client.c \ pcp_client.h \ testcase.c \ wcoll.c \ wcoll.h \ cbuf.c \ cbuf.h config.c: $(top_builddir)/config.h @(echo "char *pdsh_version = \"$(PDSH_VERSION_FULL)\";";\ echo "char *pdsh_module_dir = \"$(pkglibdir)\";"\ )> config.c testconfig.c: $(top_builddir)/config.h @(echo "char *pdsh_version = \"$(PDSH_VERSION_FULL)\";";\ moddir=`cd $(top_builddir)/src/modules/.libs && pwd`; \ echo "char *pdsh_module_dir = \"$$moddir\";"\ )> testconfig.c install-exec-hook: -mv $(DESTDIR)$(bindir)/pdsh.inst $(DESTDIR)$(bindir)/pdsh @echo "chown root $(DESTDIR)$(bindir)/pdsh" @chown root $(DESTDIR)$(bindir)/pdsh ||\ echo "Unable to chown pdsh to root" @cp -p $(DESTDIR)$(bindir)/pdsh $(DESTDIR)$(bindir)/pdcp ||\ echo "Unable to copy pdsh to pdcp" @cp -p $(DESTDIR)$(bindir)/pdsh $(DESTDIR)$(bindir)/rpdcp ||\ echo "Unable to copy pdsh to rpdcp" uninstall-local: $(RM) $(DESTDIR)$(bindir)/pdcp $(RM) $(DESTDIR)$(bindir)/rpdcp $(RM) $(DESTDIR)$(bindir)/pdsh MOSTLYCLEANFILES = \ config.c \ testconfig.c pdsh-2.36/src/pdsh/privsep.c0000664€^–Á €^–Á 0000001640515131211226024075 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include #endif #include #if HAVE_SYS_UIO_H # include #endif #include #include #include #include #include #include /* rresvport */ #include #include #include #include "src/common/err.h" #include "src/common/fd.h" #define CONTROLLEN sizeof (struct cmsghdr) + sizeof (int) static pthread_mutex_t privsep_mutex = PTHREAD_MUTEX_INITIALIZER; static pid_t cpid; static int client_fd = -1; static int server_fd = -1; uid_t user_uid = -1; gid_t user_gid = -1; uid_t priv_uid = -1; gid_t priv_gid = -1; /* * Top 16 bits of port sent to privsep server is used to encode * the address family (for rresvport_af()). Currently, value is * either AF_INET AF_INET6 (if zero, then AF_INET). */ static int privsep_get_family (int *lport) { int family = (*lport>>16) & 0xffff; /* * Mask out family from lport: */ *lport &= 0xffff; return (family ? family : AF_INET); } static int privsep_set_family (int *lport, int family) { if (family > 0xffff) return (-1); *lport |= (family<<16); return (0); } static int create_socketpair (void) { int pfds[2]; if (socketpair (AF_UNIX, SOCK_STREAM, 0, pfds) < 0) { err ("%p: socketpair failed in privilege separation: %m\n"); return -1; } client_fd = pfds[0]; server_fd = pfds[1]; return (0); } static void drop_privileges (void) { user_uid = getuid (); priv_uid = geteuid (); user_gid = getgid (); priv_gid = getegid (); #ifdef _POSIX_SAVED_IDS if (seteuid (user_uid) < 0 || setegid (user_gid) < 0) errx ("%p: seteuid/setegid: %m\n"); #else if (setreuid (priv_uid, user_uid) < 0 || setregid (priv_gid, user_gid < 0)) errx ("%p: setreuid/setregid: %m\n"); #endif } static int send_rresvport (int pipefd, int fd, int lport) { struct iovec iov[1]; struct msghdr msg; #if !HAVE_MSGHDR_ACCRIGHTS struct cmsghdr *cmsg; char * buf[CONTROLLEN]; cmsg = (struct cmsghdr *) &buf; #endif memset (&msg, 0, sizeof (msg)); iov->iov_base = (void *) &lport; iov->iov_len = sizeof (lport); msg.msg_iov = iov; msg.msg_iovlen = 1; #if HAVE_MSGHDR_ACCRIGHTS if (fd < 0) { msg.msg_accrights = NULL; msg.msg_accrightslen = 0; } else { msg.msg_accrights = (caddr_t) &fd; msg.msg_accrightslen = sizeof (int); } #else if (fd < 0) { msg.msg_control = NULL; msg.msg_controllen = 0; lport = -1; } else { cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CONTROLLEN; msg.msg_control = (caddr_t) cmsg; msg.msg_controllen = CONTROLLEN; * (int *) CMSG_DATA(cmsg) = fd; } #endif if (sendmsg (pipefd, &msg, 0) != sizeof (int)) { err ("%p: privsep: sendmsg: %m\n"); return (-1); } return (0); } static int recv_rresvport (int pipefd, int *lptr) { int fd = -1; struct iovec iov[1]; struct msghdr msg; #if !HAVE_MSGHDR_ACCRIGHTS struct cmsghdr *cmsg; char * buf[CONTROLLEN]; cmsg = (struct cmsghdr *) &buf; #endif memset (&msg, 0, sizeof (msg)); iov->iov_base = (void *) lptr; iov->iov_len = sizeof (int); msg.msg_iov = iov; msg.msg_iovlen = 1; #if HAVE_MSGHDR_ACCRIGHTS msg.msg_accrights = (caddr_t) &fd; msg.msg_accrightslen = sizeof (int); #else /* !HAVE_MSGHDR_ACCRIGHTS */ cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CONTROLLEN; msg.msg_control = (caddr_t) cmsg; msg.msg_controllen = CONTROLLEN; #endif if (recvmsg (pipefd, &msg, 0) < 0) err ("%p: privsep: recvmsg: %m\n"); if (*lptr < 0) return (-1); #if !HAVE_MSGHDR_ACCRIGHTS fd = *(int *) CMSG_DATA (cmsg); #endif return (fd); } static int p_rresvport_af (int *port, int family) { #if HAVE_RRESVPORT_AF return rresvport_af (port, family); #else /* Family must be AF_INET */ if (family != AF_INET) err ("%p: rresvport called with family != AF_INET!\n"); /* ignore family != AF_INET */ return rresvport (port); #endif } static int privsep_server (void) { int rc; int lport; close (client_fd); /* * for each request on server_fd create a reserved port and * send the created fd back to the client. */ while ((rc = read (server_fd, &lport, sizeof (lport))) > 0) { int family = privsep_get_family (&lport); int s = p_rresvport_af (&lport, family); send_rresvport (server_fd, s, lport); close (s); } if (rc < 0) err ("%p: privsep: server read failed: %m\n"); close (server_fd); return (0); } static int create_privileged_child (void) { if ((cpid = fork ()) < 0) { err ("%p: fork failed in privilege separation: %m\n"); return -1; } if (cpid == 0) { /* * Child: become privilege port server. */ privsep_server (); exit (0); } /* * Parent: drop privileges, close server_fd and return. */ close (server_fd); drop_privileges (); return (0); } int privsep_init (void) { if (geteuid() == getuid()) return 0; if (create_socketpair () < 0) return -1; return (create_privileged_child ()); } int privsep_fini (void) { int status; if (client_fd < 0 || cpid < 0) return (0); close (client_fd); if (waitpid (cpid, &status, 0) < 0) { err ("%p: failed to reap priveleged child: %m\n"); return (-1); } if (status) err ("%p: privileged chiled exited with status %d\n", status); return (0); } int privsep_rresvport_af (int *lport, int family) { int s = -1; if (client_fd < 0) return (p_rresvport_af (lport, family)); if (privsep_set_family (lport, family) < 0) { err ("%p: privsep_rresvport_af: Invalid family %d\n", family); errno = EINVAL; return (-1); } if ((errno = pthread_mutex_lock (&privsep_mutex))) errx ("%p: %s:%d: mutex_lock: %m\n", __FILE__, __LINE__); if (write (client_fd, lport, sizeof (*lport)) < 0) { err ("%p: privsep: client write: %m\n"); goto out; } s = recv_rresvport (client_fd, lport); out: if ((errno = pthread_mutex_unlock (&privsep_mutex))) errx ("%p: %s:%d: mutex_unlock: %m\n", __FILE__, __LINE__); return (s); } int privsep_rresvport (int *lport) { return privsep_rresvport_af (lport, AF_INET); } pdsh-2.36/src/pdsh/mod.h0000664€^–Á €^–Á 0000001537315131211226023174 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _MOD_H #define _MOD_H #if HAVE_CONFIG_H # include #endif #include "src/pdsh/opt.h" #define DEFAULT_MODULE_PRIORITY 100 typedef struct module_components * mod_t; /* * Initialize the module loader interface * Returns 0 for Success and -1 for Failure. */ int mod_init(void); /* * Finalize and close the module loader interface. * Cycles through list of loaded modules and runs each modules * "exit" routine if one was exported. Then frees memory associated * with the module and unloads it. * * Returns 0 for Success and -1 for Failure. */ int mod_exit(void); /* * Load all modules from specified directory. * Directory must be owned by the current user and not writable * by any other user. After successfully loading each module, * the module's "init" routine is called and module command line * options are registered. The module is not loaded if init * returns < 0 or any module option cannot be registered. * * If modules are being compiled statically, the directory argument * is ignored. * * Returns 0 for Success and -1 for Failure. */ int mod_load_modules(const char *dir, opt_t *opt); /* * List information about all loaded modules to stdout. */ void mod_list_module_info(void); /* * Traverse through loaded modules and attempt to process * option `opt' with argument `arg.' * * Note: Only one module exporting a given option can be loaded * at a time. This is enforced on a first-come-first-served basis. */ int mod_process_opt(opt_t *pdsh_opts, int opt, char *arg); /* * Traverses list of loaded modules, calling any exported "read_wcoll" * routines. Appends any returned results onto opt->wcoll. * * This routine should only be called from within pdsh/opt.c after * option processing is complete, but before mod_postop(). * * Returns -1 for failure, 0 for success. * */ int mod_read_wcoll(opt_t *pdsh_opts); /* * Traverse list of loaded modules and call any exported "postop" routines. * "Postop" routines in modules are typically used to filter the * working collective, as in the -v option with nodeupdown, or -i * with genders. * * Returns the total number of errors. * */ int mod_postop(opt_t *pdsh_opts); /* * Search list of loaded modules for a module with given type and name. * Returns the module if found, NULL if no match. */ mod_t mod_get_module(const char *type, const char *name); /* * Return the number of loaded modules of type `"type." Returns the * total number of modules if type is NULL. */ int mod_count(char *type); /* * Build list of module names of type "type" * List contains all module names if type is NULL. */ List mod_get_module_names(char *type); /* * Build list of module names that are loaded but not initialized */ List mod_get_uninitialized_module_names (char *type); /* * Print all options provided by modules * Justify option description starting on given column. */ void mod_print_all_options(int column); /* * Print options for module "mod" */ void mod_print_options(mod_t mod, int descr_column); /* * Functions that may be exported by any pdsh module * via a pdsh_module_operations structure. */ typedef int (*ModInitF) (void); typedef int (*ModExitF) (void); typedef hostlist_t (*ModReadWcollF) (opt_t *); typedef int (*ModPostOpF) (opt_t *); /* * Functions that may be exported by any rcmd module * via a pdsh_rcmd_operations structure. */ typedef int (*RcmdInitF) (opt_t *); typedef int (*RcmdSigF) (int, void *, int); typedef int (*RcmdF) (char *, char *, char *, char *, char *, int, int *, void **); typedef int (*RcmdDestroyF) (void *); /* * Module accessor functions. Return module name, type, and * look up additional exported symbols in given module. * * All return a pointer to the desired value if successful, or NULL * on failure. */ char * mod_get_name(mod_t mod); char * mod_get_type(mod_t mod); RcmdInitF mod_get_rcmd_init(mod_t mod); RcmdSigF mod_get_rcmd_signal(mod_t mod); RcmdF mod_get_rcmd(mod_t mod); RcmdDestroyF mod_get_rcmd_destroy(mod_t mod); /* * Store all module operations of a module */ struct pdsh_module_operations { ModInitF init; /* Called just after module is loaded */ ModExitF exit; /* Called just before module unloaded */ ModReadWcollF read_wcoll; /* Called if wcoll is not initialized at end of option processing. First wcoll returned by a module will be used. */ ModPostOpF postop; /* Called after argv option processing */ }; /* * Stores all rcmd operations of a module */ struct pdsh_rcmd_operations { RcmdInitF rcmd_init; RcmdSigF rcmd_signal; RcmdF rcmd; RcmdDestroyF rcmd_destroy; }; /* * Stores all information about a module */ struct pdsh_module { char *type; /* module type, e.g. Jedi */ char *name; /* module name, e.g. Yoda */ char *author; /* module author, e.g. George Lucas */ char *descr; /* module description, e.g. "Run pdsh with the force */ int personality; /* personality mask for module (DSH, PCP, or DSH|PCP */ struct pdsh_module_operations *mod_ops; struct pdsh_rcmd_operations *rcmd_ops; struct pdsh_module_option *opt_table; }; #endif /* !_MOD_H */ /* * vi: tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/dsh.c0000664€^–Á €^–Á 0000010367515131211226023171 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Theory of operation: * * The main thread creates a separate thread for each rsh/krsh/etc. which lasts * the life of the connection (establishing it, copying remote stdout/stderr * to local stdout/stderr and closing the connection). The main thread makes * sure that at most fanout number of threads are active at any given time. * When a thread terminates, it signals a condition variable (threadcount_cond) * which causes the main thread to start another rsh/krsh/etc. thread to take * its place. * * We rely on implicit stdio locking to enable us to write lines to * stdout/stderr from multiple threads concurrently without getting the lines * all mixed up. * * A special watchdog thread sends SIGLARM to any threads that have been in * the DSH_RCMD state (usually connect() in rcmd/k4cmd/etc.) for more than * CONNECT_TIMEOUT seconds. SIGALRM is masked everywhere but during connect(). * Similarly, if a command timeout is specified (default is none), the watchdog * thread sends SIGALRM to threads that have been in the DSH_READING state * too long. * * When a user types ^C, the resulting SIGINT invokes a handler which lists * threads in the DSH_READING state. If another SIGINT is received within * INTR_TIME secs (default 1 sec), pdsh terminates. * * All the state for a thread is contained in thd_t struct. An array of * these structures is declared globally so signal handlers can access. * The array is initialized by dsh() below, and the rsh() function for each * thread is passed the element corresponding to one connection. */ #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_PTHREAD_H #include #endif #include #include #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #if HAVE_STRINGS_H #include /* FD_SET calls bzero on aix */ #endif #include #include #include /* gethostbyname */ #include /* get/setrlimit */ #ifndef PTHREAD_STACK_MIN # define PTHREAD_STACK_MIN ((size_t) sysconf (_SC_THREAD_STACK_MIN)) #endif #define dsh_mutex_lock(pmutex) \ do { \ if ((errno = pthread_mutex_lock (pmutex))) \ errx ("%s:%d: mutex_lock: %m", __FILE__, __LINE__); \ } while (0) #define dsh_mutex_unlock(pmutex) \ do { \ if ((errno = pthread_mutex_unlock (pmutex))) \ errx ("%s:%d: mutex_unlock: %m", __FILE__, __LINE__); \ } while (0) /* set the default stacksize for threads to 128k */ #define DSH_THREAD_STACKSIZE 128*1024 #include "src/common/list.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/err.h" #include "src/common/xpoll.h" #include "src/common/fd.h" #include "dsh.h" #include "opt.h" #include "pcp_client.h" #include "pcp_server.h" #include "wcoll.h" #include "rcmd.h" static int debug = 0; /* * Mutex and condition variable for implementing `fanout'. When a thread * terminates, it decrements threadcount and signals threadcount_cond. * The main, once it has spawned the fanout number of threads, suspends itself * until a thread termintates. */ static pthread_mutex_t threadcount_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t threadcount_cond = PTHREAD_COND_INITIALIZER; static int threadcount = 0; /* * This array is initialized in dsh(). It contains an entry for every * potentially active thread, though only the fanout number will be active * at once. It is out here in global land so the signal handler for ^C can * report which hosts are blocked. */ static thd_t *t; static pthread_mutex_t thd_mutex = PTHREAD_MUTEX_INITIALIZER; /* * Timeout values, initialized in dsh(), used in _wdog(). */ static int connect_timeout, command_timeout; /* * Terminate on a single SIGINT (batch mode) */ static int sigint_terminates = 0; /* * Buffered output prototypes: */ typedef void (* out_f) (const char *, ...); static int _do_output (int fd, cbuf_t cb, out_f outf, bool read_rc, thd_t *t); static int _handle_rcmd_stderr (thd_t *t); static int _handle_rcmd_stdout (thd_t *t); static void _flush_output (cbuf_t cb, out_f outf, thd_t *t); /* * Emulate signal() but with BSD semantics (i.e. don't restore signal to * SIGDFL prior to executing handler). */ typedef void SigFunc(int signal); static SigFunc *_xsignal(int signal, SigFunc *handler) { struct sigaction sa, old_sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, signal); sa.sa_flags = 0; sigaction(signal, &sa, &old_sa); return ((SigFunc *) old_sa.sa_handler); } /* * SIGALRM handler. This is just a stub because we are really interested * in interrupting connect() in rcmd/k4cmd/etc. or xpoll() below and * causing them to return EINTR. */ static void _alarm_handler(int dummy) { } /* * Helper function for handle_sigint(). Lists the status of all connected * threads. */ static void _list_slowthreads(void) { int i; time_t ttl; dsh_mutex_lock(&thd_mutex); for (i = 0; t[i].host != NULL; i++) { switch (t[i].state) { case DSH_READING: err("%p: %S: command in progress", t[i].host); ttl = t[i].connect + command_timeout - time(NULL); if (debug && command_timeout) err(" (timeout in %d secs)\n", ttl); else err("\n"); break; case DSH_RCMD: ttl = t[i].start + connect_timeout - time(NULL); err("%p: %S: connecting", t[i].host, ttl); if (debug && connect_timeout) err(" (timeout in %d secs)\n", ttl); else err("\n"); break; case DSH_NEW: if (debug) err("%p: %S: [new]\n", t[i].host); break; case DSH_FAILED: if (debug) err("%p: %S: [failed]\n", t[i].host); break; case DSH_DONE: if (debug) err("%p: %S: [done]\n", t[i].host); break; case DSH_CANCELED: if (debug) err("%p: %S: [canceled]\n", t[i].host); break; } } dsh_mutex_unlock(&thd_mutex); } /* * Block SIGINT SIGTSTP SIGCHLD n this thread. */ static void _mask_signals(int how) { sigset_t blockme; assert ((how == SIG_BLOCK) || (how == SIG_UNBLOCK)); sigemptyset(&blockme); sigaddset(&blockme, SIGINT); sigaddset(&blockme, SIGTSTP); sigaddset(&blockme, SIGCHLD); pthread_sigmask(how, &blockme, NULL); } /* * If the underlying rsh mechanism supports it, forward signals to remote * process. */ static void _fwd_signal(int signum) { int i; dsh_mutex_lock(&thd_mutex); for (i = 0; t[i].host != NULL; i++) { if (t[i].state == DSH_READING) rcmd_signal(t[i].rcmd, signum); } dsh_mutex_unlock(&thd_mutex); } static int _thd_connect_timeout (thd_t *th) { if ((connect_timeout > 0) && (th->start != ((time_t) -1))) { if (th->start + connect_timeout < time (NULL)) return (1); } return (0); } static int _thd_command_timeout (thd_t *th) { if ((command_timeout > 0) && (th->connect != ((time_t) -1))) { if (th->connect + command_timeout < time (NULL)) return (1); } return (0); } /* * Watchdog thread. Send SIGALRM to * - threads in connecting state for too long * - threads in connected state for too long (if selected on command line) * Sleep for two seconds between polls */ static void *_wdog(void *args) { int i; for (;;) { if (t == NULL) /* We're done */ return NULL; for (i = 0; t[i].host != NULL; i++) { switch (t[i].state) { case DSH_RCMD: if (_thd_connect_timeout (&t[i])) pthread_kill(t[i].thread, SIGALRM); break; case DSH_READING: if (_thd_command_timeout (&t[i])) pthread_kill(t[i].thread, SIGALRM); break; case DSH_NEW: case DSH_DONE: case DSH_FAILED: case DSH_CANCELED: break; } } sleep (WDOG_POLL); } return NULL; } /* * Return the h_addr of a hostname, exiting if there is a lookup failure. * name (IN) hostname * addr (OUT) pointer to location where address will be written */ static void _gethost(char *name, char *addr) { struct hostent *hp; if (!(hp = gethostbyname(name))) errx("%p: gethostbyname(\"%S\") failed\n", name); /* assert(hp->h_addrtype == AF_INET); */ assert(IP_ADDR_LEN == hp->h_length); memcpy(addr, hp->h_addr_list[0], IP_ADDR_LEN); } /* * Update thread state to connecting, unless the thread * has been canceled, in which case close fds if they are open * and return DSH_CANCELED. */ static state_t _update_connect_state (thd_t *a) { dsh_mutex_lock(&thd_mutex); a->connect = time(NULL); if (a->state != DSH_CANCELED) a->state = DSH_READING; dsh_mutex_unlock(&thd_mutex); if (a->state == DSH_CANCELED) { if (a->rcmd->fd >= 0) close (a->rcmd->fd); if (a->rcmd->efd >= 0) close (a->rcmd->efd); } return (a->state); } static int _pcp_server (thd_t *th) { struct pcp_server svr[1]; svr->infd = th->rcmd->fd; svr->outfd = svr->infd; svr->preserve = th->pcp_popt; svr->target_is_dir = th->pcp_yopt; svr->outfile = th->outfile_name; return (pcp_server (svr)); } static int _pcp_client (thd_t *th) { struct pcp_client pcp[1]; pcp->infd = th->rcmd->fd; pcp->outfd = pcp->infd; pcp->preserve = th->pcp_popt; pcp->pcp_client = th->pcp_Zopt; pcp->host = th->host; pcp->infiles = th->pcp_infiles; return (pcp_client (pcp)); } static int _parallel_copy (thd_t *th) { int rv = 0; /* * Run threaded pcp server or client */ if (th->pcp_Popt) rv = _pcp_server (th); else rv = _pcp_client (th); if ((!th->pcp_Popt && rv < 0) || (th->pcp_Popt)) { /* * Copy any pending stderr to user * (ignore errors) * * Notes: * * If the pcp_client was executed, stderr is unlikely b/c * the pcp_server does not write to stderr and all * pdsh/pdcp option parameters have been checked for * correctness. However, under a disaster situation, * pdsh/pdcp core code itself could have an error and * write to stderr. This call should only be executed if * there is an error with the pcp_client. Since the * client is responsible for closing/finishing the * connection the below could spin if an error wasn't * available for reading. * * If the pcp_server was executed, stderr output could * come from the pdsh/pdcp core code. For example, if a * file has a permission denied error, it would be caught * in the options checks and output to stderr. Even if * there is no stderr, the below cannot spin b/c the * pcp_client on the remote node will close the * connection when the copy (or error output) is * complete. */ while (_handle_rcmd_stderr (th) > 0) ; _flush_output (th->errbuf, (out_f) err, th); } close(th->rcmd->fd); if (th->dsh_sopt) close(th->rcmd->efd); return (rv); } /* * Rcp thread. One per remote connection. * Arguments are pointer to thd_t entry defined above. */ static void *_rcp_thread(void *args) { thd_t *a = (thd_t *) args; int result = DSH_DONE; /* the desired outcome */ int rc; char *rcpycmd = NULL; #if HAVE_MTSAFE_GETHOSTBYNAME if (a->rcmd->opts->resolve_hosts) _gethost(a->host, a->addr); #endif a->start = time(NULL); dsh_mutex_lock(&thd_mutex); a->state = DSH_RCMD; dsh_mutex_unlock(&thd_mutex); /* For reverse copy, the host needs to be appended to the end of the command */ if (a->pcp_Popt) { xstrcat(&rcpycmd, a->cmd); xstrcat(&rcpycmd, " "); xstrcat(&rcpycmd, a->host); } rcmd_connect (a->rcmd, a->host, a->addr, a->luser, a->ruser, (rcpycmd) ? rcpycmd : a->cmd, a->nodeid, a->dsh_sopt); if (rcpycmd) Free((void **) &rcpycmd); if (a->rcmd->fd == -1) result = DSH_FAILED; else if (_update_connect_state(a) != DSH_CANCELED) _parallel_copy(a); /* update status */ dsh_mutex_lock(&thd_mutex); a->state = result; a->finish = time(NULL); dsh_mutex_unlock(&thd_mutex); rc = rcmd_destroy (a->rcmd); if ((a->rc == 0) && (rc > 0)) a->rc = rc; /* Signal dsh() so another thread can replace us */ dsh_mutex_lock(&threadcount_mutex); threadcount--; pthread_cond_signal(&threadcount_cond); dsh_mutex_unlock(&threadcount_mutex); return NULL; } /* * Extract a remote command return code embedded in output, returning * the code as an integer and truncating the line. */ static int _extract_rc(char *buf) { int ret = 0; char *p = strstr(buf, RC_MAGIC); if (p) { if (buf[strlen(buf) - 1] == '\n' && p != buf) *p++ = '\n'; *p = '\0'; p += strlen(RC_MAGIC); ret = atoi(p); } return ret; } static void _flush_lines (cbuf_t cb, out_f outf, bool read_rc, thd_t *th) { char c; int n; /* * Use cbuf_peek_line with a single character buffer in order to * get the buffer size needed for the next line (if any). */ while ((n = cbuf_peek_line (cb, &c, 1, 1))) { char *buf; if (n < 0) { err ("%p: %S: Failed to peek line: %m\n", th->host); break; } /* * Allocate enough space for line plus NUL character, * then actually read line data into buffer: */ buf = Malloc (n + 1); if ((n = cbuf_read (cb, buf, n))) { if (n < 0) { err ("%p: %S: Failed to read line from buffer: %m\n", th->host); break; } if (read_rc) th->rc = _extract_rc (buf); if (strlen (buf) > 0) { /* * We are careful to use a single call to write the line * to the output stream to avoid interleaved lines of * output. */ if (th->labels) outf ("%S: %s", th->host, buf); else outf ("%s", buf); fflush (NULL); } } Free ((void **)&buf); } } static int _do_output (int fd, cbuf_t cb, out_f outf, bool read_rc, thd_t *t) { int rc; int dropped = 0; if ((rc = cbuf_write_from_fd (cb, fd, -1, &dropped)) < 0) { if (errno == EAGAIN) return (1); err ("%p: %S: read: %m\n", t->host); return (-1); } _flush_lines (cb, outf, read_rc, t); return (rc); } static void _flush_output (cbuf_t cb, out_f outf, thd_t *th) { int n; bool labeled = false; char buf[8192]; _flush_lines (cb, outf, false, t); /* In case no newline at end of buffer, grab the rest of data */ while ((n = cbuf_read (cb, buf, sizeof (buf) - 1)) > 0) { buf[n] = '\0'; if (th->labels && !labeled) { outf ("%S: ", th->host); labeled = true; } outf ("%s", buf); } return; } static int _die_if_signalled (thd_t *th) { int sig; if ((sig = (th->rc - 128)) <= 0) return (0); err ("%p: process on host %S killed by signal %d\n", th->host, sig); _fwd_signal (SIGTERM); errx ("%p: terminating all processes.\n"); /* NOTREACHED */ return (0); } static int _handle_rcmd_stdout (thd_t *th) { int rc = _do_output (th->rcmd->fd, th->outbuf, (out_f) out, true, th); if (rc <= 0) { close (th->rcmd->fd); th->rcmd->fd = -1; } return (rc); } static int _handle_rcmd_stderr (thd_t *th) { int rc = _do_output (th->rcmd->efd, th->errbuf, (out_f) err, false, th); if (rc <= 0) { close (th->rcmd->efd); th->rcmd->efd = -1; } return (rc); } /* * Rsh thread. One per remote connection. * Arguments are pointer to thd_t entry defined above. */ static void *_rsh_thread(void *args) { thd_t *a = (thd_t *) args; int rv; int result = DSH_DONE; /* the desired outcome */ struct xpollfd xpfds[2]; int nfds = 1; a->start = time(NULL); #if HAVE_MTSAFE_GETHOSTBYNAME if (a->rcmd->opts->resolve_hosts) _gethost(a->host, a->addr); #endif _xsignal (SIGPIPE, SIG_IGN); /* establish the connection */ dsh_mutex_lock(&thd_mutex); a->state = DSH_RCMD; dsh_mutex_unlock(&thd_mutex); rcmd_connect (a->rcmd, a->host, a->addr, a->luser, a->ruser, a->cmd, a->nodeid, a->dsh_sopt); if (a->rcmd->fd == -1) { result = DSH_FAILED; /* connect failed */ } else if (_update_connect_state(a) != DSH_CANCELED) { fd_set_nonblocking (a->rcmd->fd); memset (xpfds, 0, sizeof (xpfds)); /* prep for poll call */ xpfds[0].fd = a->rcmd->fd; if (a->dsh_sopt) { /* separate stderr */ fd_set_nonblocking (a->rcmd->efd); xpfds[1].fd = a->rcmd->efd; nfds++; } else xpfds[1].fd = -1; xpfds[0].events = xpfds[1].events = POLLIN; #if STDIN_BCAST /* not yet supported */ xpfds[0].events |= POLLOUT; #endif /* * poll / read / report loop. */ while (xpfds[0].fd >= 0 || xpfds[1].fd >= 0) { /* poll (possibility for SIGALRM) */ rv = xpoll(xpfds, nfds, -1); if (rv == -1) { if (errno != EINTR) err("%p: %S: xpoll: %m\n", a->host); else if (_thd_command_timeout (a)) err("%p: %S: command timeout\n", a->host); else continue; /* interrupted by spurious signal */ result = DSH_FAILED; rcmd_signal (a->rcmd, SIGTERM); break; } /* stdout ready or closed ? */ if (xpfds[0].revents & (XPOLLREAD|XPOLLERR)) { if (_handle_rcmd_stdout (a) <= 0) xpfds[0].fd = -1; } /* stderr ready or closed ? */ if (a->dsh_sopt && xpfds[1].revents & (XPOLLREAD|XPOLLERR)) { if (_handle_rcmd_stderr (a) <= 0) xpfds[1].fd = -1; } /* kill parallel job if kill_on_fail and one task was signaled */ if (a->kill_on_fail) _die_if_signalled (a); #if STDIN_BCAST /* not yet supported */ /* stdin ready ? */ if (FD_ISSET(a->rcmd->fd, &writefds)) { } #endif } } /* update status */ dsh_mutex_lock(&thd_mutex); a->state = result; a->finish = time(NULL); dsh_mutex_unlock(&thd_mutex); /* flush any pending output */ _flush_output (a->outbuf, (out_f) out, a); _flush_output (a->errbuf, (out_f) err, a); rv = rcmd_destroy (a->rcmd); if ((a->rc == 0) && (rv > 0)) a->rc = rv; /* if a single qshell thread fails, terminate whole job */ if (a->kill_on_fail && ((a->state == DSH_FAILED) || (a->rc > 0))) { _fwd_signal(SIGTERM); errx("%p: terminating all processes\n"); } /* Signal dsh() so another thread can replace us */ dsh_mutex_lock(&threadcount_mutex); threadcount--; pthread_cond_signal(&threadcount_cond); dsh_mutex_unlock(&threadcount_mutex); return NULL; } #define TIME_T_YEAR 60*60*24*7*52 /* * If debugging, call this to dump thread connect/command times. */ static void _dump_debug_stats(int rshcount) { time_t conTot = 0, conMin = TIME_T_YEAR, conMax = 0; time_t cmdTot = 0, cmdMin = TIME_T_YEAR, cmdMax = 0; int failed = 0; int canceled = 0; int n; for (n = 0; n < rshcount; n++) { if (t[n].state == DSH_FAILED) { failed++; continue; } if (t[n].state == DSH_CANCELED) { canceled++; continue; } assert(t[n].start && t[n].connect && t[n].finish); conTot += t[n].connect - t[n].start; cmdTot += t[n].finish - t[n].connect; conMin = MIN(conMin, t[n].connect - t[n].start); conMax = MAX(conMax, t[n].connect - t[n].start); cmdMin = MIN(cmdMin, t[n].finish - t[n].connect); cmdMax = MAX(cmdMax, t[n].finish - t[n].connect); } if (rshcount > failed) { err("Connect time: Avg: %d sec, Min: %d sec, Max: %d sec\n", conTot / (rshcount - failed), conMin, conMax); err("Command time: Avg: %d sec, Min: %d sec, Max: %d sec\n", cmdTot / (rshcount - failed), cmdMin, cmdMax); } else { err("Connect time: no sucesses\n"); err("Command time: no sucesses\n"); } err("Failures: %d\n", failed); if (canceled) err("Canceled: %d\n", canceled); } /* * Initialize pthread attr pointed to by `attrp' with dsh default * detach state, and stacksize `stacksize.' * * Exits program on failure. */ static int _dsh_attr_init (pthread_attr_t *attrp, int stacksize) { int rc; if (stacksize < PTHREAD_STACK_MIN) stacksize = PTHREAD_STACK_MIN; if ((rc = pthread_attr_init (attrp))) errx ("pthread_attr_init: %s\n", strerror (rc)); if ((rc = pthread_attr_setdetachstate (attrp, PTHREAD_CREATE_DETACHED))) errx ("pthread_attr_setdetachstate: %s\n", strerror (rc)); if ((rc = pthread_attr_setstacksize (attrp, stacksize))) errx ("pthread_attr_setstacksize: %s\n", strerror (rc)); return (0); } /* * Increase nofile limit to maximum if necessary */ static void _increase_nofile_limit (opt_t *opt) { struct rlimit rlim[1]; /* * We'd like to be able to have at least (2*fanout + slop) fds * open at once. */ int nfds = (2 * opt->fanout) + 32; if (getrlimit (RLIMIT_NOFILE, rlim) < 0) { err ("getrlimit: %m\n"); return; } if ((rlim->rlim_cur < rlim->rlim_max) && (rlim->rlim_cur <= nfds)) { rlim->rlim_cur = rlim->rlim_max; if (setrlimit (RLIMIT_NOFILE, rlim) < 0) err ("Unable to increase max no. files: %m"); } return; } static int _thd_init (thd_t *th, opt_t *opt, List pcp_infiles, int i) { th->luser = opt->luser; /* general */ th->ruser = opt->ruser; th->state = DSH_NEW; th->labels = opt->labels; th->nodeid = i; th->cmd = opt->cmd; th->dsh_sopt = opt->separate_stderr; /* dsh-specific */ th->rc = 0; th->pcp_infiles = pcp_infiles; /* pcp-specific */ th->pcp_outfile = opt->outfile_name; th->pcp_popt = opt->preserve; th->pcp_ropt = opt->recursive; th->pcp_yopt = opt->target_is_directory; th->pcp_Popt = opt->reverse_copy; th->pcp_Zopt = opt->pcp_client; th->pcp_progname = opt->progname; th->outfile_name = opt->outfile_name; th->kill_on_fail = opt->kill_on_fail; th->outbuf = cbuf_create (64, 131072); th->errbuf = cbuf_create (64, 131072); if (!(th->rcmd = rcmd_create (th->host))) { th->state = DSH_CANCELED; return (-1); } #if !HAVE_MTSAFE_GETHOSTBYNAME /* if MT-safe, do it in parallel in rsh/rcp threads */ /* gethostbyname_r is not very portable so skip it */ if (th->rcmd->opts->resolve_hosts) _gethost(th->host, th->addr); #endif return (0); } static int _cancel_pending_threads (void) { int n = 0; int i; if (t == NULL) return (0); dsh_mutex_lock (&threadcount_mutex); for (i = 0; t[i].host != NULL; i++) { if ((t[i].state == DSH_NEW) || (t[i].state == DSH_RCMD)) { t[i].state = DSH_CANCELED; ++n; } } err ("%p: Canceled %d pending threads.\n", n); dsh_mutex_unlock (&threadcount_mutex); return (0); } /* * Handle SIGNINT from signals thread. One ^C lists slow threads. * Another ^C within one second aborts the job. */ static void _handle_sigint(time_t *last_intrp) { if (!t) return; if (sigint_terminates) { _fwd_signal(SIGINT); errx("%p: batch mode interrupt, aborting.\n"); /* NORETURN */ } else if (time(NULL) - *last_intrp > INTR_TIME) { err("%p: interrupt (one more within %d sec to abort)\n", INTR_TIME); err("%p: (^Z within %d sec to cancel pending threads)\n", INTR_TIME); *last_intrp = time(NULL); _list_slowthreads(); } else { _fwd_signal(SIGINT); errx("%p: interrupt, aborting.\n"); } } /* * ^Z handler. A ^Z within one second of ^C will cancel any * threads that have not started or are still connecting. * Otherwise, ^Z has the default behavior of stopping the process. */ static void _handle_sigtstp (time_t last_intr) { if (!t) return; if (time (NULL) - last_intr > INTR_TIME) raise (SIGSTOP); else _cancel_pending_threads (); } static void * _signals_thread (void *arg) { sigset_t set; time_t last_intr = 0; int signo; int e; sigemptyset (&set); sigaddset (&set, SIGINT); sigaddset (&set, SIGTSTP); while (t != NULL) { if ((e = sigwait (&set, &signo)) != 0) { if (e == EINTR) continue; err ("sigwait: %s\n", strerror (e)); } switch (signo) { case SIGINT: _handle_sigint (&last_intr); break; case SIGTSTP: _handle_sigtstp (last_intr); break; default: err ("%p: Didn't expect to be here.\n"); } } return NULL; } /* * Run command on a list of hosts, keeping 'fanout' number of connections * active concurrently. */ int dsh(opt_t * opt) { int i, rc = 0; int rv, rshcount; pthread_t thread_wdog; pthread_t thread_sig; pthread_attr_t attr_wdog; pthread_attr_t attr_sig; List pcp_infiles = NULL; hostlist_iterator_t itr; const char *domain = NULL; bool domain_in_label = false; _mask_signals (SIG_BLOCK); /* * Initialize rcmd modules... */ if (rcmd_init(opt) < 0) { err("%p: unable to initialize an rcmd module\n"); exit(1); } _increase_nofile_limit (opt); /* install signal handlers */ _xsignal(SIGALRM, _alarm_handler); if (opt->sigint_terminates) sigint_terminates = 1; rshcount = hostlist_count(opt->wcoll); /* prepend DSHPATH setting to command */ if (pdsh_personality() == DSH && opt->dshpath) { char *cmd = Strdup(opt->dshpath); xstrcat(&cmd, opt->cmd); Free((void **) &opt->cmd); opt->cmd = cmd; } /* Initialize getstat if needed */ if (opt->kill_on_fail || opt->ret_remote_rc) opt->getstat = ";echo " RC_MAGIC "$?"; /* append echo $? to command */ if (pdsh_personality() == DSH && opt->getstat) { char *cmd = Strdup(opt->cmd); xstrcat(&cmd, opt->getstat); Free((void **) &opt->cmd); opt->cmd = cmd; } /* build PCP command */ if (pdsh_personality() == PCP && !opt->reverse_copy) { char *cmd = NULL; /* expand directories, if any, and verify access for all files */ if (!(pcp_infiles = pcp_expand_dirs(opt->infile_names))) { err("%p: unable to build file copy list\n"); exit(1); } xstrcat(&cmd, opt->remote_program_path); if (opt->recursive) xstrcat(&cmd, " -r"); if (opt->preserve) xstrcat(&cmd, " -p"); if (list_count(pcp_infiles) > 1) /* outfile must be directory */ xstrcat(&cmd, " -y"); xstrcat(&cmd, " -z "); /* invoke pcp server */ xstrcat(&cmd, opt->outfile_name); /* outfile is remote target */ opt->cmd = cmd; } if (pdsh_personality() == PCP && opt->reverse_copy) { char *cmd = NULL; char *filename; ListIterator i; xstrcat(&cmd, opt->remote_program_path); if (opt->recursive) xstrcat(&cmd, " -r"); if (opt->preserve) xstrcat(&cmd, " -p"); xstrcat(&cmd, " -Z "); /* invoke pcp client */ i = list_iterator_create(opt->infile_names); while ((filename = list_next(i))) { xstrcat(&cmd, " "); xstrcat(&cmd, filename); } list_iterator_destroy(i); /* The 'host' will be appended to the cmd in _rcp_thread */ opt->cmd = cmd; } /* set debugging flag for this module */ if (opt->debug) debug = 1; /* build thread array--terminated with t[i].host == NULL */ t = (thd_t *) Malloc(sizeof(thd_t) * (rshcount + 1)); if (!(itr = hostlist_iterator_create(opt->wcoll))) errx("%p: hostlist_iterator_create failed\n"); i = 0; while ((t[i].host = hostlist_next(itr))) { char *d; assert(i < rshcount); _thd_init (&t[i], opt, pcp_infiles, i); /* * Require domain names in labels if hosts have * different domains */ if (!domain_in_label && (d = strchr (t[i].host, '.'))) { if (domain == NULL) domain = d; else if (strcmp (d, domain) != 0) domain_in_label = true; } i++; } assert(i == rshcount); hostlist_iterator_destroy(itr); if (domain_in_label) err_no_strip_domain (); /* set timeout values for _wdog() */ connect_timeout = opt->connect_timeout; command_timeout = opt->command_timeout; /* start the watchdog thread */ _dsh_attr_init (&attr_wdog, DSH_THREAD_STACKSIZE); rv = pthread_create(&thread_wdog, &attr_wdog, _wdog, (void *) t); /* start the signals thread */ _dsh_attr_init (&attr_sig, DSH_THREAD_STACKSIZE); rv = pthread_create(&thread_sig, &attr_sig, _signals_thread, (void *) t); /* start all the other threads (at most 'fanout' active at once) */ for (i = 0; i < rshcount; i++) { /* wait until "room" for another thread */ dsh_mutex_lock(&threadcount_mutex); if (opt->fanout == threadcount) pthread_cond_wait(&threadcount_cond, &threadcount_mutex); /* * Advance past any canceled threads */ while ((t[i].state == DSH_CANCELED) && (i < rshcount)) ++i; /* * Abort if no more threads */ if (i >= rshcount) { dsh_mutex_unlock(&threadcount_mutex); break; } /* create thread */ _dsh_attr_init (&t[i].attr, DSH_THREAD_STACKSIZE); #ifdef PTHREAD_SCOPE_SYSTEM /* we want 1:1 threads if there is a choice */ pthread_attr_setscope(&t[i].attr, PTHREAD_SCOPE_SYSTEM); #endif rv = pthread_create(&t[i].thread, &t[i].attr, pdsh_personality() == DSH ? _rsh_thread : _rcp_thread, (void *) &t[i]); if (rv != 0) { if (opt->kill_on_fail) _fwd_signal(SIGTERM); errx("%p: pthread_create %S: %S\n", t[i].host, strerror(rv)); } threadcount++; dsh_mutex_unlock(&threadcount_mutex); } /* wait for termination of remaining threads */ dsh_mutex_lock(&threadcount_mutex); while (threadcount > 0) pthread_cond_wait(&threadcount_cond, &threadcount_mutex); dsh_mutex_unlock(&threadcount_mutex); if (debug) _dump_debug_stats(rshcount); /* * Cancel signals thread and unblock SIGINT/SIGTSTP */ pthread_cancel(thread_sig); _mask_signals (SIG_UNBLOCK); /* if -S, our exit value is the largest of the return codes */ if (opt->ret_remote_rc) { for (i = 0; t[i].host != NULL; i++) { if (t[i].state == DSH_FAILED) rc = RC_FAILED; if (t[i].rc > rc) rc = t[i].rc; } } /* * free hostnames allocated in hostlist_next() * and buffers allocated when initializing thread array */ for (i = 0; t[i].host != NULL; i++) { free(t[i].host); cbuf_destroy (t[i].outbuf); cbuf_destroy (t[i].errbuf); } Free((void **) &t); /* cleanup */ return rc; } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/opt.c0000664€^–Á €^–Á 0000011577315131211226023217 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_STRING_H #include /* strcpy */ #endif #include /* getenv */ #include /* getpwuid */ #include /* MAXPATHLEN */ #include #include /* stat */ #if HAVE_UNISTD_H #include /* getopt */ #endif #include #include #include #include "src/common/hostlist.h" #include "src/common/err.h" #include "src/common/list.h" #include "src/common/split.h" #include "src/common/xstring.h" #include "src/common/xmalloc.h" #include "dsh.h" #include "opt.h" #include "wcoll.h" #include "mod.h" #include "rcmd.h" /* * Fallback maximum username length if sysconf(_SC_LOGIN_NAME_MAX) not * available or fails. (buffers will be this size + 1 for NUL termination) */ #define DEFAULT_MAX_USERNAME_LENGTH 16 #define OPT_USAGE_DSH "\ Usage: pdsh [-options] command ...\n\ -S return largest of remote command return values\n\ -k fail fast on connect failure or non-zero return code\n" /* -s option only useful on AIX */ #if HAVE_MAGIC_RSHELL_CLEANUP #define OPT_USAGE_STDERR "\ -s separate stderr and stdout\n" #endif #define OPT_USAGE_PCP "\ Usage: pdcp [-options] src [src2...] dest\n\ -r recursively copy files\n\ -p preserve modification time and modes\n\ -e PATH specify the path to pdcp on the remote machine\n" /* undocumented "-y" target must be directory option */ /* undocumented "-z" run pdcp server option */ /* undocumented "-Z" run pdcp client option */ #define OPT_USAGE_RPCP "\ Usage: rpdcp [-options] src [src2...] dir\n\ -r recursively copy files\n\ -p preserve modification time and modes\n" /* undocumented "-y" target must be directory option */ /* undocumented "-z" run pdcp server option */ /* undocumented "-Z" run pdcp client option */ #define OPT_USAGE_COMMON "\ -h output usage menu and quit\n\ -V output version information and quit\n\ -q list the option settings and quit\n\ -b disable ^C status feature (batch mode)\n\ -d enable extra debug information from ^C status\n\ -l user execute remote commands as user\n\ -t seconds set connect timeout (default is 10 sec)\n\ -u seconds set command timeout (no default)\n\ -f n use fanout of n nodes\n\ -w host,host,... set target node list on command line\n\ -x host,host,... set node exclusion list on command line\n\ -R name set rcmd module to name\n\ -M name,... select one or more misc modules to initialize first\n\ -N disable hostname: labels on output lines\n\ -L list info on all loaded modules and exit\n" /* undocumented "-T testcase" option */ /* undocumented "-Q" option */ /* undocumented "-K" option - keep domain name in output */ #if HAVE_MAGIC_RSHELL_CLEANUP #define DSH_ARGS "sSk" #else #define DSH_ARGS "Sk" #endif #define PCP_ARGS "pryzZe:" #define GEN_ARGS "hLNKR:M:t:cqf:w:x:l:u:bI:dVT:Q" /* * Pdsh options string (for getopt) -- initialized * in _init_pdsh_options(), and appended to by modules that * register new pdsh options. */ static char *pdsh_options = NULL; /* * Pdsh personality */ static pers_t personality = DSH; /* * Debug mode */ static bool debug = false; /* * Return the current pdsh "personality" */ pers_t pdsh_personality(void) { return personality; } /* * Remote argv array */ static char **remote_argv; static int remote_argc; const char **pdsh_remote_argv (void) { return (const char **) remote_argv; } int pdsh_remote_argc (void) { return remote_argc; } /* * List of explicitly excluded hosts and regex filter options: */ static List exclude_list = NULL; static List regex_list = NULL; static void _usage(opt_t * opt); static void _show_version(void); static int wcoll_args_process (opt_t *opt, char *args); static void wcoll_apply_regex (opt_t *opt, List regexs); static void wcoll_apply_excluded (opt_t *opt, List excludes); static void wcoll_expand (opt_t *opt); static void _init_pdsh_options() { pdsh_options = Strdup(GEN_ARGS); if (personality == DSH) { xstrcat(&pdsh_options, DSH_ARGS); } else xstrcat(&pdsh_options, PCP_ARGS); } /* * Check whether options in 'opt_table' are available for * provisioning. Register the option if the option's personality * allows it. 'opt_table' points to the first module_option listed * in the module. * * Returns false if option is already used by pdsh or a pdsh module. * Returns true if option was successfully registered. */ bool opt_register(struct pdsh_module_option *opt_table) { struct pdsh_module_option *p; if (opt_table == NULL) return true; if (pdsh_options == NULL) _init_pdsh_options(); /* Don't register any options if we can't register all the options * in this module */ for (p = opt_table; p && (p->opt != 0); p++) { if ( (personality & p->personality) && (strchr(pdsh_options, p->opt) != NULL)) { if (debug) { err("%p: Option %c in use by a previously loaded module.\n", p->opt); err("%p: Module options currently in use are: %s\n", pdsh_options); } return false; } } for (p = opt_table; p && (p->opt != 0); p++) { /* register only if the personality allows this option */ if (p->personality & personality) { xstrcatchar(&pdsh_options, p->opt); if (p->arginfo != NULL) xstrcatchar(&pdsh_options, ':'); } } return true; } /* * Check current path dir, cwd, and argv0 for path to program */ char * _check_path(char *dir, char *cwd, char *argv0) { char *abspath = NULL; if (*dir != '/') { abspath = Strdup(cwd); xstrcat(&abspath, "/"); } xstrcat(&abspath, dir); xstrcat(&abspath, "/"); xstrcat(&abspath, argv0); if (access(abspath, R_OK) == 0) return abspath; Free((void **) &abspath); return NULL; } /* * Determine absolute path to the program name based on argv0 */ char * _find_path(char *argv0) { char *abspath = NULL; char cwd[MAXPATHLEN]; if (*argv0 == '/') { /* is absolute path */ abspath = Strdup(argv0); goto done; } if (getcwd(cwd, MAXPATHLEN) == NULL) errx("%p: getcwd failed: %m\n"); if (*argv0 == '.' || strchr(argv0, '/') != NULL) { /* path relative */ abspath = Strdup(cwd); xstrcat(&abspath, "/"); xstrcat(&abspath, argv0); } else { /* argv0 executed in PATH environment var */ char *path, *dir, *p; if ((path = Strdup(getenv("PATH"))) == NULL) errx("%p: getenv PATH failed\n"); dir = path; while ((p = strchr(dir,':')) != NULL) { *p = '\0'; if (strlen(dir) > 0 && (abspath = _check_path(dir,cwd,argv0)) != NULL) { Free((void **) &path); goto done; } dir = ++p; } if (strlen(dir) > 0) abspath = _check_path(dir,cwd,argv0); Free((void **) &path); } done: return abspath; } /* * Return a value for the max login name length (username buffer length) */ static int login_name_max_len (void) { static int maxnamelen = -1; if (maxnamelen < 0) { #ifdef _SC_LOGIN_NAME_MAX errno = 0; if ((maxnamelen = sysconf (_SC_LOGIN_NAME_MAX)) <= 0) { err ("%p: sysconf(LOGIN_NAME_MAX): %m\n"); maxnamelen = DEFAULT_MAX_USERNAME_LENGTH; } #else maxnamelen = DEFAULT_MAX_USERNAME_LENGTH; #endif } return (maxnamelen); } static void copy_username (char *dst, const char *src) { int maxlen = login_name_max_len (); if (strlen (src) > maxlen) errx ("%p: Fatal: username '%s' exceeds max username length (%d)\n", src, maxlen); strcpy (dst, src); } /* * Set defaults for various options. * opt (IN/OUT) option struct */ void opt_default(opt_t * opt, char *argv0) { struct passwd *pw; opt->progname = xbasename(argv0); opt->luser = Malloc (login_name_max_len () + 1); opt->ruser = Malloc (login_name_max_len () + 1); opt->reverse_copy = false; if (!strcmp(opt->progname, "pdsh") || !strcmp(opt->progname, "dsh")) personality = DSH; else if (!strcmp(opt->progname, "pdcp") || !strcmp(opt->progname, "dcp") || !strcmp(opt->progname, "pcp") ) personality = PCP; else if (!strcmp(opt->progname, "rpdcp")) { personality = PCP; opt->reverse_copy = true; } else errx("%p: program must be named pdsh/dsh/pdcp/dcp/pcp/rpdcp\n"); if (pdsh_options == NULL) _init_pdsh_options(); if ((pw = getpwuid(getuid())) != NULL) { copy_username (opt->luser, pw->pw_name); copy_username (opt->ruser, pw->pw_name); opt->luid = pw->pw_uid; } else errx("%p: who are you?\n"); opt->info_only = false; opt->test_range_expansion = false; opt->wcoll = NULL; opt->connect_timeout = CONNECT_TIMEOUT; opt->command_timeout = 0; opt->fanout = DFLT_FANOUT; opt->sigint_terminates = false; opt->infile_names = NULL; opt->altnames = false; opt->debug = false; opt->labels = true; opt->rcmd_name = NULL; opt->misc_modules = NULL; /* * Resolve hostnames by default */ opt->resolve_hosts = true; /* * Do not kill all tasks on single failure by default */ opt->kill_on_fail = false; /* DSH specific */ opt->dshpath = NULL; opt->getstat = NULL; opt->ret_remote_rc = false; opt->cmd = NULL; opt->stdin_unavailable = false; #if HAVE_MAGIC_RSHELL_CLEANUP opt->separate_stderr = false; /* save a socket per connection on aix */ #else opt->separate_stderr = true; #endif opt->local_program_path = _find_path(argv0); /* * By default assume remote path to pdsh/pdcp is the same * as local path: (overridden with pdpc -e or PDSH_REMOTE_PDCP_PATH). */ opt->remote_program_path = Strdup(opt->local_program_path); /* PCP specific */ opt->outfile_name = NULL; opt->recursive = false; opt->preserve = false; opt->pcp_server = false; opt->target_is_directory = false; opt->pcp_client = false; opt->pcp_client_host = NULL; return; } static int string_to_int (const char *val, int *p2int) { char *p; long n; errno = 0; n = strtoul (val, &p, 10); if (errno || (*p != '\0')) return (-1); *p2int = (int) n; return (0); } /* * Override default options with environment variables. * opt (IN/OUT) option struct */ void opt_env(opt_t * opt) { char *rhs; if ((rhs = getenv("FANOUT")) != NULL) if (string_to_int (rhs, &opt->fanout) < 0) errx ("%p: Invalid environment variable FANOUT=%s\n", rhs); if ((rhs = getenv("PDSH_CONNECT_TIMEOUT")) != NULL) if (string_to_int (rhs, &opt->connect_timeout) < 0) errx ("%p: Invalid environment variable PDSH_CONNECT_TIMEOUT=%s\n", rhs); if ((rhs = getenv("PDSH_COMMAND_TIMEOUT")) != NULL) if (string_to_int (rhs, &opt->command_timeout) < 0) errx ("%p: Invalid environment variable PDSH_COMMAND_TIMEOUT=%s\n", rhs); if ((rhs = getenv("PDSH_RCMD_TYPE")) != NULL) opt->rcmd_name = Strdup(rhs); if ((rhs = getenv("PDSH_MISC_MODULES")) != NULL) opt->misc_modules = Strdup(rhs); if ((rhs = getenv("DSHPATH")) != NULL) { struct passwd *pw = getpwnam(opt->luser); char *shell = "sh"; if (pw && *pw->pw_shell) shell = xbasename(pw->pw_shell); /* c shell syntax */ if (!strcmp(shell, "csh") || !strcmp(shell, "tcsh")) { opt->dshpath = Strdup("setenv PATH "); xstrcat(&opt->dshpath, rhs); xstrcat(&opt->dshpath, ";"); } else { /* bourne shell syntax */ opt->dshpath = Strdup("PATH="); xstrcat(&opt->dshpath, rhs); xstrcat(&opt->dshpath, ";"); } } if (pdsh_personality() == PCP) { if ((rhs = getenv ("PDSH_REMOTE_PDCP_PATH")) != NULL) { Free ((void **) &opt->remote_program_path); opt->remote_program_path = Strdup (rhs); } } } /* * Process any options that need to be handled early, i.e. * before modules are loaded. */ void opt_args_early (opt_t * opt, int argc, char *argv[]) { int c; extern int optind; extern char *optarg; extern int opterr; #ifdef __linux int pc = 0; #endif /* * Disable error reporting from getopt during early processing, * since we won't have access to all the options provided by * dlopened modules. */ opterr = 0; #ifdef __linux if (!getenv("POSIXLY_CORRECT")) { /* Tell glibc getopt to stop eating after the first non-option arg */ putenv("POSIXLY_CORRECT=1"); pc = 1; } #endif while ((c = getopt(argc, argv, pdsh_options)) != EOF) { switch (c) { case 'M': if (opt->misc_modules) Free ((void **) &opt->misc_modules); opt->misc_modules = Strdup (optarg); break; case 'd': /* debug */ opt->debug = true; /* Also set global debug flag to get module option * debugging. */ debug = true; break; } } #ifdef __linux if (pc) { unsetenv("POSIXLY_CORRECT"); } #endif } static void wcoll_append_excluded (opt_t *opt, char *exclude_args) { List l = list_split (",", exclude_args); ListIterator i = list_iterator_create (l); char *s; while ((s = list_next (i))) { char *p = NULL; xstrcatchar (&p, '-'); xstrcat (&p, s); wcoll_args_process (opt, p); Free ((void **) &p); } list_iterator_destroy (i); list_destroy (l); } /* * Override default/environment options with command line arguments. * opt (IN/OUT) option struct * argc (IN) arg count passed in from main * argv (IN) arg vector passed in from main */ void opt_args(opt_t * opt, int argc, char *argv[]) { int c; extern int optind; extern char *optarg; extern int opterr; /* * Reset optind after opt_args_early. */ optind = 1; /* * Reinstate getopt error reporting */ opterr = 1; while ((c = getopt(argc, argv, pdsh_options)) != EOF) { switch (c) { /* * The following options were handled in opt_args_early() : */ case 'M': break; /* Continue processing regular options... */ case 'N': opt->labels = false; break; case 'L': mod_list_module_info(); exit(0); break; case 'R': opt->rcmd_name = Strdup(optarg); break; case 'S': /* get remote command status */ opt->ret_remote_rc = true; break; case 'f': /* fanout */ if (string_to_int (optarg, &opt->fanout) < 0) errx ("%p: Invalid fanout `%s' passed to -f.\n", optarg); break; case 'w': /* target node list */ if (strcmp(optarg, "-") == 0) wcoll_args_process (opt, "^-"); else wcoll_args_process (opt, optarg); break; case 'x': /* exclude node list */ wcoll_append_excluded (opt, optarg); break; case 'q': /* display fanout and wcoll then quit */ opt->info_only = true; break; /* -s option only useful on AIX */ #if HAVE_MAGIC_RSHELL_CLEANUP case 's': /* split stderr and stdout */ opt->separate_stderr = true; break; #endif case 't': /* set connect timeout */ opt->connect_timeout = atoi(optarg); break; case 'u': /* set command timeout */ opt->command_timeout = atoi(optarg); break; case 'b': /* "batch" */ opt->sigint_terminates = true; break; case 'l': /* specify remote username for rshd */ copy_username (opt->ruser, optarg); break; case 'r': /* rcp: copy recursively */ if (pdsh_personality() == PCP) opt->recursive = true; else goto test_module_option; break; case 'p': /* rcp: preserve permissions */ if (pdsh_personality() == PCP) opt->preserve = true; else goto test_module_option; break; case 'e': if (pdsh_personality() == PCP) { Free ((void **) &opt->remote_program_path); opt->remote_program_path = Strdup(optarg); } else goto test_module_option; break; case 'V': /* show version */ _show_version(); break; case 'T': /* execute testcase */ testcase(atoi(optarg)); break; case 'Q': /* info only, expand host ranges */ opt->info_only = true; opt->test_range_expansion = true; break; case 'h': /* display usage message */ _usage(opt); break; case 'K': /* don't strip host domain in output */ err_no_strip_domain (); break; case 'y': if (pdsh_personality() == PCP) opt->target_is_directory = true; /* is target a dir? */ else goto test_module_option; break; case 'z': if (pdsh_personality() == PCP) opt->pcp_server = true; /* run PDCP server */ else goto test_module_option; break; case 'Z': if (pdsh_personality() == PCP) opt->pcp_client = true; /* run PDCP client */ else goto test_module_option; break; case 'k': opt->kill_on_fail = true; break; default: test_module_option: if (mod_process_opt(opt, c, optarg) < 0) _usage(opt); } } /* * Load default module for all hosts (unless overridden) */ if (opt->rcmd_name == NULL) opt->rcmd_name = Strdup(rcmd_get_default_module ()); if (opt->rcmd_name != NULL) if (rcmd_register_default_rcmd(opt->rcmd_name) < 0) exit(1); /* * Save beginning of remote argv in case something needs * to view the unadulterated version (after shell quoting * applied, etc.) */ remote_argc = argc - optind; remote_argv = argv + optind; /* DSH: build command */ if (personality == DSH) { for (; optind < argc; optind++) { if (opt->cmd != NULL) xstrcat(&opt->cmd, " "); xstrcat(&opt->cmd, argv[optind]); } /* PCP: build file list */ } else { if (!opt->infile_names) opt->infile_names = list_create(NULL); for (; optind < argc - 1; optind++) list_append(opt->infile_names, argv[optind]); if (optind < argc) { /* If this is the initial pdcp call, the last argument is * the output file/dir. If this is the pcp_client, the * last argument is the hostname used for connecting. */ if (opt->pcp_client) xstrcat(&opt->pcp_client_host, argv[optind]); else xstrcat(&opt->outfile_name, argv[optind]); } } /* ignore wcoll filtering when running pcp server */ if (opt->pcp_server) return; /* * Give modules a chance to fill in wcoll if it hasn't been already: */ if (mod_read_wcoll (opt) < 0) exit (1); /* * If wcoll is still empty, try WCOLL env variable: */ if (opt->wcoll == NULL) { char *val = getenv ("WCOLL"); if (val != NULL) opt->wcoll = read_wcoll (val, NULL); } if (opt->wcoll) { /* * Now apply wcoll filtering */ if (exclude_list) { wcoll_apply_excluded (opt, exclude_list); list_destroy (exclude_list); } if (regex_list) { wcoll_apply_regex (opt, regex_list); list_destroy (regex_list); } /* * Finally, re-expand wcoll to allow two sets of brackets. * (For historical compatibility) */ wcoll_expand (opt); } } static void wcoll_expand (opt_t *opt) { hostlist_t hl = opt->wcoll; char *hosts; /* * Create new hostlist for wcoll */ opt->wcoll = hostlist_create (""); while ((hosts = hostlist_shift (hl))) { hostlist_push (opt->wcoll, hosts); free (hosts); } hostlist_destroy (hl); } /* * Check if infile_names are legit. */ static int _infile_names_check(opt_t * opt) { bool verified = true; ListIterator i; char *name; i = list_iterator_create(opt->infile_names); while ((name = list_next(i))) { struct stat sb; if (stat(name, &sb) < 0) { err("%p: can't stat %s\n", name); verified = false; continue; } if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) { err("%p: not a regular file or directory: %s\n", name); verified = false; break; } if (S_ISDIR(sb.st_mode) && !opt->recursive) { err("%p: use -r to copy directories: %s\n", name); verified = false; break; } } list_iterator_destroy(i); return verified; } /* * Trap a few option inconsitencies. * opt (IN) option struct */ bool opt_verify(opt_t * opt) { bool verified = true; /* * Call all post option processing functions for modules */ if (mod_postop(opt) > 0) verified = false; /* can't prompt for command if stdin was used for wcoll */ if (personality == DSH && opt->stdin_unavailable && !opt->cmd) { _usage(opt); verified = false; } if (!opt->pcp_server && !opt->pcp_client) { /* wcoll is required */ if (opt->wcoll == NULL || hostlist_count(opt->wcoll) == 0) { err("%p: no remote hosts specified\n"); verified = false; } /* connect and command timeouts must be reasonable */ if (opt->connect_timeout < 0) { err("%p: connect timeout must be >= 0\n"); verified = false; } if (opt->command_timeout < 0) { err("%p: command timeout must be >= 0\n"); verified = false; } } /* PCP: must have source and destination filename(s) */ if (personality == PCP && !opt->pcp_server && !opt->pcp_client) { if (!opt->outfile_name || list_is_empty(opt->infile_names)) { err("%p: pcp requires source and dest filenames\n"); verified = false; } if (opt->target_is_directory) { err("%p: target is directory can only be specified with pcp server\n"); verified = false; } /* If reverse copy, the infiles need not exist locally */ if (!opt->reverse_copy) { if (!_infile_names_check(opt)) verified = false; } /* If reverse copy, the destination must be a directory */ if (opt->reverse_copy && opt->outfile_name) { struct stat statbuf; if (stat(opt->outfile_name, &statbuf) < 0) { err("%p: can't stat %s\n", opt->outfile_name); verified = false; } if (!S_ISDIR(statbuf.st_mode)) { err("%p: reverse copy dest must be a directory\n"); verified = false; } } } /* PCP: server and client sanity check */ if (personality == PCP && opt->pcp_server && opt->pcp_client) { err("%p: pcp server and pcp client cannot both be set\n"); verified = false; } /* PCP: verify options when -z option specified */ if (personality == PCP && opt->pcp_server) { if (opt->infile_names && !list_is_empty(opt->infile_names)) { err("%p: do not list source files with pcp server\n"); verified = false; } if (!opt->outfile_name) { err("%p: output file must be specified with pcp server\n"); verified = false; } if (opt->pcp_client_host) { err("%p: pcp client host should not be specified with pcp server\n"); verified = false; } if (opt->reverse_copy) { err("%p: reverse copy cannot be specified with pcp server\n"); verified = false; } } /* PCP: verify options when -Z option specified */ if (personality == PCP && opt->pcp_client) { opt->reverse_copy = false; if (!opt->infile_names || list_is_empty(opt->infile_names)) { err("%p: list source files required for pcp client\n"); verified = false; } if (opt->outfile_name) { err("%p: output file should not be specified with pcp client\n"); verified = false; } if (!opt->pcp_client_host) { err("%p: pcp client host must be specified with pcp client\n"); verified = false; } /* If reverse copy the infiles should exist locally */ if (opt->infile_names && !_infile_names_check(opt)) verified = false; } return verified; } /* printf args */ #define BOOLSTR(x) ((x) ? "Yes" : "No") #define STRORNULL(x) ((x) ? (x) : "none") #define RCMDSTR(x) (x == RCMD_BSD ? "RCMD_BSD" : \ (x == RCMD_K4 ? "RCMD_K4" : \ (x == RCMD_QSHELL ? "RCMD_QSHELL" : \ (x == RCMD_SSH ? "RCMD_SSH" : "")))) #define ALLOCSTR(x) (x == ALLOC_BLOCK ? "ALLOC_BLOCK" : \ (x == ALLOC_CYCLIC ? "ALLOC_CYCLIC" : "")) /* * List the current options. * opt (IN) option list */ void opt_list(opt_t * opt) { char wcoll_str[1024]; int n; if (personality == DSH) { out("-- DSH-specific options --\n"); out("Separate stderr/stdout %s\n", BOOLSTR(opt->separate_stderr)); out("Path prepended to cmd %s\n", STRORNULL(opt->dshpath)); out("Appended to cmd %s\n", STRORNULL(opt->getstat)); out("Command: %s\n", STRORNULL(opt->cmd)); } else { char infiles [4096]; out("-- PCP-specific options --\n"); if (list_join (infiles, sizeof (infiles), ", ", opt->infile_names)) out("Infile(s) %s\n", infiles); out("Outfile %s\n", STRORNULL(opt->outfile_name)); out("Recursive %s\n", BOOLSTR(opt->recursive)); out("Preserve mod time/mode %s\n", BOOLSTR(opt->preserve)); if (opt->pcp_server) { out("pcp server %s\n", BOOLSTR(opt->pcp_server)); out("target is directory %s\n", BOOLSTR(opt->target_is_directory)); } } if (!opt->pcp_server) { out("Full program pathname %s\n", STRORNULL(opt->local_program_path)); out("Remote program path %s\n", STRORNULL(opt->remote_program_path)); out("\n-- Generic options --\n"); out("Local username %s\n", opt->luser); out("Local uid %d\n", opt->luid); out("Remote username %s\n", opt->ruser); out("Rcmd type %s\n", STRORNULL(opt->rcmd_name)); out("one ^C will kill pdsh %s\n", BOOLSTR(opt->sigint_terminates)); out("Connect timeout (secs) %d\n", opt->connect_timeout); out("Command timeout (secs) %d\n", opt->command_timeout); out("Fanout %d\n", opt->fanout); out("Display hostname labels %s\n", BOOLSTR(opt->labels)); out("Debugging %s\n", BOOLSTR(opt->debug)); out("\n-- Target nodes --\n"); if (opt->test_range_expansion) { n = hostlist_deranged_string(opt->wcoll, sizeof(wcoll_str), wcoll_str); } else { n = hostlist_ranged_string(opt->wcoll, sizeof(wcoll_str), wcoll_str); } if (n < 0) out("%s[truncated]\n", wcoll_str); else out("%s\n", wcoll_str); } } /* * Free heap-allocated memory associated with options, etc. * opt (IN/OUT) option struct */ void opt_free(opt_t * opt) { if (opt->wcoll != NULL) hostlist_destroy(opt->wcoll); if (opt->cmd != NULL) Free((void **) &opt->cmd); if (opt->rcmd_name != NULL) Free((void **) &opt->rcmd_name); if (opt->misc_modules != NULL) Free((void **) &opt->misc_modules); if (pdsh_options) Free((void **) &pdsh_options); if (opt->dshpath) Free((void **) &opt->dshpath); if (opt->local_program_path) Free((void **) &opt->local_program_path); if (opt->remote_program_path) Free((void **) &opt->remote_program_path); if (opt->infile_names) list_destroy(opt->infile_names); if (opt->luser) Free((void **) &opt->luser); if (opt->ruser) Free((void **) &opt->ruser); rcmd_exit(); } /* * Returns a string of comma separated module names of type `type' * Returns NULL if no modules of this type are loaded. */ static int _module_list_string(char *type, char *buf, int len) { List l = NULL; int n = 0; if (mod_count(type) == 0) return (0); l = mod_get_module_names(type); n = list_join(buf, len, ",", l); list_destroy(l); return (n); } static int _module_list_uninitialized (char *type, char *buf, int len) { List l = NULL; int n = 0; if (mod_count(type) == 0) return (0); l = mod_get_uninitialized_module_names(type); n = list_join(buf, len, ",", l); list_destroy(l); return (n); } static char *_rcmd_module_list(char *buf, int maxlen) { int len, len2; char rbuf [1024]; int n; n = _module_list_string ("rcmd", rbuf, sizeof (rbuf)); len = snprintf(buf, maxlen, "%s", n ? rbuf : "(none)"); if ((len < 0) || (len >= maxlen)) goto done; if (mod_count("rcmd") > 1) { char *def = rcmd_get_default_module(); len2 = snprintf ( buf+len, maxlen-len, " (default: %s)", def ? def : "none" ); if (len2 < 0) len = -1; else len += len2; } done: if ((len < 0) || (len > maxlen)) snprintf(buf + maxlen - 12, 12, "[truncated]"); buf[maxlen - 1] = '\0'; return buf; } /* * Spit out all the options and their one-line synopsis for the user, * then exit. */ static void _usage(opt_t * opt) { char buf[1024]; if (personality == DSH) { err(OPT_USAGE_DSH); #if HAVE_MAGIC_RSHELL_CLEANUP err(OPT_USAGE_STDERR); #endif } else if (!opt->reverse_copy) /* PCP */ err(OPT_USAGE_PCP); else err(OPT_USAGE_RPCP); err(OPT_USAGE_COMMON); mod_print_all_options(18); err("available rcmd modules: %s\n", _rcmd_module_list(buf, 1024)); exit(1); } static void _show_version(void) { char buf[1024]; extern char *pdsh_version; int n; printf("%s\n", pdsh_version); printf("rcmd modules: %s\n", _rcmd_module_list(buf, sizeof (buf))); n = _module_list_string("misc", buf, sizeof (buf)); printf("misc modules: %s", n ? buf : "(none)"); if ((n = _module_list_uninitialized ("misc", buf, sizeof (buf)))) { printf (" (*conflicting: %s)\n", buf); printf ("[* To force-load a conflicting module," " use the -M option]\n"); } else printf ("\n"); exit(0); } /* * Take a string `hosts' possibly of form "rcmd_type:user@hostlist" and * place the hostlist part of the string in *hptr and the rcmd part * of the string in rptr. Returns nonzero if an rcmd_type was found, * zero otherwise (in which case rptr is not touched). */ static int get_host_rcmd_type (char *hosts, char **rptr, char **hptr, char **uptr) { char *p = hosts; char *q; *hptr = hosts; p = strchr (hosts, ':'); q = strchr (hosts, '@'); if (p && q && p > q) errx ("Host spec \"%s\" not of form [rcmd_type:][user@]hosts\n", hosts); /* * If we found a single ':' character, then everything * preceeding that is the rcmd type. Otherwise, we ignore * presence of all colons. This can be done b/c even if there * were another colon later in the string, the string preceeding * it can not be an rcmd type since colon is not extant in rcmd * type names. */ if (p && (*(p+1) != ':')) { *rptr = *hptr; *p++ = '\0'; *hptr = p; } /* * If we found a '@' char then what precedes it is username. */ if (q) { *uptr = *hptr; *q++ = '\0'; *hptr = q; } return (1); } void free_f (void *x) { Free (&x); } struct regex_info { int exclude; int cflags; int eflags; int compiled; char * pattern; regex_t reg; }; void regex_info_destroy (struct regex_info *re) { if (re->compiled) regfree (&re->reg); if (re->pattern) Free ((void **) &re->pattern); Free ((void **) &re); } struct regex_info * regex_info_create (const char *r, int exclude) { int rc; struct regex_info *re = Malloc (sizeof (*re)); re->pattern = Strdup (r); re->exclude = exclude; re->cflags = REG_EXTENDED | REG_NOSUB; re->eflags = 0; re->compiled = 0; if ((rc = regcomp (&re->reg, re->pattern, re->cflags)) != 0) { char msg [4096]; regerror (rc, &re->reg, msg, sizeof (msg)); err ("%p: Error %s pattern \"%s\": %s\n", re->exclude ? "excluding" : "matching", re->pattern, msg); regex_info_destroy (re); return (NULL); } re->compiled = 1; return (re); } void hostlist_filter_regex (hostlist_t hl, struct regex_info *re) { char *host; hostlist_iterator_t i; i = hostlist_iterator_create (hl); while ((host = hostlist_next (i))) { int rc = regexec (&re->reg, host, 0, NULL, re->eflags); if ((re->exclude && rc == 0) || (!re->exclude && rc == REG_NOMATCH)) hostlist_remove (i); free (host); } hostlist_iterator_destroy (i); } static void list_push_hostlist (List l, hostlist_t hl) { size_t n = 4096; char *s = Malloc (n); while ((hostlist_ranged_string (hl, n-1, s) < 0) && (n*=2 < 0x7fffff)) { Realloc ((void **) &s, n); } list_push (l, s); } static void hostlist_assign (hostlist_t *hlp, hostlist_t hl2) { if (*hlp == NULL) *hlp = hostlist_create (""); hostlist_push_list (*hlp, hl2); } static int wcoll_arg_process (char *arg, opt_t *opt) { struct regex_info *re; int excluded = 0; char *p = arg; if (exclude_list == NULL) exclude_list = list_create (free_f); if (regex_list == NULL) regex_list = list_create ((ListDelF) regex_info_destroy); /* * Check for excluded arg */ if (*p == '-') { excluded = 1; p++; } /* * Move past any leading whitespace */ while (isspace (*p)) p++; if (*p == '^') { hostlist_t hl = read_wcoll (p+1, NULL); if (hl == NULL) errx ("%p: Error reading wcoll: %s: %m\n", p+1); if (excluded) list_push_hostlist (exclude_list, hl); else hostlist_assign (&opt->wcoll, hl); hostlist_destroy (hl); } else if (*p == '/') { int len; ++p; len = strlen (p); if (p [len - 1] == '/') p [len - 1] = '\0'; if ((re = regex_info_create (p, excluded)) == NULL) errx ("%p: Fatal error\n"); list_push (regex_list, re); } else { if (excluded) { list_push (exclude_list, Strdup (p)); } else { char *rcmd_type = NULL; char *hosts, *user = NULL; if (!opt->wcoll) opt->wcoll = hostlist_create (""); get_host_rcmd_type (p, &rcmd_type, &hosts, &user); hostlist_push (opt->wcoll, hosts); if (rcmd_type || user) { if (rcmd_register_defaults (hosts, rcmd_type, user) < 0) errx ("%p: Failed to register rcmd \"%s\" for \"%s\"\n", rcmd_type, hosts); } } } return 0; } static int wcoll_args_process (opt_t *opt, char * args) { int rc; List l = list_split (",", args); rc = list_for_each (l, (ListForF) wcoll_arg_process, opt); list_destroy (l); return (rc); } static void wcoll_apply_regex (opt_t *opt, List regexs) { struct regex_info *re; ListIterator i; if (!opt->wcoll || !regexs) return; /* * filter any supplied regular expression args */ i = list_iterator_create (regexs); while ((re = list_next (i))) hostlist_filter_regex (opt->wcoll, re); list_iterator_destroy (i); } static void wcoll_apply_excluded (opt_t *opt, List excludes) { ListIterator i; char *arg; if (!opt->wcoll || !excludes) return; /* * filter explicitly excluded hosts: */ i = list_iterator_create (excludes); while ((arg = list_next (i))) hostlist_delete (opt->wcoll, arg); list_iterator_destroy (i); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/dsh.h0000664€^–Á €^–Á 0000000735715131211226023176 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _DSH_INCLUDED #define _DSH_INCLUDED #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_PTHREAD_H #include #endif #include "src/common/macros.h" #include "src/common/list.h" #include "src/pdsh/opt.h" #include "src/pdsh/cbuf.h" #include "src/pdsh/rcmd.h" #define INTR_TIME 1 /* secs */ #define WDOG_POLL 2 /* secs */ /* some handy SP constants */ /* NOTE: degenerate case of one node per frame, nodes would be 1, 17, 33,... */ #define MAX_SP_NODES 512 #define MAX_SP_NODES_PER_FRAME 16 #define MAX_SP_NODE_NUMBER (MAX_SP_NODES * MAX_SP_NODES_PER_FRAME - 1) typedef enum { DSH_NEW, DSH_RCMD, DSH_READING, DSH_DONE, DSH_FAILED, DSH_CANCELED } state_t; typedef struct thd { pthread_t thread; pthread_attr_t attr; state_t state; /* thread state */ char *host; /* host name */ char *luser; /* local username */ char *ruser; /* remote username */ bool resolve_hosts; /* resolve hosts in thread? */ time_t start; /* time stamp for start */ time_t connect; /* time stamp for connect */ time_t finish; /* time stamp for finish */ char *cmd; /* command */ bool dsh_sopt; /* true if -s (sep stderr/out) */ bool kill_on_fail; /* If true, kill all procs on single failure */ List pcp_infiles; /* name of input files/dirs */ char *pcp_outfile; /* name of output file/dir */ bool pcp_popt; /* preserve mtime/mode */ bool pcp_ropt; /* recursive */ bool pcp_yopt; /* target is directory */ bool pcp_Popt; /* reverse copy */ bool pcp_Zopt; /* pcp client */ char *pcp_progname; /* program name */ char *outfile_name; /* outfile name */ int rc; /* remote return code (-S) */ int nodeid; /* node index */ int nnodes; /* number of nodes in job */ struct rcmd_info *rcmd; /* rcmd connection info */ cbuf_t outbuf; /* output buffer */ cbuf_t errbuf; /* stderr buffer */ bool labels; /* display host: labels */ char addr[IP_ADDR_LEN]; /* IP address */ } thd_t; int dsh(opt_t *); void set_rcmd_timeout(int); void testcase(int); #endif /* _DSH_INCLUDED */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/rcmd.c0000664€^–Á €^–Á 0000002466015131211226023334 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2005-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include #endif #include #include #include #include #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/list.h" #include "opt.h" #include "mod.h" #include "rcmd.h" /* * Ordered preference for determining default rcmd method. * Warning: If none of these modules are loaded, there will be no default. */ static char * rcmd_rank[] = #if defined(RCMD_RANK_LIST) { RCMD_RANK_LIST, NULL }; #else { "mrsh", "rsh", "ssh", "krb4", "qsh", "mqsh", "exec", "xcpu", NULL }; #endif /* RCMD_RANK_LIST */ struct rcmd_module { char * name; mod_t mod; struct rcmd_options options; RcmdInitF init; RcmdSigF signal; RcmdF rcmd; RcmdDestroyF rcmd_destroy; }; struct node_rcmd_info { char *hostname; char *username; struct rcmd_module *rmod; }; static List host_info_list = NULL; static List rcmd_module_list = NULL; static struct rcmd_module *default_rcmd_module = NULL; static struct rcmd_module *current_rcmd_module = NULL; static struct node_rcmd_info * node_rcmd_info_create (char *hostname, char *user, struct rcmd_module *module) { struct node_rcmd_info *n = Malloc (sizeof (*n)); if (!n) return NULL; n->hostname = Strdup (hostname); n->username = Strdup (user); n->rmod = module; return (n); } static void node_rcmd_info_destroy (struct node_rcmd_info *n) { if (!n) return; Free ((void **)&n->hostname); if (n->username) Free ((void **)&n->username); Free ((void **)&n); } struct rcmd_module * rcmd_module_create (mod_t mod) { struct rcmd_module *rmod = Malloc (sizeof (*rmod)); rmod->mod = mod; rmod->name = mod_get_name (mod); if (!(rmod->init = (RcmdInitF) mod_get_rcmd_init(mod))) { err("Unable to resolve \"rcmd_init\" in module \"%s\"\n", mod_get_name(mod)); goto fail; } if (!(rmod->signal = (RcmdSigF) mod_get_rcmd_signal(mod))) { err("Unable to resolve \"rcmd_signal\" in module \"%s\"\n", mod_get_name(mod)); goto fail; } if (!(rmod->rcmd = (RcmdF) mod_get_rcmd(mod))) { err("Unable to resolve \"rcmd\" in module \"%s\"\n", mod_get_name(mod)); goto fail; } /* * Destroy function not required */ rmod->rcmd_destroy = (RcmdDestroyF) mod_get_rcmd_destroy (mod); rmod->options.resolve_hosts = 1; return (rmod); fail: Free ((void **) &rmod); return (NULL); } static void rcmd_module_destroy (struct rcmd_module *rmod) { Free ((void **) &rmod); } static int find_rcmd_module (struct rcmd_module *x, char *name) { return (strcmp (x->name, name) == 0); } static int find_host (struct node_rcmd_info *x, char *hostname) { return (strcmp (x->hostname, hostname) == 0); } static struct node_rcmd_info * host_rcmd_info (char *host) { if (host_info_list == NULL) return (NULL); return (list_find_first (host_info_list, (ListFindF) find_host, host)); } static struct rcmd_module * rcmd_module_register (char *name) { mod_t mod = NULL; struct rcmd_module *rmod = NULL; if (rcmd_module_list == NULL) rcmd_module_list = list_create ((ListDelF) rcmd_module_destroy); else rmod = list_find_first (rcmd_module_list, (ListFindF) find_rcmd_module, name); if (rmod != NULL) return (rmod); if (!(mod = mod_get_module ("rcmd", name))) { err ("No such rcmd module \"%s\"\n", name); return (NULL); } if (!(rmod = rcmd_module_create (mod))) return (NULL); if (!list_append (rcmd_module_list, rmod)) { err ("Failed to append rcmd module \"%s\"\n", name); rcmd_module_destroy (rmod); return (NULL); } return (rmod); } static int hostlist_register_rcmd (const char *hosts, struct rcmd_module *rmod, char *user) { hostlist_t hl = hostlist_create (hosts); char * host; if (hl == NULL) return (-1); if (host_info_list == NULL) host_info_list = list_create ((ListDelF) node_rcmd_info_destroy); while ((host = hostlist_pop (hl))) { struct node_rcmd_info *n = NULL; /* * Do not override previously installed host info. First registered * rcmd type for a host wins. This allows command line to override * everything else. */ if (list_find_first (host_info_list, (ListFindF) find_host, host)) continue; if ((n = node_rcmd_info_create (host, user, rmod)) == NULL) errx ("Failed to create rcmd info for host \"%s\"\n", host); list_append (host_info_list, n); free (host); } hostlist_destroy (hl); return (0); } /* * Walk through list of default candidate modules, starting at head, * and return the first module that is loaded. * Unless rcmd_default_module is already registered. */ char * rcmd_get_default_module (void) { mod_t mod = NULL; int i = 0; const char *name = NULL; if (default_rcmd_module != NULL) return (default_rcmd_module->name); while ((name = rcmd_rank[i++]) && !mod) mod = mod_get_module ("rcmd", name); return mod ? mod_get_name (mod) : NULL; } int rcmd_register_default_rcmd (char *rcmd_name) { struct rcmd_module *rmod = NULL; if (!(rmod = rcmd_module_register (rcmd_name))) return (-1); default_rcmd_module = rmod; return (0); } int rcmd_register_defaults (char *hosts, char *rcmd_name, char *user) { struct rcmd_module *rmod = NULL; if (rcmd_name && !(rmod = rcmd_module_register (rcmd_name))) return (-1); /* If host list is NULL, we are registering a new global default * rcmd module. Set the convenience pointer and return */ if (hosts == NULL) { default_rcmd_module = rmod; return (0); } if (hostlist_register_rcmd (hosts, rmod, user) < 0) return (-1); return (0); } struct rcmd_info * rcmd_info_create (struct rcmd_module *rmod) { struct rcmd_info *r = Malloc (sizeof (*r)); if (r == NULL) return (NULL); r->fd = -1; r->efd = -1; r->rmod = rmod; r->opts = &rmod->options; r->arg = NULL; r->ruser = NULL; return (r); } void rcmd_info_destroy (struct rcmd_info *r) { Free ((void **) &r); } struct rcmd_info * rcmd_create (char *host) { struct rcmd_info *rcmd = NULL; struct rcmd_module *rmod = NULL; struct node_rcmd_info *n = NULL; if ((n = host_rcmd_info (host))) { rmod = n->rmod; } /* * If no rcmd module use default */ if (rmod == NULL) { if ((rmod = default_rcmd_module) == NULL) { err ("%p: No rcmd module for \"%s\"\n", host); return (NULL); } } if ((rcmd = rcmd_info_create (rmod)) == NULL) { err ("%p: Unable to allocate rcmd info for \"%s\"\n", host); return (NULL); } if (n != NULL && n->username) rcmd->ruser = n->username; return (rcmd); } int rcmd_connect (struct rcmd_info *rcmd, char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int nodeid, bool error_fd) { /* * rcmd->ruser overrides default */ if (rcmd->ruser) remuser = rcmd->ruser; rcmd->fd = (*rcmd->rmod->rcmd) (ahost, addr, locuser, remuser, cmd, nodeid, error_fd ? &rcmd->efd : NULL, &rcmd->arg); return (rcmd->fd); } int rcmd_destroy (struct rcmd_info *rcmd) { int rc = 0; if (rcmd == NULL) return (0); if (rcmd->rmod->rcmd_destroy) rc = (*rcmd->rmod->rcmd_destroy) (rcmd->arg); rcmd_info_destroy (rcmd); return (rc); } int rcmd_signal (struct rcmd_info *rcmd, int signum) { assert (rcmd != NULL); assert (rcmd->rmod != NULL); return (*rcmd->rmod->signal) (rcmd->efd, rcmd->arg, signum); } int rcmd_init (opt_t *opt) { struct rcmd_module *r = NULL; ListIterator i; if (!rcmd_module_list) { if (default_rcmd_module == NULL) return (-1); current_rcmd_module = default_rcmd_module; (*default_rcmd_module->init) (opt); current_rcmd_module = NULL; return (0); } i = list_iterator_create (rcmd_module_list); while ((r = list_next (i))) { current_rcmd_module = r; (*r->init) (opt); current_rcmd_module = NULL; } list_iterator_destroy (i); return (0); } int rcmd_exit (void) { if (host_info_list) list_destroy (host_info_list); if (rcmd_module_list) list_destroy (rcmd_module_list); return (0); } int rcmd_opt_set (int id, void * value) { if (current_rcmd_module == NULL) { errno = ESRCH; return (-1); } switch (id) { case RCMD_OPT_RESOLVE_HOSTS: current_rcmd_module->options.resolve_hosts = (long int) value; break; default: errno = EINVAL; return (-1); } return (0); } /* * vi: ts=4 sw=4 expandtab */ pdsh-2.36/src/pdsh/opt.h0000664€^–Á €^–Á 0000001275315131211226023216 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _OPT_INCLUDED #define _OPT_INCLUDED #if HAVE_CONFIG_H #include "config.h" #endif #include /* for uid_t */ #include "src/common/macros.h" #include "src/common/list.h" #include "src/common/hostlist.h" #define MAX_GENDATTR 64 #define RC_MAGIC "XXRETCODE:" #define RC_FAILED 254 /* -S exit value if any hosts fail to connect */ /* set to 0x1 and 0x2 so we can do bitwise operations with DSH and PCP */ typedef enum { DSH = 0x1, PCP = 0x2} pers_t; typedef struct { /* common options */ char *progname; /* argv[0] */ bool debug; /* -d */ bool info_only; /* -q */ bool test_range_expansion; /* -Q (implies -q) */ bool sdr_verify; /* -v */ bool sdr_global; /* -G */ bool altnames; /* -i */ bool sigint_terminates; /* -b */ hostlist_t wcoll; /* target node list (-w, WCOLL, or stdin) */ char *luser; /* local username */ uid_t luid; /* uid for above */ char *ruser; /* remote username (-l or default) */ int fanout; /* (-f, FANOUT, or default) */ int connect_timeout; int command_timeout; char *rcmd_name; /* -R name */ char *misc_modules; /* Explicit list of misc modules to load */ bool resolve_hosts; /* Set optionally by rcmd modules */ bool kill_on_fail; /* DSH-specific options */ bool separate_stderr; /* -s */ bool stdin_unavailable; /* set if stdin used for WCOLL */ char *cmd; char *dshpath; /* optional PATH command prepended to cmd */ char *getstat; /* optional echo $? appended to cmd */ bool ret_remote_rc; /* -S: return largest remote return val */ bool labels; /* display host: before output */ /* PCP-specific options */ bool preserve; /* -p */ bool recursive; /* -r */ List infile_names; /* -I or pcp source spec */ char *outfile_name; /* pcp dest spec */ bool pcp_server; /* undocument pdcp server option */ bool target_is_directory; /* undocumented pdcp is target a directory */ bool pcp_client; /* undocumented pdcp client option */ char *pcp_client_host; /* hostname used to execute client */ char *local_program_path; /* absolute path to program on local node */ char *remote_program_path; /* absolute path to program on remote nodes */ bool reverse_copy; /* rpdcp: reverse copy */ } opt_t; void opt_default(opt_t *, char *argv0); void opt_env(opt_t *); void opt_args_early(opt_t *, int, char **); void opt_args(opt_t *, int, char **); bool opt_verify(opt_t *); void opt_list(opt_t *); void opt_free(opt_t *); /* * Return the current pdsh "personality" */ pers_t pdsh_personality(void); /* * Return a list of the original remote args */ const char ** pdsh_remote_argv (void); /* * Return a list of the original remote arg count */ int pdsh_remote_argc (void); /* * Structure for pdsh modules to export new options. * * Module should define a table of options as: * * struct pdsh_module_option pdsh_module_opts[] = { ... }; * * which will be read by the module loader. The module loader * (see mod.c) will call opt_register for each of the defined * options. If any option fails to register, the module will * be unloaded and a warning message printed. */ typedef int (*optFunc)(opt_t *opt, int optopt, char *optarg); struct pdsh_module_option { char opt; /* option character */ char *arginfo; /* one word descr of arg if option takes one */ char *descr; /* short description of option */ int personality; /* Personality for which this option is suitable. * * May be set to DSH, PCP, or DSH |PCP */ optFunc f; /* callback function for option processing */ }; #define PDSH_OPT_TABLE_END { 0, NULL, NULL, 0, NULL } bool opt_register(struct pdsh_module_option *popt); #endif /* OPT_INCLUDED */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/mod.c0000664€^–Á €^–Á 0000006043215131211226023163 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H # include #endif #if HAVE_FEATURES_H # include #endif #include #include #include #include #include #include #include #if STATIC_MODULES #include "static_modules.h" #else #include "dlfcn.h" #endif #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/hostlist.h" #include "src/common/list.h" #include "src/common/split.h" #include "mod.h" /* * pdsh/322: Workaround apparent bug in glibc 2.2.4 which * occaisionally causes LinuxThreads manager thread to * segfault at exit. (Disable dlclose() and lt_dlexit() * in these versions of glibc) */ #if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3) # define PREVENT_DLCLOSE_BUG 1 #endif /* * Components of a module. */ struct module_components { #ifndef NDEBUG #define MOD_MAGIC 0xc0c0b0b int magic; #endif #if !STATIC_MODULES void *handle; #endif char *filename; int priority; int initialized; struct pdsh_module *pmod; }; typedef enum permission_error { DIR_OK, DIR_NOT_DIRECTORY, DIR_BAD_OWNER, DIR_WORLD_WRITABLE } perm_error_t; /* * Static function prototypes: */ #if STATIC_MODULES static int _mod_load_static_modules(void); static int _mod_load_static(int); #else static int _mod_load_dynamic_modules(const char *, opt_t *); static int _mod_load_dynamic(const char *, opt_t *); static int _cmp_filenames(mod_t, char *); static int _is_loaded(char *filename); static bool _path_permissions_ok(const char *dir, uid_t pdsh_owner); static perm_error_t _dir_permission_error(struct stat *, uid_t alt_uid); #endif static int _mod_initialize(mod_t mod); static int _mod_init_list_safe(mod_t mod, void *arg); static void _mod_destroy(mod_t mod); static bool _mod_opts_ok(mod_t mod); static int _mod_print_info(mod_t mod); static void _print_option_help(struct pdsh_module_option *p, int col); static struct pdsh_module_option * _mod_find_opt(mod_t mod, int opt); /* * Static list of loaded modules */ static List module_list; static bool initialized = false; int mod_init(void) { if (!initialized) { if (!(module_list = list_create((ListDelF) _mod_destroy))) { err("Unable to create module list\n"); return -1; } initialized = true; return 0; } else return 0; } int mod_exit(void) { if (!initialized) return 0; /* * list_destroy() will call module destructor on each * element in list */ list_destroy(module_list); return 0; } hostlist_t _mod_read_wcoll(mod_t mod, opt_t *pdsh_opts) { if (mod->pmod->mod_ops && mod->pmod->mod_ops->read_wcoll) return (*mod->pmod->mod_ops->read_wcoll) (pdsh_opts); return 0; } int _mod_postop(mod_t mod, opt_t *pdsh_opts) { if (mod->pmod->mod_ops && mod->pmod->mod_ops->postop) return (*mod->pmod->mod_ops->postop) (pdsh_opts); return 0; } /* * Like list_next (i), but skip over inactive modules */ static mod_t _mod_next_active (ListIterator i) { mod_t mod; while ((mod = list_next (i))) { if (mod->initialized) return (mod); } return (NULL); } /* * Call any "read wcoll" functions exported by modules. The module * is responsible for deciding when to generate a new wcoll, * append to existing wcoll, etc. (presumably based on the contents * of opt) * * This function appends to wcoll any new hosts returned from the * module specific read_wcoll() functions. * * Returns -1 on error, 0 for success. */ int mod_read_wcoll(opt_t *opt) { mod_t mod; ListIterator module_itr; if (!initialized) mod_init(); if (!(module_itr = list_iterator_create(module_list))) { err("Unable to create module list iterator\n"); return -1; } while ((mod = _mod_next_active (module_itr))) { hostlist_t hl = NULL; if (!(hl = _mod_read_wcoll(mod, opt))) continue; if (opt->wcoll != NULL) { hostlist_push_list(opt->wcoll, hl); hostlist_destroy(hl); } else opt->wcoll = hl; } list_iterator_destroy(module_itr); return 0; } int mod_postop(opt_t *pdsh_opts) { mod_t mod; int errors = 0; ListIterator module_itr; if (!initialized) mod_init(); if (!(module_itr = list_iterator_create(module_list))) { err("Unable to create module list iterator\n"); return 1; } while ((mod = _mod_next_active (module_itr))) errors += _mod_postop(mod, pdsh_opts); list_iterator_destroy(module_itr); return errors; } mod_t mod_create(void) { mod_t mod = Malloc(sizeof(*mod)); #if !STATIC_MODULES mod->handle = NULL; #endif mod->filename = NULL; mod->priority = DEFAULT_MODULE_PRIORITY; mod->initialized = 0; assert(mod->magic = MOD_MAGIC); return mod; } static void _mod_destroy(mod_t mod) { assert(mod->magic == MOD_MAGIC); /* must assert mod->mod, loading of module may have failed */ if (mod->pmod) { mod->pmod->type = NULL; mod->pmod->name = NULL; /* * Only run exit function if module was initialized */ if (mod->initialized && mod->pmod->mod_ops && mod->pmod->mod_ops->exit) (*mod->pmod->mod_ops->exit)(); } if (mod->filename) Free((void **) &mod->filename); #if !STATIC_MODULES # if !PREVENT_DLCLOSE_BUG if (mod->handle) dlclose(mod->handle); # endif #endif assert(mod->magic = ~MOD_MAGIC); Free((void **) &mod); return; } static bool _mod_opts_ok(mod_t mod) { if (!opt_register(mod->pmod->opt_table)) return false; return true; } static int _cmp_f (mod_t x, mod_t y) { if (x->priority == y->priority) return strcmp (x->pmod->name, y->pmod->name); return (y->priority - x->priority); } static int _mod_find_misc (mod_t mod, const char *name) { if (strcmp (mod->pmod->type, "misc") != 0) return 0; if (strcmp (mod->pmod->name, name) != 0) return 0; return 1; } static int _mod_initialize_by_name (char *name, List l) { mod_t mod = list_find_first (l, (ListFindF) _mod_find_misc, name); if (mod != NULL && _mod_initialize (mod) < 0) err("%p: Warning: Failed to initialize requested module \"%s/%s\"\n", mod->pmod->type, mod->pmod->name); return (0); } static int _mod_initialize_modules_by_name (char *names, List m) { List l; if (names == NULL || strlen(names) == 0) return (0); l = list_split (",", names); list_for_each (l, (ListForF) _mod_initialize_by_name, m); return (0); } int mod_load_modules(const char *dir, opt_t *opt) { int rc = 0; #if STATIC_MODULES rc = _mod_load_static_modules(); #else rc = _mod_load_dynamic_modules(dir, opt); #endif list_sort(module_list, (ListCmpF) _cmp_f); /* * Initialize misc modules by name */ _mod_initialize_modules_by_name (opt->misc_modules, module_list); /* * Initialize remaining modules in modules_list: */ list_for_each (module_list, (ListForF) _mod_init_list_safe, NULL); return(rc); } /* * Print all options from module option table 'p,' aligning description * with column 'col' */ static void _print_option_help(struct pdsh_module_option *p, int col) { char buf[81]; assert(p != NULL); snprintf(buf, 81, "-%c %-*s %s\n", p->opt, col - 4, (p->arginfo ? p->arginfo : ""), p->descr); err("%s", buf); } void mod_print_options(mod_t mod, int col) { struct pdsh_module_option *p; assert(mod != NULL); assert(mod->pmod != NULL); p = mod->pmod->opt_table; if (!p || !p->opt) return; /* * out("%s/%s Options:\n", mod->pmod->type, mod->pmod->name); */ for (p = mod->pmod->opt_table; p && (p->opt != 0); p++) _print_option_help(p, col); } /* * Print to stdout information stanza for module "mod" */ static int _mod_print_info(mod_t mod) { if (mod == NULL) return 0; out("Module: %s/%s\n", mod->pmod->type, mod->pmod->name); out("Author: %s\n", mod->pmod->author ? mod->pmod->author : "???"); out("Descr: %s\n", mod->pmod->descr ? mod->pmod->descr : "???"); out("Active: %s\n", mod->initialized ? "yes" : "no"); if (mod->pmod->opt_table && mod->pmod->opt_table->opt) { out("Options:\n"); mod_print_options(mod, 18); } out("\n"); return 0; } static int _opt_print(mod_t mod, int *col) { if (mod->initialized) mod_print_options(mod, *col); return 0; } void mod_print_all_options(int col) { list_for_each(module_list, (ListForF) _opt_print, &col); } static int _cmp_type(mod_t mod, char *type) { return (strcmp(mod->pmod->type, type) == 0); } int mod_count(char *type) { int i = 0; ListIterator module_itr; assert(module_list != NULL); if (type == NULL) return list_count(module_list); if (!(module_itr = list_iterator_create(module_list))) { err("Unable to create module list iterator\n"); return -1; } while (list_find(module_itr, (ListFindF) _cmp_type, type)) i++; list_iterator_destroy(module_itr); return i; } static List _mod_get_module_names(char *type, int get_active) { List l; mod_t mod; ListIterator module_itr; assert(module_list != NULL); l = list_create(NULL); if (!(module_itr = list_iterator_create(module_list))) { err("Unable to create module list iterator\n"); list_destroy(l); return NULL; } while ((mod = (type ? list_find(module_itr, (ListFindF) _cmp_type, type) : list_next(module_itr)))) { /* * Push active (initialized) modules onto list if get_active * is true, otherwise push inactive (!initialized) modules: */ if (!get_active == !mod->initialized) list_push(l, mod->pmod->name); } list_iterator_destroy(module_itr); return l; } List mod_get_module_names (char *type) { return _mod_get_module_names (type, 1); } List mod_get_uninitialized_module_names (char *type) { return _mod_get_module_names (type, 0); } void mod_list_module_info(void) { int nmodules = list_count(module_list); out("%d module%s loaded:\n\n", nmodules, (nmodules > 1 ? "s" : "")); if (nmodules == 0) return; list_for_each(module_list, (ListForF) _mod_print_info, NULL); } static int _mod_description_match (mod_t m, const char *type, const char *name) { if ( (strcmp(m->pmod->type, type) == 0) && (strcmp(m->pmod->name, name) == 0) ) return (1); return (0); } mod_t mod_get_module(const char *type, const char *name) { mod_t mod; ListIterator module_itr; assert(type != NULL); assert(name != NULL); if (!(module_itr = list_iterator_create(module_list))) { err("Unable to create module list iterator\n"); return NULL; } while ((mod = list_next(module_itr))) { if (_mod_description_match (mod, type, name)) break; } list_iterator_destroy(module_itr); return mod; } char * mod_get_name(mod_t mod) { assert(mod != NULL); assert(mod->pmod != NULL); return mod->pmod->name; } char * mod_get_type(mod_t mod) { assert(mod != NULL); assert(mod->pmod != NULL); return mod->pmod->type; } RcmdInitF mod_get_rcmd_init(mod_t mod) { assert(mod != NULL); assert(mod->pmod != NULL); if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_init) return mod->pmod->rcmd_ops->rcmd_init; else return NULL; } RcmdSigF mod_get_rcmd_signal(mod_t mod) { assert(mod != NULL); assert(mod->pmod != NULL); if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_signal) return mod->pmod->rcmd_ops->rcmd_signal; else return NULL; } RcmdF mod_get_rcmd(mod_t mod) { assert(mod != NULL); assert(mod->pmod != NULL); if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd) return mod->pmod->rcmd_ops->rcmd; else return NULL; } RcmdDestroyF mod_get_rcmd_destroy (mod_t mod) { assert (mod != NULL); assert (mod->pmod != NULL); if (mod->pmod->rcmd_ops && mod->pmod->rcmd_ops->rcmd_destroy) return mod->pmod->rcmd_ops->rcmd_destroy; else return NULL; } int mod_process_opt(opt_t *opt, int c, char *optarg) { mod_t mod; struct pdsh_module_option *p = NULL; ListIterator module_itr; if (!(module_itr = list_iterator_create(module_list))) { err("Unable to create module list iterator\n"); return -1; } while ((mod = _mod_next_active (module_itr))) { if ((p = _mod_find_opt(mod, c))) { list_iterator_destroy(module_itr); return p->f(opt, c, optarg); } } list_iterator_destroy(module_itr); return -1; } /* * Return pointer to pdsh_module_option struct for option `opt' * or NULL if no loaded module provides that option. */ static struct pdsh_module_option * _mod_find_opt(mod_t mod, int opt) { struct pdsh_module_option *p = mod->pmod->opt_table; for (p = mod->pmod->opt_table; p && (p->opt != 0); p++) if (p->opt == opt) return p; return NULL; } static int _mod_delete (const char *type, const char *name) { ListIterator i = list_iterator_create (module_list); mod_t m; while ((m = list_next (i))) { if (_mod_description_match (m, type, name)) list_delete (i); } return (0); } static int _mod_register (mod_t mod, const char *name) { mod_t prev; /* * Must have atleast a name and type */ if (!mod->pmod->type || !mod->pmod->name) { err("%p:[%s] type or name not specified in module\n", name); return -1; } /* * Check for existing module of the same type and name * Delete previous module if its priority is higher. */ if ((prev = mod_get_module (mod->pmod->type, mod->pmod->name))) { err("%p: %s: [%s/%s] already loaded from [%s]\n", mod->filename, mod->pmod->type, mod->pmod->name, prev->filename); if (mod->priority > prev->priority) _mod_delete (mod->pmod->type, mod->pmod->name); else return (-1); } /* * Continue with module loading only if personality acceptable */ if (!(mod->pmod->personality & pdsh_personality())) return -1; list_prepend(module_list, mod); return 0; } static int _mod_initialize (mod_t mod) { if (!_mod_opts_ok(mod)) return -1; if (mod->pmod->mod_ops && mod->pmod->mod_ops->init && ((*mod->pmod->mod_ops->init)() < 0)) { err("%p: error: %s/%s failed to initialize.\n", mod->pmod->type, mod->pmod->name); return -1; } mod->initialized = 1; return 0; } /* * Version of _mod_initialize that always returns zero * for use with list_for_each. */ static int _mod_init_list_safe (mod_t mod, void *arg) { _mod_initialize (mod); return 0; } #if STATIC_MODULES /* * Set pdsh module pointer (pmod) to point to address from * statically defined external static_mods array. */ static int _mod_load_static(int idx) { mod_t mod = mod_create(); mod->pmod = static_mods[idx]; mod->priority = *priority[idx]; mod->filename = Strdup("static"); _mod_register(mod, static_mod_names[idx]); return 0; } /* * Load all statically defined modules from internal static_mods array */ static int _mod_load_static_modules(void) { int i = 0; while (static_mods[i] != NULL) { if (_mod_load_static(i++) < 0) continue; } return 0; } #else /* !STATIC_MODULES */ /* * Load a single module from file `fq_path' and append to module_list. */ static int _mod_load_dynamic(const char *fq_path, opt_t *pdsh_opts) { mod_t mod = NULL; int *priority; assert(fq_path != NULL); mod = mod_create(); /* RTLD_GLOBAL is used here so that symbols in any libraries * linked to by the loaded module are global, such that any DSOs * dlopened in turn by those libraries can find symbols in * their parent library. This is specifically a problem with the * nodeupdown and slurm modules at this time. */ if (!(mod->handle = dlopen(fq_path, RTLD_GLOBAL | RTLD_NOW))) { if (pdsh_opts->debug) { err("%p: Can't load %s: %s\n", fq_path, dlerror()); } goto fail; } mod->filename = Strdup(fq_path); if (_is_loaded(mod->filename)) { /* Module already loaded. This is OK, no need for * error message. (Could have already opened a .la and * we are now opening the corresponding .so */ goto fail; } /* load all module info from the pdsh_module structure */ if (!(mod->pmod = dlsym(mod->handle, "pdsh_module_info"))) { err("%p:[%s] can't resolve pdsh module\n", mod->filename); goto fail; } if ((priority = dlsym(mod->handle, "pdsh_module_priority"))) mod->priority = *priority; if (_mod_register(mod, mod->filename) < 0) goto fail; return 0; fail: _mod_destroy(mod); return -1; } static int _pdsh_owner(const char *pdsh_path, uid_t *pdsh_uid) { struct stat st; if (stat (pdsh_path, &st) < 0) { err ("%p: Unable to determine ownership of pdsh binary: %m\n"); return -1; } *pdsh_uid = st.st_uid; return 0; } static int _mod_load_dynamic_modules(const char *dir, opt_t *pdsh_opts) { DIR *dirp = NULL; struct dirent *entry = NULL; char path[MAXPATHLEN + 1]; char *p; int count = 0; uid_t pdsh_owner = 0; assert(dir != NULL); assert(*dir != '\0'); if (!initialized) mod_init(); if (_pdsh_owner(pdsh_opts->local_program_path, &pdsh_owner) < 0) return -1; if (!_path_permissions_ok(dir, pdsh_owner)) return -1; if (!(dirp = opendir(dir))) return -1; strncpy(path, dir, MAXPATHLEN); p = path + strlen(dir); *(p++) = '/'; while ((entry = readdir(dirp))) { struct stat st; strcpy(p, entry->d_name); if (stat(path, &st) < 0) continue; if (!S_ISREG(st.st_mode)) continue; /* * Do not load modules that could have been altered by * a user other than root or the current user or the user * owning the pdsh executable. Otherwise pdsh could execute * arbitrary code. */ if ( (st.st_uid != 0) && (st.st_uid != getuid()) && (st.st_uid != pdsh_owner)) { err ("%p: skipping insecure module \"%s\" (check owner)\n", path); continue; } if (st.st_mode & S_IWOTH) { err ("%p: skipping insecure module \"%s\" (check perms)\n", path); continue; } if (_mod_load_dynamic(path, pdsh_opts) < 0) continue; count++; } if (closedir(dirp) < 0) err("%p: error closing %s: %m", dir); if (count == 0) errx("%p: no modules found\n"); return 0; } static int _cmp_filenames(mod_t mod, char *filename) { return (strcmp(mod->filename, filename) == 0); } static int _is_loaded(char *filename) { if (list_find_first(module_list, (ListFindF) _cmp_filenames, filename)) return 1; return 0; } static char * _perm_error_string (perm_error_t error) { switch (error) { case DIR_OK: return ("Permissions are valid"); case DIR_NOT_DIRECTORY: return ("Not a directory"); case DIR_BAD_OWNER: return ("Owner not root, current uid, or pdsh executable owner"); case DIR_WORLD_WRITABLE: return ("World writable and sticky bit is not set"); default: break; } return ("Unspecified error"); } /* * Return permissions error if stat buffer shows any of the below * 1. This is not a directory. * 2. Ownership something other than root, the current uid, or the * same ownership as the pdsh executable. * 3. Permissions are world writable and sticky bit is not set. */ static perm_error_t _dir_permission_error(struct stat *st, uid_t alt_uid) { if (!S_ISDIR(st->st_mode)) return DIR_NOT_DIRECTORY; if ( (st->st_uid != 0) && (st->st_uid != getuid()) && (st->st_uid != alt_uid)) return DIR_BAD_OWNER; if ((st->st_mode & S_IWOTH) && !(st->st_mode & S_ISVTX)) return DIR_WORLD_WRITABLE; return DIR_OK; } /* * Temprarily chdir() to path and use getcwd to return real patch * to caller. */ static char * _get_dir_name (const char *path, char *buf, size_t len) { int pathlen = 256; char * orig_path = Malloc (pathlen * sizeof (char)); while (!getcwd (orig_path, pathlen) && (pathlen < MAXPATHLEN*2)) Realloc ((void **) &orig_path, pathlen*=2 * sizeof (char)); if (chdir (path) < 0) errx ("Unable to chdir() to %s: %m", path); if (!getcwd (buf, len)) errx ("Unable to get working directory for module path: %s\n", path); if (chdir (orig_path) < 0) err ("Unable to return to original working directory: %s: %m\n", orig_path); Free ((void **) &orig_path); return (buf); } /* * Returns true if, for the directory "dir" and all of its parent * directories, the following are true: * - ownership is root or the calling user (as returned by getuid()) * or same ownership as the pdsh or pdcp binary. * - directory has user write permission only * * Returns false if one of the assertions above are false for any * directory in the tree. * */ static bool _path_permissions_ok(const char *dir, uid_t pdsh_owner) { struct stat st; char dirbuf[MAXPATHLEN + 1]; dev_t rootdev; ino_t rootino; perm_error_t error; int pos = 0; assert(dir != NULL); if (stat("/", &st) < 0) { err("%p: Can't stat root directory: %m\n"); return false; } rootdev = st.st_dev; rootino = st.st_ino; strncpy(dirbuf, dir, MAXPATHLEN); dirbuf[MAXPATHLEN] = '\0'; pos = strlen(dirbuf); do { if (stat(dirbuf, &st) < 0) { err("%p: Can't stat \"%s\": %m\n", dir); return false; } if ((error = _dir_permission_error(&st, pdsh_owner)) != DIR_OK) { char buf [MAXPATHLEN]; err("%p: module path \"%s\" insecure.\n", dir); err("%p: \"%s\": %s\n", _get_dir_name (dirbuf, buf, MAXPATHLEN), _perm_error_string (error)); return false; } /* Check for impending overflow */ if (pos > MAXPATHLEN - 3) { err("%p :-( Path too long while checking permissions\n"); return false; } /* Check parent */ strncpy(&dirbuf[pos], "/..", 4); pos+=3; } while ( !((st.st_ino == rootino) && (st.st_dev == rootdev)) ); return true; } #endif /* !STATIC_MODULES */ /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/testcase.c0000664€^–Á €^–Á 0000000706215131211226024217 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2002 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Internal unit tests called by DejaGNU. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/pipecmd.h" #include "src/common/fd.h" #include "dsh.h" typedef enum { FAIL, PASS } testresult_t; typedef testresult_t((*testfun_t) (void)); typedef struct { char *desc; testfun_t fun; } testcase_t; static testresult_t _test_xstrerrorcat(void); static testresult_t _test_pipecmd(void); static testcase_t testcases[] = { /* 0 */ {"xstrerrorcat", &_test_xstrerrorcat}, /* 1 */ {"pipecmd", &_test_pipecmd}, }; static void _testmsg(int testnum, testresult_t result) { out("%P: Test %d: %s: %s\n", testnum, testcases[testnum].desc, result == PASS ? "PASS" : "FAIL"); } static testresult_t _test_xstrerrorcat(void) { int e; testresult_t result = PASS; for (e = 1; e < 100; e++) { char *s1 = NULL; char *s2 = strerror(e); errno = e; xstrerrorcat(&s1); if (strcmp(s1, s2) != 0) { err ("xsterrorcat (errno=%d) = \"%s\" (should be \"%s\")\n", e, s1, s2); result = FAIL; } Free((void **) &s1); } return result; } static testresult_t _test_pipecmd(void) { const char expected[] = "host=foo0 user=foouser n=0"; const char *args[] = { "host=%h", "user=%u", "n=%n", NULL }; int n; char buf [1024]; pipecmd_t p; if (!(p = pipecmd ("/bin/echo", args, "foo0", "foouser", 0))) return FAIL; if ((n = fd_read_n (pipecmd_stdoutfd (p), buf, sizeof (buf))) < 0) return FAIL; buf [n-1] = '\0'; if (strcmp (expected, buf)) { err ("testcase: pipecmd: expected \"%s\" got \"%s\"\n", expected, buf); return FAIL; } pipecmd_wait (p, NULL); pipecmd_destroy (p); return PASS; } void testcase(int testnum) { testresult_t result; if (testnum < 0 || testnum >= (sizeof(testcases) / sizeof(testcase_t))) errx("%P: Test %d unknown\n", testnum); result = testcases[testnum].fun(); _testmsg(testnum, result); exit(0); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/cbuf.h0000664€^–Á €^–Á 0000003135215131211226023327 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id$ ***************************************************************************** * Copyright (C) 2002-2003 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * * This file is from LSD-Tools, the LLNL Software Development Toolbox. * * LSD-Tools is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * LSD-Tools is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with LSD-Tools; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #ifndef LSD_CBUF_H #define LSD_CBUF_H /*********** * Notes * ***********/ /* * Cbuf is a circular-buffer capable of dynamically resizing itself. * Unread data in the buffer will be overwritten once the cbuf has * reached its maximum size or is unable to allocate additional memory. * * The CBUF_OPT_OVERWRITE option specifies how unread cbuf data will * be overwritten. If set to CBUF_NO_DROP, unread data will never be * overwritten; writes into the cbuf will return -1 with ENOSPC. If set * to CBUF_WRAP_ONCE, a single write operation will wrap-around the buffer * at most once, and up to cbuf_used() bytes of data may be overwritten. * If set to CBUF_WRAP_MANY, a single write operation will wrap-around the * buffer as many times as needed in order to write all of the data. * * If NDEBUG is not defined, internal debug code will be enabled. This is * intended for development use only and production code should define NDEBUG. * * If WITH_LSD_FATAL_ERROR_FUNC is defined, the linker will expect to * find an external lsd_fatal_error(file,line,mesg) function. By default, * lsd_fatal_error(file,line,mesg) is a macro definition that outputs an * error message to stderr. This macro may be redefined to invoke another * routine instead. * * If WITH_LSD_NOMEM_ERROR_FUNC is defined, the linker will expect to * find an external lsd_nomem_error(file,line,mesg) function. By default, * lsd_nomem_error(file,line,mesg) is a macro definition that returns NULL. * This macro may be redefined to invoke another routine instead. * * If WITH_PTHREADS is defined, these routines will be thread-safe. */ /**************** * Data Types * ****************/ typedef struct cbuf * cbuf_t; /* circular-buffer opaque data type */ typedef enum { /* cbuf option names */ CBUF_OPT_OVERWRITE } cbuf_opt_t; typedef enum { /* CBUF_OPT_OVERWRITE values: */ CBUF_NO_DROP, /* -never drop data, ENOSPC if full */ CBUF_WRAP_ONCE, /* -drop data, wrapping at most once */ CBUF_WRAP_MANY /* -drop data, wrapping as needed */ } cbuf_overwrite_t; /*************** * Functions * ***************/ cbuf_t cbuf_create (int minsize, int maxsize); /* * Creates and returns a new circular buffer, or lsd_nomem_error() on failure. * The buffer is initially allocated to hold [minsize] bytes of data, * but can attempt to grow up to [maxsize] bytes before overwriting data. * Set minsize = maxsize to prevent cbuf from dynamically resizing itself. * The default overwrite option behavior is CBUF_WRAP_MANY. * Abandoning a cbuf without calling cbuf_destroy() will cause a memory leak. */ void cbuf_destroy (cbuf_t cb); /* * Destroys the circular buffer [cb]. */ void cbuf_flush (cbuf_t cb); /* * Flushes all data (including replay data) in [cb]. */ int cbuf_size (cbuf_t cb); /* * Returns the current size of the buffer allocated to [cb] * (ie, the number of bytes in can currently hold). */ int cbuf_free (cbuf_t cb); /* * Returns the number of bytes in [cb] available for writing before * unread data is overwritten (unless the cbuf is able to resize itself). */ int cbuf_used (cbuf_t cb); /* * Returns the number of bytes in [cb] available for reading. */ int cbuf_lines_used (cbuf_t cb); /* * Returns the number of lines in [cb] available for reading. */ int cbuf_reused (cbuf_t cb); /* * Returns the number of bytes in [cb] available for replaying/rewinding. */ int cbuf_lines_reused (cbuf_t cb); /* * Returns the number of lines in [cb] available for replaying/rewinding. */ int cbuf_is_empty (cbuf_t cb); /* * Returns non-zero if [cb] is empty; o/w, returns zero. */ int cbuf_opt_get (cbuf_t cb, cbuf_opt_t name, int *value); /* * Gets the [name] option for [cb] and sets [value] to the result. * Returns 0 on success, or -1 on error (with errno set). */ int cbuf_opt_set (cbuf_t cb, cbuf_opt_t name, int value); /* * Sets the [name] option for [cb] to [value]. * Returns 0 on success, or -1 on error (with errno set). */ int cbuf_drop (cbuf_t src, int len); /* * Discards up to [len] bytes of unread data from [src]; * if [len] is -1, all unread data will be dropped. * Dropped data is still available via the replay buffer. * Returns the number of bytes dropped, or -1 on error (with errno set). */ int cbuf_peek (cbuf_t src, void *dstbuf, int len); /* * Reads up to [len] bytes of data from the [src] cbuf into [dstbuf], * but does not consume the data read from the cbuf. * The "peek" can be committed to the cbuf via a call to cbuf_drop(), * but the peek+drop combination is not atomic. * Returns the number of bytes read, or -1 on error (with errno set). */ int cbuf_read (cbuf_t src, void *dstbuf, int len); /* * Reads up to [len] bytes of data from the [src] cbuf into [dstbuf]. * Returns the number of bytes read, or -1 on error (with errno set). */ int cbuf_replay (cbuf_t src, void *dstbuf, int len); /* * Replays up to [len] bytes of previously read data from the [src] cbuf * into [dstbuf]. * Returns the number of bytes replayed, or -1 on error (with errno set). */ int cbuf_rewind (cbuf_t src, int len); /* * Rewinds [src] by up to [len] bytes, placing previously read data back in * the unread data buffer; if [len] is -1, all replay data will be rewound. * Returns the number of bytes rewound, or -1 on error (with errno set). */ int cbuf_write (cbuf_t dst, void *srcbuf, int len, int *ndropped); /* * Writes up to [len] bytes of data from [srcbuf] into the [dst] cbuf * according to dst's CBUF_OPT_OVERWRITE behavior. * Returns the number of bytes written, or -1 on error (with errno set). * Sets [ndropped] (if not NULL) to the number of bytes overwritten. */ int cbuf_drop_line (cbuf_t src, int len, int lines); /* * Discards the specified [lines] of data from [src]. If [lines] is -1, * discards the maximum number of lines comprised of up to [len] characters. * Dropped data is still available via the replay buffer. * Returns the number of bytes dropped, or -1 on error (with errno set). * Returns 0 if the number of lines is not available (ie, all or none). */ int cbuf_peek_line (cbuf_t src, char *dstbuf, int len, int lines); /* * Reads the specified [lines] of data from the [src] cbuf into [dstbuf], * but does not consume the data read from the cbuf. If [lines] is -1, * reads the maximum number of lines that [dstbuf] can hold. The buffer * will be NUL-terminated and contain at most ([len] - 1) characters. * The "peek" can be committed to the cbuf via a call to cbuf_drop(), * but the peek+drop combination is not atomic. * Returns strlen of the line(s) on success; truncation occurred if >= [len]. * Returns 0 if the number of lines is not available (ie, all or none). * Returns -1 on error (with errno set). */ int cbuf_read_line (cbuf_t src, char *dstbuf, int len, int lines); /* * Reads the specified [lines] of data from the [src] cbuf into [dstbuf]. * If [lines] is -1, reads the maximum number of lines that [dstbuf] * can hold. The buffer will be NUL-terminated and contain at most * ([len] - 1) characters. * Returns strlen of the line(s) on success; truncation occurred if >= [len], * in which case excess line data is discarded. Returns 0 if the number * of lines is not available (ie, all or none), in which case no data is * consumed. Returns -1 on error (with errno set). */ int cbuf_replay_line (cbuf_t src, char *dstbuf, int len, int lines); /* * Replays the specified [lines] of data from the [src] cbuf into [dstbuf]. * If [lines] is -1, replays the maximum number of lines that [dstbuf] * can hold. A newline will be appended to [dstbuf] if the last (ie, most * recently read) line does not contain a trailing newline. The buffer * will be NUL-terminated and contain at most ([len] - 1) characters. * Returns strlen of the line(s) on success; truncation occurred if >= [len]. * Returns 0 if the number of lines is not available (ie, all or none). * Returns -1 on error (with errno set). */ int cbuf_rewind_line (cbuf_t src, int len, int lines); /* * Rewinds [src] by the specified [lines] of data, placing previously read * data back in the unread data buffer. If [lines] is -1, rewinds the * maximum number of lines comprised of up to [len] characters. * Returns the number of bytes rewound, or -1 on error (with errno set). * Returns 0 if the number of lines is not available (ie, all or none). */ int cbuf_write_line (cbuf_t dst, char *srcbuf, int *ndropped); /* * Writes the entire NUL-terminated [srcbuf] string into the [dst] cbuf * according to dst's CBUF_OPT_OVERWRITE behavior. A newline will be * appended to the cbuf if [srcbuf] does not contain a trailing newline. * Returns the number of bytes written, or -1 or error (with errno set). * Sets [ndropped] (if not NULL) to the number of bytes overwritten. */ int cbuf_peek_to_fd (cbuf_t src, int dstfd, int len); /* * Reads up to [len] bytes of data from the [src] cbuf into the file * referenced by the [dstfd] file descriptor, but does not consume the * data read from the cbuf. If [len] is -1, it will be set to the number * of [src] bytes available for reading. * The "peek" can be committed to the cbuf via a call to cbuf_drop(), * but the peek+drop combination is not atomic. * Returns the number of bytes read, or -1 on error (with errno set). */ int cbuf_read_to_fd (cbuf_t src, int dstfd, int len); /* * Reads up to [len] bytes of data from the [src] cbuf into the file * referenced by the [dstfd] file descriptor. If [len] is -1, it will * be set to the number of [src] bytes available for reading. * Returns the number of bytes read, or -1 on error (with errno set). */ int cbuf_replay_to_fd (cbuf_t src, int dstfd, int len); /* * Replays up to [len] bytes of previously read data from the [src] cbuf into * the file referenced by the [dstfd] file descriptor. If [len] is -1, it * will be set to the maximum number of [src] bytes available for replay. * Returns the number of bytes replayed, or -1 on error (with errno set). */ int cbuf_write_from_fd (cbuf_t dst, int srcfd, int len, int *ndropped); /* * Writes up to [len] bytes of data from the file referenced by the * [srcfd] file descriptor into the [dst] cbuf according to dst's * CBUF_OPT_OVERWRITE behavior. If [len] is -1, it will be set to * an appropriate chunk size. * Returns the number of bytes written, 0 on EOF, or -1 on error (with errno). * Sets [ndropped] (if not NULL) to the number of bytes overwritten. */ int cbuf_copy (cbuf_t src, cbuf_t dst, int len, int *ndropped); /* * Copies up to [len] bytes of data from the [src] cbuf into the [dst] cbuf * according to dst's CBUF_OPT_OVERWRITE behavior. If [len] is -1, * it will be set to the number of [src] bytes available for reading. * Returns the number of bytes copied, or -1 on error (with errno set). * Sets [ndropped] (if not NULL) to the number of [dst] bytes overwritten. */ int cbuf_move (cbuf_t src, cbuf_t dst, int len, int *ndropped); /* * Moves up to [len] bytes of data from the [src] cbuf into the [dst] cbuf * according to dst's CBUF_OPT_OVERWRITE behavior. If [len] is -1, * it will be set to the number of [src] bytes available for reading. * Returns the number of bytes moved, or -1 on error (with errno set). * Sets [ndropped] (if not NULL) to the number of [dst] bytes overwritten. */ #endif /* !LSD_CBUF_H */ pdsh-2.36/src/pdsh/pcp_client.c0000664€^–Á €^–Á 0000003054215131211226024523 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Copyright (c) 1983, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Advertising clause removed per the following letter: * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if HAVE_CONFIG_H # include "config.h" #endif #include /* roundup() */ #if HAVE_SYS_SYSMACROS_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "src/common/err.h" #include "src/common/fd.h" #include "src/common/list.h" #include "src/common/xstring.h" #include "src/common/err.h" #include "src/common/xmalloc.h" #include "pcp_client.h" #include "wcoll.h" #ifndef MAXPATHNAMELEN #define MAXPATHNAMELEN MAXPATHLEN #endif static void _rexpand_dir(List list, char *name) { DIR *dir; struct dirent *dp; struct stat sb; char file[MAXPATHNAMELEN]; struct pcp_filename *pf = NULL; dir = opendir(name); if (dir == NULL) errx("%p: opendir: %s: %m\n", name); while ((dp = readdir(dir))) { if (dp->d_ino == 0) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; snprintf(file, sizeof(file), "%s/%s", name, dp->d_name); if (stat(file, &sb) < 0) errx("%p: can't stat %s: %m\n", file); if (access(name, R_OK) < 0) errx("%p: access: %s: %m\n", name); if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) errx("%p: not a regular file or directory: %s\n", file); /* XXX: This memleaks */ pf = Malloc(sizeof(struct pcp_filename)); pf->filename = Strdup(file); pf->file_specified_by_user = 0; list_append(list, pf); if (S_ISDIR(sb.st_mode)) _rexpand_dir(list, file); } closedir(dir); /* Since pdcp reads file names and directories only once for * efficiency, we must specify a special flag so we know when * to tell the server to "move up" the directory tree. */ /* XXX: This memleaks */ pf = Malloc(sizeof(struct pcp_filename)); pf->filename = Strdup(EXIT_SUBDIR_FILENAME); pf->file_specified_by_user = 0; list_append(list, pf); } List pcp_expand_dirs(List infiles) { List new = list_create(NULL); struct stat sb; char *name; ListIterator i; i = list_iterator_create(infiles); while ((name = list_next(i))) { struct pcp_filename *pf = NULL; if (access(name, R_OK) < 0) errx("%p: access: %s: %m\n", name); if (stat(name, &sb) < 0) errx("%p: stat: %s: %m\n", name); /* XXX: This memleaks */ pf = Malloc(sizeof(struct pcp_filename)); pf->filename = name; pf->file_specified_by_user = 1; list_append(new, pf); /* -r option checked during command line argument checks */ if (S_ISDIR(sb.st_mode)) _rexpand_dir(new, name); } return new; } /* * Wrapper for the write system call that handles short writes. * Not sure if write ever returns short in practice but we have to be sure. * fd (IN) file descriptor to write to * buf (IN) data to write * size (IN) size of buf * RETURN -1 on failure, size on success */ static int _pcp_write(int fd, char *buf, int size) { char *bufp = buf; int towrite = size; int outbytes; while (towrite > 0) { outbytes = write(fd, bufp, towrite); if (outbytes <= 0) { assert(outbytes != 0); return -1; } towrite -= outbytes; bufp += outbytes; } return size; } /* * Write the contents of the named file to the specified file descriptor. * outfd (IN) file descriptor to write to * filename (IN) name of file * host (IN) name of remote host for error messages * RETURN -1 on failure, 0 on success. */ static int _pcp_send_file_data(int outfd, char *filename, char *host) { int filefd, inbytes, total = 0; char tmpbuf[BUFSIZ]; filefd = open(filename, O_RDONLY); /* checked ahead of time - shouldn't happen */ if (filefd < 0) { err("%S: _pcp_send_file_data: open %s: %m\n", host, filename); return -1; } do { inbytes = read(filefd, tmpbuf, BUFSIZ); if (inbytes < 0) { err("%S: _pcp_send_file_data: read %s: %m\n", host, filename); return -1; } if (inbytes > 0) { total += inbytes; if (_pcp_write(outfd, tmpbuf, inbytes) < 0) { err("%S: _pcp_send_file_data: write: %m\n", host); return -1; } } } while (inbytes > 0); /* until EOF */ close(filefd); return 0; } /* * Send string to the specified file descriptor. Do not send trailing '\0' * as RCP terminates strings with newlines. * fd (IN) file descriptor to write to * str (IN) string to write * host (IN) name of remote host for error messages * RETURN -1 on failure, 0 on success */ static int pcp_sendstr(int outfd, char *str, char *host) { int n; assert(strlen(str) > 0); assert(str[strlen(str) - 1] == '\n'); if ((n = _pcp_write(outfd, str, strlen(str))) < 0) return -1; assert(n == strlen(str)); return 0; } /* * Receive an RCP response code and possibly error message. * fd (IN) file desciptor to read from * host (IN) hostname for error messages * RETURN -1 on fatal error, 0 otherwise */ static int pcp_response(int infd, char *host) { char resp; int i = 0, result = -1; int n; char errstr[BUFSIZ]; if ((n = read(infd, &resp, sizeof(resp))) != sizeof(resp)) return (-1); switch (resp) { case 0: /* ok */ result = 0; break; default: /* just error string */ errstr[i++] = resp; result = 0; case 1: /* fatal error + string */ fd_read_line (infd, &errstr[i], BUFSIZ - i); err("%p: %S: %s: %s", host, result ? "fatal" : "error", errstr); break; } return result; } #define RCP_MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) int pcp_sendfile(struct pcp_client *pcp, char *file, char *output_file) { int result = 0; char tmpstr[BUFSIZ], *template; struct stat sb; if (output_file == NULL) output_file = file; /*err("%S: %s\n", host, file); */ if (stat(file, &sb) < 0) { err("%S: %s: %m\n", pcp->host, file); goto fail; } if (pcp->preserve) { /* * 1: SEND stat time: "T%ld %ld %ld %ld\n" * (st_mtime, st_mtime_usec, st_atime, st_atime_usec) */ snprintf(tmpstr, sizeof(tmpstr), "T%ld %ld %ld %ld\n", (long) sb.st_mtime, 0L, sb.st_atime, 0L); if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0) goto fail; /* 2: RECV response code */ if (pcp_response(pcp->infd, pcp->host) < 0) goto fail; } if (S_ISDIR(sb.st_mode)) { /* * 3a: SEND directory mode: "D%04o %d %s\n" * (st_mode & RCP_MODEMASK, 0, name) */ snprintf(tmpstr, sizeof(tmpstr), "D%04o %d %s\n", sb.st_mode & RCP_MODEMASK, 0, xbasename(output_file)); if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0) goto fail; } else { /* * 3b: SEND file mode: "C%04o %lld %s\n" or "C%04o %ld %s\n" * (st_mode & MODE_MASK, st_size, basename(filename)) * Use second template if sizeof(st_size) > sizeof(long). */ template = (sizeof(sb.st_size) > sizeof(long) ? "C%04o %lld %s\n" : "C%04o %ld %s\n"); snprintf(tmpstr, sizeof(tmpstr), template, sb.st_mode & RCP_MODEMASK, sb.st_size, xbasename(output_file)); if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0) goto fail; } /* 4: RECV response code */ if (pcp_response(pcp->infd, pcp->host) < 0) goto fail; if (S_ISREG(sb.st_mode)) { /* 5: SEND data */ if (_pcp_send_file_data(pcp->outfd, file, pcp->host) < 0) goto fail; /* 6: SEND NULL byte */ if (_pcp_write(pcp->outfd, "", 1) < 0) goto fail; /* 7: RECV response code */ if (pcp_response(pcp->infd, pcp->host) < 0) goto fail; } result = 1; /* indicate success */ fail: return result; } static int _pcp_sendfile (struct pcp_filename *pf, struct pcp_client *pcp) { char *output_filename = NULL; if (strcmp(pf->filename, EXIT_SUBDIR_FILENAME) == 0) { if (pcp_sendstr(pcp->outfd, EXIT_SUBDIR_FLAG, pcp->host) < 0) errx("%p: failed to send exit subdir flag\n"); if (pcp_response(pcp->infd, pcp->host) < 0) errx("%p: failed to exit subdir properly\n"); return (0); } /* during a reverse copy, the hostname has to be attached * to the end of the output filename for files specified * by the user. */ if (pcp->pcp_client && pf->file_specified_by_user) { output_filename = Strdup(pf->filename); xstrcat(&output_filename, "."); xstrcat(&output_filename, pcp->host); } pcp_sendfile (pcp, pf->filename, output_filename); return (0); } int pcp_client(struct pcp_client *pcp) { /* 0: RECV response code */ if (pcp_response(pcp->infd, pcp->host) >= 0) { struct pcp_filename *pf; ListIterator i = list_iterator_create (pcp->infiles); while ((pf = list_next (i))) _pcp_sendfile (pf, pcp); list_iterator_destroy (i); return 0; } return -1; } pdsh-2.36/src/pdsh/pcp_server.c0000664€^–Á €^–Á 0000003313115131211226024550 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ /* * Copyright (c) 1983, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Advertising clause removed per the following letter: * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ char copyright[] = "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n" "All rights reserved.\n"; /* * From: @(#)rcp.c 5.32 (Berkeley) 2/25/91 */ char rcsid[] = "$Id$"; /* #include "../version.h" */ #if HAVE_CONFIG_H # include "config.h" #endif #include /* roundup() */ #if HAVE_SYS_SYSMACROS_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "src/common/err.h" #include "pcp_server.h" #include "opt.h" #ifndef roundup # define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #endif /* The majority of the code below is unchanged from the original * rcp code. Changes include: * - rcp bug fix * - removal of conditions that are impossible to hit in pdcp * - update error messages to use pdcp error functions * - minor changes to enhance readability and fit style to rest * of pdsh/pdcp code. * - pass infd/outfd through structure rather than global * - don't exit on error, just return */ typedef struct _buf { int cnt; char *buf; } BUF; static int _verifydir(struct pcp_server *s, const char *cp); static int _response(struct pcp_server *s); static BUF *_allocbuf(struct pcp_server *s, BUF *bp, int fd, int blksize); static void _error(struct pcp_server *s, const char *fmt, ...); static void _sink(struct pcp_server *s, char *targ, BUF *bufp); static int _verifydir(struct pcp_server *s, const char *cp) { struct stat stb; if (stat(cp, &stb) >= 0) { if ((stb.st_mode & S_IFMT) == S_IFDIR) return 0; errno = ENOTDIR; } _error(s, "%s not a directory\n", cp); return -1; } static int _response(struct pcp_server *s) { char resp; if (read(s->infd, &resp, sizeof(resp)) != sizeof(resp)) { _error(s, "lost connection\n"); return -1; } switch(resp) { case 0: /* ok */ return 0; default: _error(s, "invalid response received\n"); return -1; } /*NOTREACHED*/ return 0; } static BUF * _allocbuf(struct pcp_server *s, BUF *bp, int fd, int blksize) { struct stat stb; int size; if (fstat(fd, &stb) < 0) { _error(s, "fstat: %m\n"); return NULL; } size = roundup(stb.st_blksize, blksize); if (size == 0) size = blksize; if (bp->cnt < size) { if (bp->buf != 0) free(bp->buf); bp->buf = malloc(size); if (!bp->buf) { _error(s, "malloc: out of memory\n"); bp->cnt = 0; return NULL; } } bp->cnt = size; return(bp); } static void _error(struct pcp_server *s, const char *fmt, ...) { static FILE *fp = NULL; char newfmt[1000]; va_list ap; int save_errno = errno; /* errno could be changed by fopen */ if (!(fp = fdopen(s->outfd, "w"))) return; va_start(ap, fmt); /* must put "1" at beginning of the format to indicate an error */ snprintf(newfmt, 1000, "%c%s", 0x01, fmt); errno = save_errno; errf(fp, newfmt, ap); va_end(ap); fflush(fp); } static void _sink(struct pcp_server *svr, char *targ, BUF *bufp) { register char *cp; struct stat stb; struct timeval tv[2]; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i, j, size; char ch; const char *why = "failed to set 'why' string"; int amt, count, exists, mask, mode; int ofd, setimes, targisdir, cursize = 0; char *np, *buf = NULL, *namebuf = NULL; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } if (!(buf = malloc(BUFSIZ))) { _error(svr, "out of memory for buf: %m\n"); return; } setimes = targisdir = 0; mask = umask(0); if (!svr->preserve) (void)umask(mask); if (svr->target_is_dir) { if (_verifydir(svr, svr->outfile) < 0) return; } if (write(svr->outfd, "", 1) != 1) SCREWUP("write failed"); if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) targisdir = 1; while (1) { int rc; cp = buf; if ((rc = read(svr->infd, cp, 1)) <= 0) goto end_server; if (*cp++ == '\n') SCREWUP("unexpected "); do { if (read(svr->infd, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); *cp = 0; if (buf[0] == '\01' || buf[0] == '\02') { if (buf[0] == '\02') goto end_server; continue; } if (buf[0] == 'E') { if (write(svr->outfd, "", 1) != 1) SCREWUP("write failed"); goto end_server; } if (ch == '\n') *--cp = 0; #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); cp = buf; if (*cp == 'T') { setimes++; cp++; getnum(mtime.tv_sec); if (*cp++ != ' ') SCREWUP("mtime.sec not delimited"); getnum(mtime.tv_usec); if (*cp++ != ' ') SCREWUP("mtime.usec not delimited"); getnum(atime.tv_sec); if (*cp++ != ' ') SCREWUP("atime.sec not delimited"); getnum(atime.tv_usec); if (*cp++ != '\0') SCREWUP("atime.usec not delimited"); if (write(svr->outfd, "", 1) != 1) SCREWUP("write failed"); continue; } if (*cp != 'C' && *cp != 'D') SCREWUP("expected control record"); mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); size = 0; while (isdigit(*cp)) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); /* filename is "retrieved" in this if/else block */ if (targisdir) { /* achu: The original rcp code here was completely whack. * Memory was allocated for every file, memory was never * freed, cursize was never set to the current buffer * size, and a code path existed that could write past * allocated memory boundaries. Lots and lots of fixes * here. Atleast one person on google-groups concurs with * my thoughts. */ int need; need = strlen(targ) + strlen(cp) + 250; if (need > cursize) { if (namebuf) free(namebuf); if (!(namebuf = malloc(need))) { _error(svr, "out of memory\n"); cursize = 0; /* original rcp may not work with a continue here, * but it will work with pdcp protocol. */ continue; } cursize = need; } (void)snprintf(namebuf, cursize, "%s%s%s", targ, *targ ? "/" : "", cp); np = namebuf; } else np = targ; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { if (exists) { if ((stb.st_mode & S_IFMT) != S_IFDIR) { errno = ENOTDIR; goto bad; } if (svr->preserve) (void)chmod(np, mode); } else if (mkdir(np, mode) < 0) goto bad; /* recursively go down a directory */ _sink(svr, np, bufp); if (setimes) { setimes = 0; if (utimes(np, tv) < 0) _error(svr, "can't set times on %s: %m\n", np); } continue; } if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { bad: _error(svr, "%s: %m\n", np); continue; } if (exists && svr->preserve) (void)fchmod(ofd, mode); if (write(svr->outfd, "", 1) != 1) _error(svr, "failed to write to outfd: %m\n"); if ((bp = _allocbuf(svr, bufp, ofd, BUFSIZ)) == NULL) { (void)close(ofd); continue; } cp = bp->buf; count = 0; wrerr = NO; for (i = 0; i < size; i += BUFSIZ) { amt = BUFSIZ; if (i + amt > size) amt = size - i; count += amt; do { j = read(svr->infd, cp, amt); if (j <= 0) { _error(svr, "%m\n"); goto end_server; } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { if (wrerr == NO && write(ofd, bp->buf, count) != count) wrerr = YES; count = 0; cp = bp->buf; } } if (count != 0 && wrerr == NO && write(ofd, bp->buf, count) != count) wrerr = YES; if (ftruncate(ofd, size)) { _error(svr, "can't truncate %s: %m\n", np); wrerr = DISPLAYED; } (void)close(ofd); if (_response(svr) < 0) goto end_server; if (setimes && wrerr == NO) { setimes = 0; if (utimes(np, tv) < 0) { _error(svr, "can't set times on %s: %m\n", np); wrerr = DISPLAYED; } } switch(wrerr) { case YES: _error(svr, "%s: %m\n", np); break; case NO: if (write(svr->outfd, "", 1) != 1) _error(svr, "write failed to outfd: %m\n"); break; case DISPLAYED: break; } } screwup: _error(svr, "protocol screwup: %s\n", why); end_server: if (buf) free(buf); if (namebuf) free(namebuf); return; } int pcp_server(struct pcp_server *svr) { BUF buffer; memset (&buffer, 0, sizeof (buffer)); /* If reverse copy, outfile is always a directory. */ _sink (svr, svr->outfile, &buffer); if (buffer.buf) free (buffer.buf); return 0; } pdsh-2.36/src/pdsh/pcp_server.h0000664€^–Á €^–Á 0000000305415131211226024556 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _PCP_SERVER_H #define _PCP_SERVER_H #if HAVE_CONFIG_H # include #endif #include "src/pdsh/opt.h" struct pcp_server { int infd; int outfd; bool preserve; bool target_is_dir; char *outfile; }; int pcp_server (struct pcp_server *s); #endif /* _PCP_SERVER_H */ pdsh-2.36/src/pdsh/cbuf.c0000664€^–Á €^–Á 0000013433515131211226023327 0ustar arif.ali@canonical.comarif.ali@canonical.com/***************************************************************************** * $Id$ ***************************************************************************** * Copyright (C) 2002-2003 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Chris Dunlap . * * This file is from LSD-Tools, the LLNL Software Development Toolbox. * * LSD-Tools is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * LSD-Tools is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with LSD-Tools; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***************************************************************************** * Refer to "cbuf.h" for documentation on public functions. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef WITH_PTHREADS # include #endif /* WITH_PTHREADS */ #include #include #include #include #include #include "cbuf.h" #define _unused(x) ((void)(x)) /********************* * lsd_fatal_error * *********************/ #ifdef WITH_LSD_FATAL_ERROR_FUNC # undef lsd_fatal_error extern void lsd_fatal_error(char *file, int line, char *mesg); #else /* !WITH_LSD_FATAL_ERROR_FUNC */ # ifndef lsd_fatal_error # include # include # include # define lsd_fatal_error(file, line, mesg) \ do { \ fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \ file, line, mesg, strerror(errno)); \ } while (0) # endif /* !lsd_fatal_error */ #endif /* !WITH_LSD_FATAL_ERROR_FUNC */ /********************* * lsd_nomem_error * *********************/ #ifdef WITH_LSD_NOMEM_ERROR_FUNC # undef lsd_nomem_error extern void * lsd_nomem_error(char *file, int line, char *mesg); #else /* !WITH_LSD_NOMEM_ERROR_FUNC */ # ifndef lsd_nomem_error # define lsd_nomem_error(file, line, mesg) (NULL) # endif /* !lsd_nomem_error */ #endif /* !WITH_LSD_NOMEM_ERROR_FUNC */ /*************** * Constants * ***************/ #define CBUF_CHUNK 1000 #define CBUF_MAGIC 0xDEADBEEF #define CBUF_MAGIC_LEN (sizeof(unsigned long)) /**************** * Data Types * ****************/ struct cbuf { #ifndef NDEBUG unsigned long magic; /* cookie for asserting validity */ #endif /* !NDEBUG */ #ifdef WITH_PTHREADS pthread_mutex_t mutex; /* mutex to protect access to cbuf */ #endif /* WITH_PTHREADS */ int alloc; /* num bytes malloc'd/realloc'd */ int minsize; /* min bytes of data to allocate */ int maxsize; /* max bytes of data to allocate */ int size; /* num bytes of data allocated */ int used; /* num bytes of unread data */ cbuf_overwrite_t overwrite; /* overwrite option behavior */ int got_wrap; /* true if data has wrapped */ int i_in; /* index to where data is written in */ int i_out; /* index to where data is read out */ int i_rep; /* index to where data is replayable */ unsigned char *data; /* ptr to circular buffer of data */ }; typedef int (*cbuf_iof) (void *cbuf_data, void *arg, int len); /**************** * Prototypes * ****************/ static int cbuf_find_replay_line (cbuf_t cb, int chars, int *nlines, int *nl); static int cbuf_find_unread_line (cbuf_t cb, int chars, int *nlines); static int cbuf_get_fd (void *dstbuf, int *psrcfd, int len); static int cbuf_get_mem (void *dstbuf, unsigned char **psrcbuf, int len); static int cbuf_put_fd (void *srcbuf, int *pdstfd, int len); static int cbuf_put_mem (void *srcbuf, unsigned char **pdstbuf, int len); static int cbuf_copier (cbuf_t src, cbuf_t dst, int len, int *ndropped); static int cbuf_dropper (cbuf_t cb, int len); static int cbuf_reader (cbuf_t src, int len, cbuf_iof putf, void *dst); static int cbuf_replayer (cbuf_t src, int len, cbuf_iof putf, void *dst); static int cbuf_writer (cbuf_t dst, int len, cbuf_iof getf, void *src, int *ndropped); static int cbuf_grow (cbuf_t cb, int n); static int cbuf_shrink (cbuf_t cb); #ifndef NDEBUG static int cbuf_is_valid (cbuf_t cb); #endif /* !NDEBUG */ /************ * Macros * ************/ #ifndef MAX # define MAX(x,y) (((x) >= (y)) ? (x) : (y)) #endif /* !MAX */ #ifndef MIN # define MIN(x,y) (((x) <= (y)) ? (x) : (y)) #endif /* !MIN */ #ifdef WITH_PTHREADS # define cbuf_mutex_init(cb) \ do { \ int e = pthread_mutex_init(&cb->mutex, NULL); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex init"); \ abort(); \ } \ } while (0) # define cbuf_mutex_lock(cb) \ do { \ int e = pthread_mutex_lock(&cb->mutex); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex lock"); \ abort(); \ } \ } while (0) # define cbuf_mutex_unlock(cb) \ do { \ int e = pthread_mutex_unlock(&cb->mutex); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex unlock"); \ abort(); \ } \ } while (0) # define cbuf_mutex_destroy(cb) \ do { \ int e = pthread_mutex_destroy(&cb->mutex); \ if (e) { \ errno = e; \ lsd_fatal_error(__FILE__, __LINE__, "cbuf mutex destroy"); \ abort(); \ } \ } while (0) # ifndef NDEBUG static int cbuf_mutex_is_locked (cbuf_t cb); # endif /* !NDEBUG */ #else /* !WITH_PTHREADS */ # define cbuf_mutex_init(cb) # define cbuf_mutex_lock(cb) # define cbuf_mutex_unlock(cb) # define cbuf_mutex_destroy(cb) # define cbuf_mutex_is_locked(cb) (1) #endif /* !WITH_PTHREADS */ /*************** * Functions * ***************/ cbuf_t cbuf_create (int minsize, int maxsize) { cbuf_t cb; if (minsize <= 0) { errno = EINVAL; return(NULL); } if (!(cb = malloc(sizeof(struct cbuf)))) { errno = ENOMEM; return(lsd_nomem_error(__FILE__, __LINE__, "cbuf struct")); } /* Circular buffer is empty when (i_in == i_out), * so reserve 1 byte for this sentinel. */ cb->alloc = minsize + 1; #ifndef NDEBUG /* Reserve space for the magic cookies used to protect the * cbuf data[] array from underflow and overflow. */ cb->alloc += 2 * CBUF_MAGIC_LEN; #endif /* !NDEBUG */ if (!(cb->data = malloc(cb->alloc))) { free(cb); errno = ENOMEM; return(lsd_nomem_error(__FILE__, __LINE__, "cbuf data")); } cbuf_mutex_init(cb); cb->minsize = minsize; cb->maxsize = (maxsize > minsize) ? maxsize : minsize; cb->size = minsize; cb->used = 0; cb->overwrite = CBUF_WRAP_MANY; cb->got_wrap = 0; cb->i_in = cb->i_out = cb->i_rep = 0; #ifndef NDEBUG /* C is for cookie, that's good enough for me, yeah! * The magic cookies are only defined during DEBUG code. * The first "magic" cookie is at the top of the structure. * Magic cookies are also placed at the top & bottom of the * cbuf data[] array to catch buffer underflow & overflow errors. */ cb->data += CBUF_MAGIC_LEN; /* jump forward past underflow magic */ cb->magic = CBUF_MAGIC; /* * Must use memcpy since overflow cookie may not be word-aligned. */ memcpy(cb->data - CBUF_MAGIC_LEN, (void *) &cb->magic, CBUF_MAGIC_LEN); memcpy(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); cbuf_mutex_unlock(cb); #endif /* !NDEBUG */ return(cb); } void cbuf_destroy (cbuf_t cb) { assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); #ifndef NDEBUG /* The moon sometimes looks like a C, but you can't eat that. * Munch the magic cookies before freeing memory. */ cb->magic = ~CBUF_MAGIC; /* the anti-cookie! */ memcpy(cb->data - CBUF_MAGIC_LEN, (void *) &cb->magic, CBUF_MAGIC_LEN); memcpy(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN); cb->data -= CBUF_MAGIC_LEN; /* jump back to what malloc returned */ #endif /* !NDEBUG */ free(cb->data); cbuf_mutex_unlock(cb); cbuf_mutex_destroy(cb); free(cb); return; } void cbuf_flush (cbuf_t cb) { assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); /* * FIXME: Shrink buffer back to minimum size. */ cb->used = 0; cb->got_wrap = 0; cb->i_in = cb->i_out = cb->i_rep = 0; assert(cbuf_is_valid(cb)); cbuf_mutex_unlock(cb); return; } int cbuf_size (cbuf_t cb) { int size; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); size = cb->size; cbuf_mutex_unlock(cb); return(size); } int cbuf_free (cbuf_t cb) { int nfree; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); nfree = cb->size - cb->used; cbuf_mutex_unlock(cb); return(nfree); } int cbuf_used (cbuf_t cb) { int used; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); used = cb->used; cbuf_mutex_unlock(cb); return(used); } int cbuf_lines_used (cbuf_t cb) { int lines = -1; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); cbuf_find_unread_line(cb, cb->size, &lines); cbuf_mutex_unlock(cb); return(lines); } int cbuf_reused (cbuf_t cb) { int reused; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); reused = (cb->i_out - cb->i_rep + (cb->size + 1)) % (cb->size + 1); cbuf_mutex_unlock(cb); return(reused); } int cbuf_lines_reused (cbuf_t cb) { int lines = -1; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); cbuf_find_replay_line(cb, cb->size, &lines, NULL); cbuf_mutex_unlock(cb); return(lines); } int cbuf_is_empty (cbuf_t cb) { int used; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); used = cb->used; cbuf_mutex_unlock(cb); return(used == 0); } int cbuf_opt_get (cbuf_t cb, cbuf_opt_t name, int *value) { int rc = 0; assert(cb != NULL); if (value == NULL) { errno = EINVAL; return(-1); } cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); if (name == CBUF_OPT_OVERWRITE) { *value = cb->overwrite; } else { errno = EINVAL; rc = -1; } cbuf_mutex_unlock(cb); return(rc); } int cbuf_opt_set (cbuf_t cb, cbuf_opt_t name, int value) { int rc = 0; assert(cb != NULL); cbuf_mutex_lock(cb); assert(cbuf_is_valid(cb)); if (name == CBUF_OPT_OVERWRITE) { if ( (value == CBUF_NO_DROP) || (value == CBUF_WRAP_ONCE) || (value == CBUF_WRAP_MANY) ) { cb->overwrite = value; } else { errno = EINVAL; rc = -1; } } else { errno = EINVAL; rc = -1; } assert(cbuf_is_valid(cb)); cbuf_mutex_unlock(cb); return(rc); } int cbuf_drop (cbuf_t src, int len) { assert(src != NULL); if (len < -1) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); if (len == -1) { len = src->used; } else { len = MIN(len, src->used); } if (len > 0) { cbuf_dropper(src, len); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(len); } int cbuf_peek (cbuf_t src, void *dstbuf, int len) { int n; assert(src != NULL); if ((dstbuf == NULL) || (len < 0)) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_mem, &dstbuf); assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_read (cbuf_t src, void *dstbuf, int len) { int n; assert(src != NULL); if ((dstbuf == NULL) || (len < 0)) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_mem, &dstbuf); if (n > 0) { cbuf_dropper(src, n); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_replay (cbuf_t src, void *dstbuf, int len) { int n; assert(src != NULL); if ((dstbuf == NULL) || (len < 0)) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_replayer(src, len, (cbuf_iof) cbuf_put_mem, &dstbuf); assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_rewind (cbuf_t src, int len) { int reused; assert(src != NULL); if (len < -1) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); reused = (src->i_out - src->i_rep + (src->size + 1)) % (src->size + 1); if (len == -1) { len = reused; } else { len = MIN(len, reused); } if (len > 0) { src->used += len; src->i_out = (src->i_out - len + (src->size + 1)) % (src->size + 1); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(len); } int cbuf_write (cbuf_t dst, void *srcbuf, int len, int *ndropped) { int n; assert(dst != NULL); if (ndropped) { *ndropped = 0; } if ((srcbuf == NULL) || (len < 0)) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } cbuf_mutex_lock(dst); assert(cbuf_is_valid(dst)); n = cbuf_writer(dst, len, (cbuf_iof) cbuf_get_mem, &srcbuf, ndropped); assert(cbuf_is_valid(dst)); cbuf_mutex_unlock(dst); return(n); } int cbuf_drop_line (cbuf_t src, int len, int lines) { int n; assert(src != NULL); if ((len < 0) || (lines < -1)) { errno = EINVAL; return(-1); } if (lines == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_find_unread_line(src, len, &lines); if (n > 0) { cbuf_dropper(src, n); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_peek_line (cbuf_t src, char *dstbuf, int len, int lines) { int n, m, l; char *pdst; _unused (l); assert(src != NULL); if ((dstbuf == NULL) || (len < 0) || (lines < -1)) { errno = EINVAL; return(-1); } if (lines == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_find_unread_line(src, len - 1, &lines); if (n > 0) { if (len > 0) { m = MIN(n, len - 1); if (m > 0) { pdst = dstbuf; l = cbuf_reader(src, m, (cbuf_iof) cbuf_put_mem, &pdst); assert(l == m); } assert(m < len); dstbuf[m] = '\0'; } } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_read_line (cbuf_t src, char *dstbuf, int len, int lines) { int n, m, l; char *pdst; assert(src != NULL); _unused (l); if ((dstbuf == NULL) || (len < 0) || (lines < -1)) { errno = EINVAL; return(-1); } if (lines == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_find_unread_line(src, len - 1, &lines); if (n > 0) { if (len > 0) { m = MIN(n, len - 1); if (m > 0) { pdst = dstbuf; l = cbuf_reader(src, m, (cbuf_iof) cbuf_put_mem, &pdst); assert(l == m); } assert(m < len); dstbuf[m] = '\0'; } cbuf_dropper(src, n); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_replay_line (cbuf_t src, char *dstbuf, int len, int lines) { int n, m, l; int nl; char *pdst; assert(src != NULL); _unused (l); if ((dstbuf == NULL) || (len < 0) || (lines < -1)) { errno = EINVAL; return(-1); } if (lines == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_find_replay_line(src, len - 1, &lines, &nl); if (n > 0) { if (len > 0) { assert((nl == 0) || (nl == 1)); m = MIN(n, len - 1 - nl); m = MAX(m, 0); if (m > 0) { pdst = dstbuf; l = cbuf_replayer(src, m, (cbuf_iof) cbuf_put_mem, &pdst); assert(l == m); } /* Append newline if needed and space allows. */ if ((nl) && (len > 1)) { dstbuf[m++] = '\n'; } assert(m < len); dstbuf[m] = '\0'; n += nl; } } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_rewind_line (cbuf_t src, int len, int lines) { int n; assert(src != NULL); if ((len < 0) || (lines < -1)) { errno = EINVAL; return(-1); } if (lines == 0) { return(0); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); n = cbuf_find_replay_line(src, len, &lines, NULL); if (n > 0) { src->used += n; src->i_out = (src->i_out - n + (src->size + 1)) % (src->size + 1); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_write_line (cbuf_t dst, char *srcbuf, int *ndropped) { int len; int nfree, ncopy, n; int ndrop = 0; int d = 0; char *psrc = srcbuf; char *newline = "\n"; assert(dst != NULL); _unused (n); if (ndropped) { *ndropped = 0; } if (srcbuf == NULL) { errno = EINVAL; return(-1); } /* Compute number of bytes to effectively copy to dst cbuf. * Reserve space for the trailing newline if needed. */ len = ncopy = strlen(srcbuf); if ((len == 0) || (srcbuf[len - 1] != '\n')) { len++; } cbuf_mutex_lock(dst); assert(cbuf_is_valid(dst)); /* * Attempt to grow dst cbuf if necessary. */ nfree = dst->size - dst->used; if ((len > nfree) && (dst->size < dst->maxsize)) { nfree += cbuf_grow(dst, len - nfree); } /* Determine if src will fit (or be made to fit) in dst cbuf. */ if (dst->overwrite == CBUF_NO_DROP) { if (len > dst->size - dst->used) { errno = ENOSPC; len = -1; /* cannot return while mutex locked */ } } else if (dst->overwrite == CBUF_WRAP_ONCE) { if (len > dst->size) { errno = ENOSPC; len = -1; /* cannot return while mutex locked */ } } if (len > 0) { /* * Discard data that won't fit in dst cbuf. */ if (len > dst->size) { ndrop += len - dst->size; ncopy -= ndrop; psrc += ndrop; } /* Copy data from src string to dst cbuf. */ if (ncopy > 0) { n = cbuf_writer(dst, ncopy, (cbuf_iof) cbuf_get_mem, &psrc, &d); assert(n == ncopy); ndrop += d; } /* Append newline if needed. */ if (srcbuf[len - 1] != '\n') { n = cbuf_writer(dst, 1, (cbuf_iof) cbuf_get_mem, &newline, &d); assert(n == 1); ndrop += d; } } assert(cbuf_is_valid(dst)); cbuf_mutex_unlock(dst); if (ndropped) { *ndropped = ndrop; } return(len); } int cbuf_peek_to_fd (cbuf_t src, int dstfd, int len) { int n = 0; assert(src != NULL); if ((dstfd < 0) || (len < -1)) { errno = EINVAL; return(-1); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); if (len == -1) { len = src->used; } if (len > 0) { n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_fd, &dstfd); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_read_to_fd (cbuf_t src, int dstfd, int len) { int n = 0; assert(src != NULL); if ((dstfd < 0) || (len < -1)) { errno = EINVAL; return(-1); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); if (len == -1) { len = src->used; } if (len > 0) { n = cbuf_reader(src, len, (cbuf_iof) cbuf_put_fd, &dstfd); if (n > 0) { cbuf_dropper(src, n); } } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_replay_to_fd (cbuf_t src, int dstfd, int len) { int n = 0; assert(src != NULL); if ((dstfd < 0) || (len < -1)) { errno = EINVAL; return(-1); } cbuf_mutex_lock(src); assert(cbuf_is_valid(src)); if (len == -1) { len = src->size - src->used; } if (len > 0) { n = cbuf_replayer(src, len, (cbuf_iof) cbuf_put_fd, &dstfd); } assert(cbuf_is_valid(src)); cbuf_mutex_unlock(src); return(n); } int cbuf_write_from_fd (cbuf_t dst, int srcfd, int len, int *ndropped) { int n = 0; assert(dst != NULL); if (ndropped) { *ndropped = 0; } if ((srcfd < 0) || (len < -1)) { errno = EINVAL; return(-1); } cbuf_mutex_lock(dst); assert(cbuf_is_valid(dst)); if (len == -1) { /* * Try to use all of the free buffer space available for writing. * If it is all in use, try to grab another chunk and limit the * amount of data being overwritten. */ len = dst->size - dst->used; if (len == 0) { len = MIN(dst->size, CBUF_CHUNK); } } if (len > 0) { n = cbuf_writer(dst, len, (cbuf_iof) cbuf_get_fd, &srcfd, ndropped); } assert(cbuf_is_valid(dst)); cbuf_mutex_unlock(dst); return(n); } int cbuf_copy (cbuf_t src, cbuf_t dst, int len, int *ndropped) { int n = 0; assert(src != NULL); assert(dst != NULL); if (ndropped) { *ndropped = 0; } if (src == dst) { errno = EINVAL; return(-1); } if (len < -1) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } /* Lock cbufs in order of lowest memory address to prevent deadlock. */ if (src < dst) { cbuf_mutex_lock(src); cbuf_mutex_lock(dst); } else { cbuf_mutex_lock(dst); cbuf_mutex_lock(src); } assert(cbuf_is_valid(src)); assert(cbuf_is_valid(dst)); if (len == -1) { len = src->used; } if (len > 0) { n = cbuf_copier(src, dst, len, ndropped); } assert(cbuf_is_valid(src)); assert(cbuf_is_valid(dst)); cbuf_mutex_unlock(src); cbuf_mutex_unlock(dst); return(n); } int cbuf_move (cbuf_t src, cbuf_t dst, int len, int *ndropped) { int n = 0; assert(src != NULL); assert(dst != NULL); if (ndropped) { *ndropped = 0; } if (src == dst) { errno = EINVAL; return(-1); } if (len < -1) { errno = EINVAL; return(-1); } if (len == 0) { return(0); } /* Lock cbufs in order of lowest memory address to prevent deadlock. */ if (src < dst) { cbuf_mutex_lock(src); cbuf_mutex_lock(dst); } else { cbuf_mutex_lock(dst); cbuf_mutex_lock(src); } assert(cbuf_is_valid(src)); assert(cbuf_is_valid(dst)); if (len == -1) { len = src->used; } if (len > 0) { n = cbuf_copier(src, dst, len, ndropped); if (n > 0) { cbuf_dropper(src, n); } } assert(cbuf_is_valid(src)); assert(cbuf_is_valid(dst)); cbuf_mutex_unlock(src); cbuf_mutex_unlock(dst); return(n); } static int cbuf_find_replay_line (cbuf_t cb, int chars, int *nlines, int *nl) { /* Finds the specified number of lines from the replay region of the buffer. * If ([nlines] > 0), returns the number of bytes comprising the line count, * or 0 if this number of lines is not available (ie, all or none). * If ([nlines] == -1), returns the number of bytes comprising the maximum * line count bounded by the number of characters specified by [chars]. * Only complete lines (ie, those terminated by a newline) are counted, * with once exception: the most recent line of replay data is treated * as a complete line regardless of the presence of a terminating newline. * Sets the value-result parameter [nlines] to the number of lines found. * Sets [nl] to '1' if a newline is required to terminate the replay data. */ int i, n, m, l; int lines; assert(cb != NULL); assert(nlines != NULL); assert(*nlines >= -1); assert(cbuf_mutex_is_locked(cb)); n = m = l = 0; lines = *nlines; *nlines = 0; if (nl) { *nl = 0; /* init in case of early return */ } if ((lines == 0) || ((lines <= -1) && (chars <= 0))) { return(0); } if (cb->i_out == cb->i_rep) { return(0); /* no replay data available */ } if (lines > 0) { chars = -1; /* chars parm not used if lines > 0 */ } else { ++chars; /* incr to allow for preceding '\n' */ } /* Since the most recent line of replay data is considered implicitly * terminated, decrement the char count to account for the newline * if one is not present, or increment the line count if one is. * Note: cb->data[(O - 1 + (S+1)) % (S+1)] is the last replayable char. */ if (cb->data[(cb->i_out + cb->size) % (cb->size + 1)] != '\n') { if (nl) { *nl = 1; } --chars; } else { if (lines > 0) { ++lines; } --l; } i = cb->i_out; while (i != cb->i_rep) { i = (i + cb->size) % (cb->size + 1); /* (i - 1 + (S+1)) % (S+1) */ ++n; if (chars > 0) { --chars; } /* Complete lines are identified by a preceding newline. */ if (cb->data[i] == '\n') { if (lines > 0) { --lines; } m = n - 1; /* do not include preceding '\n' */ ++l; } if ((chars == 0) || (lines == 0)) { break; } } /* But the first line written in does not need a preceding newline. */ if ((!cb->got_wrap) && ((chars > 0) || (lines > 0))) { if (lines > 0) { --lines; } m = n; ++l; } if (lines > 0) { return(0); /* all or none, and not enough found */ } *nlines = l; return(m); } static int cbuf_find_unread_line (cbuf_t cb, int chars, int *nlines) { /* Finds the specified number of lines from the unread region of the buffer. * If ([nlines] > 0), returns the number of bytes comprising the line count, * or 0 if this number of lines is not available (ie, all or none). * If ([nlines] == -1), returns the number of bytes comprising the maximum * line count bounded by the number of characters specified by [chars]. * Only complete lines (ie, those terminated by a newline) are counted. * Sets the value-result parameter [nlines] to the number of lines found. */ int i, n, m, l; int lines; assert(cb != NULL); assert(nlines != NULL); assert(*nlines >= -1); assert(cbuf_mutex_is_locked(cb)); n = m = l = 0; lines = *nlines; *nlines = 0; if ((lines == 0) || ((lines <= -1) && (chars <= 0))) { return(0); } if (cb->used == 0) { return(0); /* no unread data available */ } if (lines > 0) { chars = -1; /* chars parm not used if lines > 0 */ } i = cb->i_out; while (i != cb->i_in) { ++n; if (chars > 0) { --chars; } if (cb->data[i] == '\n') { if (lines > 0) { --lines; } m = n; ++l; } if ((chars == 0) || (lines == 0)) { break; } i = (i + 1) % (cb->size + 1); } if (lines > 0) { return(0); /* all or none, and not enough found */ } *nlines = l; return(m); } static int cbuf_get_fd (void *dstbuf, int *psrcfd, int len) { /* Copies data from the file referenced by the file descriptor * pointed at by [psrcfd] into cbuf's [dstbuf]. * Returns the number of bytes read from the fd, 0 on EOF, or -1 on error. */ int n; assert(dstbuf != NULL); assert(psrcfd != NULL); assert(*psrcfd >= 0); assert(len > 0); do { n = read(*psrcfd, dstbuf, len); } while ((n < 0) && (errno == EINTR)); return(n); } static int cbuf_get_mem (void *dstbuf, unsigned char **psrcbuf, int len) { /* Copies data from the buffer pointed at by [psrcbuf] into cbuf's [dstbuf]. * Returns the number of bytes copied. */ assert(dstbuf != NULL); assert(psrcbuf != NULL); assert(*psrcbuf != NULL); assert(len > 0); memcpy(dstbuf, *psrcbuf, len); *psrcbuf += len; return(len); } static int cbuf_put_fd (void *srcbuf, int *pdstfd, int len) { /* Copies data from cbuf's [srcbuf] into the file referenced * by the file descriptor pointed at by [pdstfd]. * Returns the number of bytes written to the fd, or -1 on error. */ int n; assert(srcbuf != NULL); assert(pdstfd != NULL); assert(*pdstfd >= 0); assert(len > 0); do { n = write(*pdstfd, srcbuf, len); } while ((n < 0) && (errno == EINTR)); return(n); } static int cbuf_put_mem (void *srcbuf, unsigned char **pdstbuf, int len) { /* Copies data from cbuf's [srcbuf] into the buffer pointed at by [pdstbuf]. * Returns the number of bytes copied. */ assert(srcbuf != NULL); assert(pdstbuf != NULL); assert(*pdstbuf != NULL); assert(len > 0); memcpy(*pdstbuf, srcbuf, len); *pdstbuf += len; return(len); } static int cbuf_copier (cbuf_t src, cbuf_t dst, int len, int *ndropped) { /* Copies up to [len] bytes from the [src] cbuf into the [dst] cbuf. * Returns the number of bytes copied, or -1 on error (with errno set). * Sets [ndropped] (if not NULL) to the number of [dst] bytes overwritten. */ int ncopy, nfree, nleft, nrepl, n; int i_src, i_dst; assert(src != NULL); assert(dst != NULL); assert(len > 0); assert(cbuf_mutex_is_locked(src)); assert(cbuf_mutex_is_locked(dst)); /* Bound len by the number of bytes available. */ len = MIN(len, src->used); if (len == 0) { return(0); } /* Attempt to grow dst cbuf if necessary. */ nfree = dst->size - dst->used; if ((len > nfree) && (dst->size < dst->maxsize)) { nfree += cbuf_grow(dst, len - nfree); } /* Compute number of bytes to effectively copy to dst cbuf. */ if (dst->overwrite == CBUF_NO_DROP) { len = MIN(len, dst->size - dst->used); if (len == 0) { errno = ENOSPC; return(-1); } } else if (dst->overwrite == CBUF_WRAP_ONCE) { len = MIN(len, dst->size); } /* Compute number of bytes that will be overwritten in dst cbuf. */ if (ndropped) { *ndropped = MAX(0, len - dst->size + dst->used); } /* Compute number of bytes to physically copy to dst cbuf. This prevents * copying data that will overwritten if the cbuf wraps multiple times. */ ncopy = len; i_src = src->i_out; i_dst = dst->i_in; if (ncopy > dst->size) { n = ncopy - dst->size; i_src = (i_src + n) % (src->size + 1); ncopy -= n; } /* Copy data from src cbuf to dst cbuf. */ nleft = ncopy; while (nleft > 0) { n = MIN(((src->size + 1) - i_src), ((dst->size + 1) - i_dst)); n = MIN(n, nleft); memcpy(&dst->data[i_dst], &src->data[i_src], n); i_src = (i_src + n) % (src->size + 1); i_dst = (i_dst + n) % (dst->size + 1); nleft -= n; } /* Update dst cbuf metadata. */ if (ncopy > 0) { nrepl = (dst->i_out - dst->i_rep + (dst->size + 1)) % (dst->size + 1); dst->used = MIN(dst->used + ncopy, dst->size); assert(i_dst == (dst->i_in + ncopy) % (dst->size + 1)); dst->i_in = i_dst; if (ncopy > nfree - nrepl) { dst->got_wrap = 1; dst->i_rep = (dst->i_in + 1) % (dst->size + 1); } if (ncopy > nfree) { dst->i_out = dst->i_rep; } } return(len); } static int cbuf_dropper (cbuf_t cb, int len) { /* Discards exactly [len] bytes of unread data from [cb]. * Returns the number of bytes dropped. */ assert(cb != NULL); assert(len > 0); assert(len <= cb->used); assert(cbuf_mutex_is_locked(cb)); cb->used -= len; cb->i_out = (cb->i_out + len) % (cb->size + 1); /* Attempt to shrink cbuf if possible. */ if ((cb->size - cb->used > CBUF_CHUNK) && (cb->size > cb->minsize)) { cbuf_shrink(cb); } /* Don't call me clumsy, don't call me a fool. * When things fall down on me, I'm following the rule. */ return(len); } static int cbuf_reader (cbuf_t src, int len, cbuf_iof putf, void *dst) { /* Reads up to [len] bytes from [src] into the object pointed at by [dst]. * The I/O function [putf] specifies how data is written into [dst]. * Returns the number of bytes read, or -1 on error (with errno set). * Note that [dst] is a value-result parameter and will be "moved forward" * by the number of bytes written into it. */ int nleft, n, m; int i_src; assert(src != NULL); assert(len > 0); assert(putf != NULL); assert(dst != NULL); assert(cbuf_mutex_is_locked(src)); /* Bound len by the number of bytes available. */ len = MIN(len, src->used); if (len == 0) { return(0); } /* Copy data from src cbuf to dst obj. Do the cbuf hokey-pokey and * wrap-around the buffer at most once. Break out if putf() returns * either an ERR or a short count. */ i_src = src->i_out; nleft = len; m = 0; while (nleft > 0) { n = MIN(nleft, (src->size + 1) - i_src); m = putf(&src->data[i_src], dst, n); if (m > 0) { nleft -= m; i_src = (i_src + m) % (src->size + 1); } if (n != m) { break; /* got ERR or "short" putf() */ } } /* Compute number of bytes written to dst obj. */ n = len - nleft; assert((n >= 0) && (n <= len)); /* * If no data has been written, return the ERR reported by putf(). */ if (n == 0) { return(m); } return(n); } static int cbuf_replayer (cbuf_t src, int len, cbuf_iof putf, void *dst) { /* Replays up to [len] bytes from [src] into the object pointed at by [dst]. * The I/O function [putf] specifies how data is written into [dst]. * Returns the number of bytes replayed, or -1 on error (with errno set). * Note that [dst] is a value-result parameter and will be "moved forward" * by the number of bytes written into it. */ int nleft, n, m; int i_src; assert(src != NULL); assert(len > 0); assert(putf != NULL); assert(dst != NULL); assert(cbuf_mutex_is_locked(src)); /* Bound len by the number of bytes available. */ n = (src->i_out - src->i_rep + (src->size + 1)) % (src->size + 1); len = MIN(len, n); if (len == 0) { return(0); } /* Copy data from src cbuf to dst obj. Do the cbuf hokey-pokey and * wrap-around the buffer at most once. Break out if putf() returns * either an ERR or a short count. */ i_src = (src->i_out - len + (src->size + 1)) % (src->size + 1); nleft = len; m = 0; while (nleft > 0) { n = MIN(nleft, (src->size + 1) - i_src); m = putf(&src->data[i_src], dst, n); if (m > 0) { nleft -= m; i_src = (i_src + m) % (src->size + 1); } if (n != m) { break; /* got ERR or "short" putf() */ } } /* Compute number of bytes written to dst obj. */ n = len - nleft; assert((n >= 0) && (n <= len)); /* * If no data has been written, return the ERR reported by putf(). */ if (n == 0) { return(m); } return(n); } static int cbuf_writer (cbuf_t dst, int len, cbuf_iof getf, void *src, int *ndropped) { /* Writes up to [len] bytes from the object pointed at by [src] into [dst]. * The I/O function [getf] specifies how data is read from [src]. * Returns the number of bytes written, or -1 on error (with errno set). * Sets [ndropped] (if not NULL) to the number of [dst] bytes overwritten. * Note that [src] is a value-result parameter and will be "moved forward" * by the number of bytes read from it. */ int nfree, nleft, nrepl, n, m; int i_dst; assert(dst != NULL); assert(len > 0); assert(getf != NULL); assert(src != NULL); assert(cbuf_mutex_is_locked(dst)); /* Attempt to grow dst cbuf if necessary. */ nfree = dst->size - dst->used; if ((len > nfree) && (dst->size < dst->maxsize)) { nfree += cbuf_grow(dst, len - nfree); } /* Compute number of bytes to write to dst cbuf. */ if (dst->overwrite == CBUF_NO_DROP) { len = MIN(len, dst->size - dst->used); if (len == 0) { errno = ENOSPC; return(-1); } } else if (dst->overwrite == CBUF_WRAP_ONCE) { len = MIN(len, dst->size); } /* Copy data from src obj to dst cbuf. Do the cbuf hokey-pokey and * wrap-around the buffer as needed. Break out if getf() returns * either an EOF/ERR or a short count. */ i_dst = dst->i_in; nleft = len; m = 0; while (nleft > 0) { n = MIN(nleft, (dst->size + 1) - i_dst); m = getf(&dst->data[i_dst], src, n); if (m > 0) { nleft -= m; i_dst = (i_dst + m) % (dst->size + 1); } if (n != m) { break; /* got EOF/ERR or "short" getf() */ } } /* Compute number of bytes written to dst cbuf. */ n = len - nleft; assert((n >= 0) && (n <= len)); /* * If no data has been written, return the EOF/ERR reported by getf(). */ if (n == 0) { return(m); } /* Update dst cbuf metadata. */ if (n > 0) { nrepl = (dst->i_out - dst->i_rep + (dst->size + 1)) % (dst->size + 1); dst->used = MIN(dst->used + n, dst->size); assert(i_dst == (dst->i_in + n) % (dst->size + 1)); dst->i_in = i_dst; if (n > nfree - nrepl) { dst->got_wrap = 1; dst->i_rep = (dst->i_in + 1) % (dst->size + 1); } if (n > nfree) { dst->i_out = dst->i_rep; } } if (ndropped) { *ndropped = MAX(0, n - nfree); } return(n); } static int cbuf_grow (cbuf_t cb, int n) { /* Attempts to grow the circular buffer [cb] by at least [n] bytes. * Returns the number of bytes by which the buffer has grown (which may be * less-than, equal-to, or greater-than the number of bytes requested). */ unsigned char *data; int size_old, size_meta; int m; assert(cb != NULL); assert(n > 0); assert(cbuf_mutex_is_locked(cb)); if (cb->size == cb->maxsize) { return(0); } size_old = cb->size; size_meta = cb->alloc - cb->size; /* size of sentinel & magic cookies */ assert(size_meta > 0); /* Attempt to grow data buffer by multiples of the chunk-size. */ m = cb->alloc + n; m = m + (CBUF_CHUNK - (m % CBUF_CHUNK)); m = MIN(m, (cb->maxsize + size_meta)); assert(m > cb->alloc); data = cb->data; #ifndef NDEBUG data -= CBUF_MAGIC_LEN; /* jump back to what malloc returned */ #endif /* !NDEBUG */ if (!(data = realloc(data, m))) { /* * XXX: Set flag or somesuch to prevent regrowing when out of memory? */ return(0); /* unable to grow data buffer */ } cb->data = data; cb->alloc = m; cb->size = m - size_meta; #ifndef NDEBUG /* A round cookie with one bite out of it looks like a C. * The underflow cookie will have been copied by realloc() if needed. * But the overflow cookie must be rebaked. * Must use memcpy since overflow cookie may not be word-aligned. */ cb->data += CBUF_MAGIC_LEN; /* jump forward past underflow magic */ memcpy(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN); #endif /* !NDEBUG */ /* The memory containing replay and unread data must be contiguous modulo * the buffer size. Additional memory must be inserted between where * new data is written in (i_in) and where replay data starts (i_rep). * If replay data wraps-around the old buffer, move it to the new end * of the buffer so it wraps-around in the same manner. */ if (cb->i_rep > cb->i_in) { n = (size_old + 1) - cb->i_rep; m = (cb->size + 1) - n; memmove(cb->data + m, cb->data + cb->i_rep, n); if (cb->i_out >= cb->i_rep) { cb->i_out += m - cb->i_rep; } cb->i_rep = m; } assert(cbuf_is_valid(cb)); return(cb->size - size_old); } static int cbuf_shrink (cbuf_t cb) { /* XXX: DOCUMENT ME. */ assert(cb != NULL); assert(cbuf_mutex_is_locked(cb)); assert(cbuf_is_valid(cb)); if (cb->size == cb->minsize) { return(0); } if (cb->size - cb->used <= CBUF_CHUNK) { return(0); } /* FIXME: NOT IMPLEMENTED. */ assert(cbuf_is_valid(cb)); return(0); } #ifndef NDEBUG #ifdef WITH_PTHREADS static int cbuf_mutex_is_locked (cbuf_t cb) { /* Returns true if the mutex is locked; o/w, returns false. */ int rc; assert(cb != NULL); rc = pthread_mutex_trylock(&cb->mutex); return(rc == EBUSY ? 1 : 0); } #endif /* WITH_PTHREADS */ #endif /* !NDEBUG */ #ifndef NDEBUG static int cbuf_is_valid (cbuf_t cb) { /* Validates the data structure. All invariants should be tested here. * Returns true if everything is valid; o/w, aborts due to assertion failure. */ int nfree; assert(cb != NULL); assert(cbuf_mutex_is_locked(cb)); assert(cb->data != NULL); assert(cb->magic == CBUF_MAGIC); /* * Must use memcmp since overflow cookie may not be word-aligned. */ assert(memcmp(cb->data - CBUF_MAGIC_LEN, (void *) &cb->magic, CBUF_MAGIC_LEN) == 0); assert(memcmp(cb->data + cb->size + 1, (void *) &cb->magic, CBUF_MAGIC_LEN) == 0); assert(cb->alloc > 0); assert(cb->alloc > cb->size); assert(cb->size > 0); assert(cb->size >= cb->minsize); assert(cb->size <= cb->maxsize); assert(cb->minsize > 0); assert(cb->maxsize > 0); assert(cb->used >= 0); assert(cb->used <= cb->size); assert(cb->overwrite == CBUF_NO_DROP || cb->overwrite == CBUF_WRAP_ONCE || cb->overwrite == CBUF_WRAP_MANY); assert(cb->got_wrap || !cb->i_rep); /* i_rep = 0 if data has not wrapped */ assert(cb->i_in >= 0); assert(cb->i_in <= cb->size); assert(cb->i_out >= 0); assert(cb->i_out <= cb->size); assert(cb->i_rep >= 0); assert(cb->i_rep <= cb->size); if (cb->i_in >= cb->i_out) { assert((cb->i_rep > cb->i_in) || (cb->i_rep <= cb->i_out)); } else /* if (cb->in < cb->i_out) */ { assert((cb->i_rep > cb->i_in) && (cb->i_rep <= cb->i_out)); } nfree = (cb->i_out - cb->i_in - 1 + (cb->size + 1)) % (cb->size + 1); assert(cb->size - cb->used == nfree); return(1); } #endif /* !NDEBUG */ pdsh-2.36/src/pdsh/rcmd.h0000664€^–Á €^–Á 0000000635215131211226023337 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Mark Grondona . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _HAVE_RCMD_H #define _HAVE_RCMD_H #include "opt.h" struct rcmd_options { bool resolve_hosts; }; #define RCMD_OPT_RESOLVE_HOSTS 0x1 struct rcmd_info { int fd; int efd; struct rcmd_module *rmod; struct rcmd_options *opts; char *ruser; void *arg; }; /* * Register default rcmd parameters for hosts in hostlist string "hosts." * rcmd_type - if non-NULL set default rcmd connect module for "hosts." * user - if non-NULL set default remote username for "hosts." * * The first call to this function "wins," i.e. later calls to register * will not override existing defaults. This is done because currently * in pdsh, command line options are processed *before* configuration * type files (i.e. genders) since these files are processed by pdsh * modules. */ int rcmd_register_defaults (char *hosts, char *rcmd_type, char *user); /* * Register default rcmd type */ int rcmd_register_default_rcmd (char *rcmd_name); /* * Return default rcmd module name. */ char * rcmd_get_default_module (void); /* * Create and rcmd_info object for specified host */ struct rcmd_info * rcmd_create (char *host); /* * Connect using rcmd_info rcmd */ int rcmd_connect (struct rcmd_info *rcmd, char *host, char *addr, char *locuser, char *remuser, char *cmd, int nodeid, bool err); /* * Destroy rcmd connections */ int rcmd_destroy (struct rcmd_info *); /* * Send a signal over the specified remote connection */ int rcmd_signal (struct rcmd_info *, int signum); int rcmd_init (opt_t *opt); /* * Free all rcmd module information. */ int rcmd_exit (void); /* * Called by rcmd module during "init" function to set various * rcmd-specific options. (see rcmd_options structure above) * * Returns -1 with errno set to ESRCH if called from anywhere but * module's rcmd_init function. */ int rcmd_opt_set (int id, void * value); #endif /* !_HAVE_RCMD_H */ pdsh-2.36/src/pdsh/wcoll.h0000664€^–Á €^–Á 0000000346515131211226023534 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _WCOLL_INCLUDED #define _WCOLL_INCLUDED #if HAVE_CONFIG_H #include "config.h" #endif #include /* FILE * */ #include "src/common/macros.h" #include "src/common/hostlist.h" hostlist_t read_wcoll(char *, FILE *); /* * Read WCOLL file [file] searching colon-separated path [path]. * * Supports the use of '#include filename' to include other WCOLL files * that are also in [path]. */ hostlist_t read_wcoll_path (const char *path, const char *file); #endif /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/main.c0000664€^–Á €^–Á 0000002561515131211226023334 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #if HAVE_UNISTD_H #include /* setuid */ #endif #include /* wait */ #include /* strcmp */ #include /* exit */ #include #if HAVE_READLINE #include #include #include #include #include #endif #include "src/common/err.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "dsh.h" #include "opt.h" #include "mod.h" #include "pcp_client.h" #include "pcp_server.h" #include "privsep.h" extern const char *pdsh_module_dir; static void _interactive_dsh(opt_t *); static int _pcp_remote_client (opt_t *); static int _pcp_remote_server (opt_t *); int main(int argc, char *argv[]) { opt_t opt; int retval = 0; const char *m; char *prog = xbasename(argv[0]); #if HAVE_READLINE rl_readline_name = prog; #endif /* * Initialize. */ err_init(prog); /* init err package */ /* * If running setuid, fork a child to handle * all privileged operations and drop privs in this process. */ privsep_init(); /* * Seed options with default values: */ opt_default(&opt, argv[0]); /* * Override defaults with environment */ opt_env(&opt); /* * Process any options that need to be handled early: */ opt_args_early(&opt, argc, argv); /* * Load static or dynamic pdsh modules */ mod_init(); /* * Allow module directory to be overridden, but not when * running as root or setuid. (This is mainly for testing...) */ if (!(m = getenv ("PDSH_MODULE_DIR")) || getuid() == 0 || getuid() != geteuid()) m = pdsh_module_dir; if (mod_load_modules(m, &opt) < 0) errx("%p: Couldn't load any pdsh modules\n"); /* * Handle options. */ opt_args(&opt, argc, argv); /* override with command line */ if (opt_verify(&opt)) { /* verify options, print errors */ /* * Do the work. */ if (opt.info_only) /* display info only */ opt_list(&opt); else if (pdsh_personality() == PCP && opt.pcp_server) retval = (_pcp_remote_server (&opt) < 0); else if (pdsh_personality() == PCP && opt.pcp_client) retval = (_pcp_remote_client (&opt) < 0); else if (pdsh_personality() == PCP || opt.cmd != NULL) retval = dsh(&opt); /* single dsh/pcp command */ else /* prompt loop */ _interactive_dsh(&opt); } else { retval = 1; } mod_exit(); /* * Clean up. */ privsep_fini(); opt_free(&opt); /* free heap storage in opt struct */ err_cleanup(); return retval; } #if HAVE_READLINE static int _history_file_create (char *path, size_t len) { char * home; struct stat sbuf; int fd; int rc; int n; memset (path, 0, len); if (!(home = getenv ("HOME"))) { err ("%p: Unable to read HOME env var\n"); return (-1); } n = snprintf (path, len, "%s/.pdsh", home); if ((n < 0) || (n >= len)) { err ("%p: Unable to open %s/.pdsh: path too long\n", home); return (-1); } /* Check for ~/.pdsh directory * and create if it does not exist */ if (lstat (path, &sbuf) < 0) { if (errno == ENOENT) { if (mkdir (path, 0700) < 0) { err ("%p: Unable to create ~/.pdsh: %m\n"); return (-1); } } else { err ("%p: Couldn't find ~/.pdsh: %m\n"); return (-1); } } else if (!(S_ISDIR (sbuf.st_mode))) { err ("%p: ~/.pdsh exists but is not a directory\n"); return (-1); } /* Now should have ~/.pdsh/, * create ~/.pdsh/history if it does not exist. */ n = snprintf (path, len, "%s/.pdsh/history", home); if ((n < 0) || (n >= len)) { err ("%p: Unable to open %s/.pdsh/history: path too long\n", home); return (-1); } if ((fd = open (path, O_CREAT, 00600)) < 0) { err ("%p: Error: Unable to create history file \"%s\": %m\n", path); return (-1); } close (fd); if ((rc = read_history (path))) { err ("%p: Warning: Unable to read history file \"%s\": %s\n", path, strerror (rc)); return (-1); } return (0); } static void _history_list (void) { HIST_ENTRY **list; int i; if (!(list = history_list ())) return; for (i = 0; list[i]; i++) { out ("%p: %d: %s\n", i + history_base, list[i]->line); } return; } /* Warning: May be running setuid root! */ static void _interactive_dsh(opt_t * opt) { pid_t pid; char prompt[64]; char history_filename[MAXPATHLEN]; char *cmd = NULL; int got_history_file = 1; int len; snprintf(prompt, sizeof(prompt), "%s> ", opt->progname); using_history (); len = sizeof (history_filename); if (_history_file_create (history_filename, len) < 0) { got_history_file = 0; } while ((cmd = readline(prompt)) != NULL) { int errnum; char *expansion; if ((errnum = history_expand (cmd, &expansion))) { err ("%p: %s\n", expansion); } free (cmd); if ((errnum < 0) || (errnum == 2)) { free (expansion); continue; } cmd = expansion; if (!strcmp(cmd, "history")) { _history_list (); continue; } add_history (cmd); if (strlen(cmd) == 0) { /* empty line */ free(cmd); continue; } if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit")) { free(cmd); /* quit or exit */ break; } if ((strlen(cmd) != 0) && (got_history_file)) append_history (1, history_filename); /* * fork dsh so we can ignore SIGINT in prompt loop */ switch (pid = fork()) { case -1: /* error */ errx("%p: fork: %m\n"); case 0: /* child - run cmd */ opt->cmd = Strdup(cmd); dsh(opt); Free((void **) &opt->cmd); exit(0); default: /* parent - wait */ while (waitpid(pid, NULL, 0) < 0) { if (errno != EINTR) break; } break; } free (cmd); } } #else static char *_getcmd(char *); static void _shell(uid_t, char *); /* * Prompt for dsh commands, then execute them, prompt again, ... * "quit", "exit", or ^D to get out. * opt (IN) program options struct */ static void _interactive_dsh(opt_t * opt) { pid_t pid; signal(SIGINT, SIG_IGN); while ((opt->cmd = _getcmd(opt->progname))) { if (*opt->cmd == '\0') { /* empty command */ Free((void **) &opt->cmd); continue; } if (*opt->cmd == '!') { /* shell escape */ _shell(opt->luid, opt->cmd + 1); Free((void **) &opt->cmd); continue; } if (strcmp(opt->cmd, "quit") == 0 /* user exit */ || strcmp(opt->cmd, "exit") == 0) { Free((void **) &opt->cmd); break; } /* must fork dsh so we can ignore SIGINT in prompt loop */ switch (pid = fork()) { case -1: errx("%p: fork: %m\n"); case 0: dsh(opt); /* run command */ exit(0); default: while (waitpid(pid, NULL, 0) < 0 && errno == EINTR); } Free((void **) &opt->cmd); } } /* * Run a command that was shell-escaped from the dsh> prompt. Run it as * the real uid of the invoking user, so we must fork to maintain root * effective uid in the parent. * uid (IN) uid used to execute command * cmd (IN) command and args */ static void _shell(uid_t uid, char *cmd) { int ret; pid_t pid; switch (pid = fork()) { case -1: errx("%p: fork: %m\n"); case 0: if (setuid(uid) < 0) errx ("%p: setuid: %m\n"); ret = system(cmd); if (ret) err ("%p: exited with status %d\n", ret); exit(0); default: waitpid(pid, NULL, 0); } } /* * Prompt for a command and return it. * prompt (IN) string used to build prompt (e.g. program name) */ static char *_getcmd(char *prompt) { char *cmd = NULL; char buf[LINEBUFSIZE]; out("%s> ", prompt); if (fgets(buf, LINEBUFSIZE, stdin) != NULL) { buf[LINEBUFSIZE - 1] = '\0'; xstrcln(buf, NULL); cmd = Strdup(buf); } return cmd; } #endif /* WITH_READLINE */ static int _pcp_remote_server (opt_t *opt) { struct pcp_server svr[1]; svr->infd = STDIN_FILENO; svr->outfd = STDOUT_FILENO; svr->preserve = opt->preserve; svr->target_is_dir = opt->target_is_directory; svr->outfile = opt->outfile_name; return (pcp_server (svr)); } static int _pcp_remote_client (opt_t *opt) { struct pcp_client pcp[1]; pcp->infd = STDIN_FILENO; pcp->outfd = STDOUT_FILENO; pcp->infiles = pcp_expand_dirs (opt->infile_names); pcp->host = opt->pcp_client_host; pcp->preserve = opt->preserve; pcp->pcp_client = opt->pcp_client; return (pcp_client (pcp)); } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/src/pdsh/pcp_client.h0000664€^–Á €^–Á 0000000424615131211226024532 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _PCP_CLIENT_H #define _PCP_CLIENT_H #if HAVE_CONFIG_H # include #endif #include "src/pdsh/opt.h" #include "src/common/list.h" /* define the filename flag as an impossible filename */ #define EXIT_SUBDIR_FILENAME "a!b@c#d$" #define EXIT_SUBDIR_FLAG "E\n" /* Store the file that should be copied and if it was a * file specified by the user or if it is a file found due to * recursively moving down a directory (-r option). This flag * is needed so the right output filename can be determined * on reverse copies. */ struct pcp_filename { char *filename; int file_specified_by_user; }; /* expand directories, if any, and verify access for all files */ List pcp_expand_dirs (List infile_names); struct pcp_client { int infd; int outfd; bool preserve; bool pcp_client; char *host; List infiles; }; int pcp_client (struct pcp_client *cli); #endif /* _PCP_CLIENT_H */ pdsh-2.36/src/pdsh/privsep.h0000664€^–Á €^–Á 0000000337415131211226024103 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #ifndef _PRIVSEP_H #define _PRIVSEP_H /* * Initialize privilege separation: fork off privileged child * if we're running setuid. */ int privsep_init (void); /* * Close privsep server pipe and reap child. */ int privsep_fini (void); /* * Request privilege server to bind to reserved port. */ int privsep_rresvport (int *lport); /* * Request privilege server to bind to reserved port (Including family). */ int privsep_rresvport_af (int *lport, int family); #endif /* !_PRIVSEP_H */ pdsh-2.36/src/pdsh/wcoll.c0000664€^–Á €^–Á 0000002174615131211226023531 0ustar arif.ali@canonical.comarif.ali@canonical.com/*****************************************************************************\ * $Id$ ***************************************************************************** * Copyright (C) 2001-2006 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Jim Garlick . * UCRL-CODE-2003-005. * * This file is part of Pdsh, a parallel remote shell program. * For details, see . * * Pdsh is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with Pdsh; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. \*****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #if HAVE_UNISTD_H #include /* for R_OK, access() */ #endif #include /* atoi */ #include #include #include #include "src/common/err.h" #include "src/common/list.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/hostlist.h" #include "src/common/split.h" #include "src/common/xstring.h" #include "dsh.h" #include "wcoll.h" struct wcoll_ctx { hostlist_t hl; List path_list; List include_cache; }; static void free_f (void *x) { Free (&x); } static int strcmp_f (char *a, char *b) { return (strcmp (a, b) == 0); } static struct wcoll_ctx * wcoll_ctx_create (const char *path) { char *copy = Strdup (path); struct wcoll_ctx *ctx = Malloc (sizeof (*ctx)); if (!copy || !ctx) errx ("%p: wcoll_ctx_create: Out of memory\n"); ctx->hl = hostlist_create (""); ctx->path_list = list_split (":", copy); ctx->include_cache = list_create ((ListDelF) free_f); Free ((void **) ©); return (ctx); } static void wcoll_ctx_destroy (struct wcoll_ctx *ctx) { list_destroy (ctx->path_list); list_destroy (ctx->include_cache); /* * Do not destroy hostlist, it is pulled out of ctx and returned * to caller of read_wcoll() */ ctx->hl = NULL; Free ((void **)&ctx); } static int wcoll_ctx_file_is_cached (struct wcoll_ctx *ctx, char *file) { if (list_find_first (ctx->include_cache, (ListFindF) strcmp_f, file)) return 1; list_push (ctx->include_cache, Strdup (file)); return 0; } /* * Find first file named [name] in colon-separated path [path] * Returns file path in supplied buffer [buf] of length [n]. * Returns -1 on failure. */ static int wcoll_ctx_path_lookup (struct wcoll_ctx *ctx, const char *name, char *buf, int len) { int rc = -1; ListIterator i; const char *s; if (name == NULL) return -1; i = list_iterator_create (ctx->path_list); while ((s = list_next (i))) { int n = snprintf (buf, len, "%s/%s", s, name); if (n < 0 || n >= len) { errno = ENOSPC; break; } if (access (buf, R_OK) >= 0) { rc = 0; break; } } list_iterator_destroy (i); return (rc); } static char * include_file (char *line) { const char *sep = "\n\r\t "; char *p = line; char *orig; char *included = NULL; /* * Check for "#include" starting in first column. If it exists, * then attempt to return the included filename or path. */ if (strncmp (p, "#include", 8) != 0) return (NULL); p += 8; /* * Skip whitespace */ while (isblank (*p)) p++; /* * Copy original line in case we need to print an error. */ orig = Strdup (line); /* * Get next token, which should be the included filename or path. * * If there are more tokens following the filename, then ignore * this line and print an error. */ if ((p = strtok (p, sep)) == NULL || strtok (NULL, "\n\r\t ")) err ("%p: warning: Ignoring invalid line: %s", orig); else included = p; Free ((void **) &orig); return (included); } /* * Return absolute path to [file] in buffer [buf] of max length [len]. * * If [file] argument is an absolute path or relative to '.' or '..' * then [file] is copied to [buf], otherwise, colon-separated [path] * is searched for a file named [file]. * * If no file is found, then NULL is returned, otherwise a pointer * to the passed [buf] is returned. */ static char * wcoll_ctx_resolve_path (struct wcoll_ctx *ctx, const char *file, char *buf, int len) { if (file[0] == '/') strncpy (buf, file, len - 1); else if ( file[0] == '.' && (file[1] == '/' || (file[1] == '.' && file[2] == '/'))) strncpy (buf, file, len - 1); else { if (wcoll_ctx_path_lookup (ctx, file, buf, len) < 0) return NULL; } return buf; } static int wcoll_ctx_read_file (struct wcoll_ctx *ctx, const char *f); static int wcoll_ctx_read_line (struct wcoll_ctx *ctx, char *line) { char *p; char *included; /* * Check for comment. Zap the comment and the rest of the line * unless this is an include statement '#include foo' */ if ((p = strchr(line, '#')) != NULL) { if (p == line && (included = include_file (p)) != NULL) wcoll_ctx_read_file (ctx, included); *p = '\0'; } xstrcln(line, NULL); if ((line[0] != '\0') && (hostlist_push(ctx->hl, line) == 0)) err("%p: warning: target '%s' not parsed\n", line); return 0; } static int wcoll_ctx_read_stream (struct wcoll_ctx *ctx, FILE *fp) { char buf [LINEBUFSIZE]; assert (ctx != NULL); assert (fp != NULL); while (fgets(buf, LINEBUFSIZE, fp) != NULL) wcoll_ctx_read_line (ctx, buf); return 0; } /* * Append the contents of file [f], optionally searching [path], to * the supplied hostlist [hl], returning the result. If [hl] is NULL, * then a new hostlist is allocated. * * This function supports * * #include file * * to include other working collective files. */ static int wcoll_ctx_read_file (struct wcoll_ctx *ctx, const char *f) { char fq_path [4096]; FILE *fp = NULL; assert (ctx != NULL); assert (f != NULL); if (wcoll_ctx_resolve_path (ctx, f, fq_path, sizeof (fq_path)) == NULL) errx("%p: %s: %m\n", f); /* * Detect recursive #include: */ if (wcoll_ctx_file_is_cached (ctx, fq_path)) { err("%p: warning: file '%s' included multiple times\n", f); return -1; } if (access(fq_path, R_OK) == -1 || !(fp = fopen(fq_path, "r"))) errx("%p: %s: %m\n", f); wcoll_ctx_read_stream (ctx, fp); fclose(fp); return 0; } hostlist_t read_wcoll_path (const char *path, const char *file) { struct wcoll_ctx *ctx; hostlist_t hl; ctx = wcoll_ctx_create (path); wcoll_ctx_read_file (ctx, file); hl = ctx->hl; wcoll_ctx_destroy (ctx); return hl; } /* * Get the dirname for the file path [file] and copy into the buffer * [dir] of length [len]. If [file] is NULL then return ".". */ static char * get_file_path (const char *file, char *dir, int len) { char *str; char *dname; memset (dir, 0, len); dir[0] = '.'; if (file == NULL) return (dir); str = Strdup (file); dname = dirname (str); if (dname && strlen (dname) < len - 1) strcpy (dir, dname); else err ("%p: %s: Error reading file path\n"); Free ((void **) &str); return (dir); } /* * Read wcoll from specified file or from the specified FILE pointer. * (one of the arguments must be NULL). * file (IN) name of wcoll file (or NULL) * f (IN) FILE pointer to wcoll file (or NULL) * RETURN new list containing hostnames */ hostlist_t read_wcoll(char *file, FILE * f) { char path[4096]; hostlist_t new; struct wcoll_ctx *ctx; FILE *fp = NULL; assert(f != NULL || file != NULL); if (strcmp (file, "-") == 0) { f = stdin; file = NULL; } if (f == NULL) { /* read_wcoll("file", NULL) */ if (access(file, R_OK) == -1 || !(fp = fopen(file, "r"))) errx("%p: %s: %m\n", file); } else /* read_wcoll(NULL, fp) */ fp = f; get_file_path (file, path, sizeof (path)); ctx = wcoll_ctx_create (path); wcoll_ctx_read_stream (ctx, fp); new = ctx->hl; wcoll_ctx_destroy (ctx); return new; } /* * vi:tabstop=4 shiftwidth=4 expandtab */ pdsh-2.36/COPYING0000664€^–Á €^–Á 0000004325415131211226021551 0ustar arif.ali@canonical.comarif.ali@canonical.com GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. pdsh-2.36/doc/0000775€^–Á €^–Á 0000000000015131211226021253 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/doc/Makefile.am0000664€^–Á €^–Á 0000000105415131211226023307 0ustar arif.ali@canonical.comarif.ali@canonical.com##**************************************************************************** ## $Id$ ##**************************************************************************** ## Process this file with automake to produce Makefile.in. ##**************************************************************************** man_MANS = pdsh.1 pdcp.1 dshbak.1 EXTRA_DIST = dshbak.1 install-data-local: $(INSTALL) -d -m 0755 "$(DESTDIR)$(mandir)/man1" ln -f -s pdcp.1 "$(DESTDIR)$(mandir)/man1/rpdcp.1" uninstall-local: $(RM) "$(DESTDIR)$(mandir)/man1/rpdcp.1" pdsh-2.36/doc/dshbak.10000664€^–Á €^–Á 0000000217315131211226022574 0ustar arif.ali@canonical.comarif.ali@canonical.com.\" $Id$ .\" .TH DSHBAK 1 "2011-02-26" .SH NAME dshbak \- format output from pdsh command .SH SYNOPSIS .B dshbak [\fIOPTION\fR].. .SH DESCRIPTION The \fBdshbak\fR program formats \fBpdsh\fR pdsh output for humans. Output from each node is consolidated, the leading "\fInode:\fR" is stripped, and a header block with the node name is added. If the \fI-c\fR option is specified, nodes with identical output are not displayed twice; instead, the header will contain a list of nodes. The list of nodes is further compressed into node ranges if the node names have a numeric suffix. .SH OPTIONS .TP .BI "-h" Display a summary of command line options. .TP .BI "-c" Do not display identical output from nodes twice. Instead, print the list of nodes with matching output in the header block. .TP .BI "-d " DIR Write consolidated node output to separate files in output directory \fIDIR\fR. Any existing files will be overwritten. .TP .BI "-f" With \fI-d\fR, force creation of specified \fIDIR\fR. .SH "ORIGIN" A rewrite of IBM dshbak(1) by Jim Garlick .br on LLNL's ASCI Blue-Pacific IBM SP system. .SH "SEE ALSO" .BR pdsh (1) pdsh-2.36/doc/pdsh.1.in0000664€^–Á €^–Á 0000004464515131211226022715 0ustar arif.ali@canonical.comarif.ali@canonical.com\." $Id$ .\" .\" .TH "@PACKAGE@" "1" "@host_os@" "@PDSH_VERSION@" .SH NAME pdsh \- issue commands to groups of hosts in parallel .SH SYNOPSIS \fBpdsh\fR [\fIoptions\fR]... command .SH DESCRIPTION \fBpdsh\fR is a variant of the rsh(1) command. Unlike rsh(1), which runs commands on a single remote host, \fBpdsh\fR can run multiple remote commands in parallel. \fBpdsh\fR uses a "sliding window" (or \fIfanout\fR) of threads to conserve resources on the initiating host while allowing some connections to time out. .LP When \fBpdsh\fR receives SIGINT (ctrl-C), it lists the status of current threads. A second SIGINT within one second terminates the program. Pending threads may be canceled by issuing ctrl-Z within one second of ctrl-C. Pending threads are those that have not yet been initiated, or are still in the process of connecting to the remote host. .LP If a remote command is not specified on the command line, \fBpdsh\fR runs interactively, prompting for commands and executing them when terminated with a carriage return. In interactive mode, target nodes that time out on the first command are not contacted for subsequent commands, and commands prefixed with an exclamation point will be executed on the local system. .LP The core functionality of \fBpdsh\fR may be supplemented by dynamically loadable modules. The modules may provide a new connection protocol (replacing the standard rcmd(3) protocol used by rsh(1)), filtering options (e.g. removing hosts that are "down" from the target list), and/or host selection options (e.g., \fI\-a\fR selects all hosts from a configuration file.). By default, \fBpdsh\fR must have at least one "rcmd" module loaded. See the \fBRCMD MODULES\fR section for more information. .SH "RCMD MODULES" The method by which \fBpdsh\fR runs commands on remote hosts may be selected at runtime using the \fI-R\fR option (See \fIOPTIONS\fR below). This functionality is ultimately implemented via dynamically loadable modules, and so the list of available options may be different from installation to installation. A list of currently available rcmd modules is printed when using any of the \fI-h\fR, \fI-V\fR, or \fI-L\fR options. The default rcmd module will also be displayed with the \fI-h\fR and \fI-V\fR options. .LP A list of \fIrcmd\fR modules currently distributed with \fBpdsh\fR follows. .TP 8 rsh Uses an internal, thread-safe implementation of BSD rcmd(3) to run commands using the standard rsh(1) protocol. .TP exec Executes an arbitrary command for each target host. The first of the \fBpdsh\fR remote arguments is the local command to execute, followed by any further arguments. Some simple parameters are substitued on the command line, including \fI%h\fR for the target hostname, \fI%u\fR for the remote username, and \fI%n\fR for the remote rank [0-n] (To get a literal \fI%\fR use \fI%%\fR). For example, the following would duplicate using the \fBssh\fR module to run \fBhostname\fR(1) across the hosts foo[0-10]: .nf pdsh -R exec -w foo[0-10] ssh -x -l %u %h hostname .fi and this command line would run \fBgrep\fR(1) in parallel across the files console.foo[0-10]: .nf pdsh -R exec -w foo[0-10] grep BUG console.%h .fi .TP ssh Uses a variant of popen(3) to run multiple copies of the ssh(1) command. .TP mrsh This module uses the mrsh(1) protocol to execute jobs on remote hosts. The mrsh protocol uses a credential based authentication, forgoing the need to allocate reserved ports. In other aspects, it acts just like rsh. Remote nodes must be running mrshd(8) in order for the mrsh module to work. .TP krb4 The krb4 module allows users to execute remote commands after authenticating with kerberos. Of course, the remote rshd daemons must be kerberized. .TP xcpu The xcpu module uses the xcpu service to execute remote commands. .SH OPTIONS The list of available options is determined at runtime by supplementing the list of standard \fBpdsh\fR options with any options provided by loaded \fIrcmd\fR and \fImisc\fR modules. In some cases, options provided by modules may conflict with each other. In these cases, the modules are incompatible and the first module loaded wins. .SH "Standard target nodelist options" .TP \fB\-w\fR \fITARGETS,...\fR Target and or filter the specified list of hosts. Do not use with any other node selection options (e.g. \fI\-a\fR, \fI\-g\fR, if they are available). No spaces are allowed in the comma-separated list. Arguments in the \fITARGETS\fR list may include normal host names, a range of hosts in hostlist format (See \fBHOSTLIST EXPRESSIONS\fR), or a single `-' character to read the list of hosts on stdin. If a host or hostlist is preceded by a `-' character, this causes those hosts to be explicitly excluded. If the argument is preceded by a single `^' character, it is taken to be the path to file containing a list of hosts, one per line. If the item begins with a `/' character, it is taken as a regular expression on which to filter the list of hosts (a regex argument may also be optionally trailed by another '/', e.g. /node.*/). A regex or file name argument may also be preceeded by a minus `-' to exclude instead of include thoses hosts. A list of hosts may also be preceded by "user@" to specify a remote username other than the default, or "rcmd_type:" to specify an alternate rcmd connection type for these hosts. When used together, the rcmd type must be specified first, e.g. "ssh:user1@host0" would use ssh to connect to host0 as user "user1." .TP .I \fB-x\fR \fIhost,host,...\fR Exclude the specified hosts. May be specified in conjunction with other target node list options such as \fI\-a\fR and \fI\-g\fR (when available). Hostlists may also be specified to the \fI\-x\fR option (see the \fBHOSTLIST EXPRESSIONS\fR section below). Arguments to \fI-x\fR may also be preceeded by the filename (`^') and regex ('/') characters as described above, in which case the resulting hosts are excluded as if they had been given to \fB\-w\fR and preceeded with the minus `-' character. .SH "Standard pdsh options" .TP .I "-S" Return the largest of the remote command return values. .TP .I "-k" Fail fast on connect failure or non-zero return code. .TP .I "-h" Output usage menu and quit. A list of available rcmd modules will also be printed at the end of the usage message. .TP .I "-s" Only on AIX, separate remote command stderr and stdout into two sockets. .TP .I "-q" List option values and the target nodelist and exit without action. .TP .I "-b" Disable ctrl-C status feature so that a single ctrl-C kills parallel job. (Batch Mode) .TP .I "-l user" This option may be used to run remote commands as another user, subject to authorization. For BSD rcmd, this means the invoking user and system must be listed in the user\'s .rhosts file (even for root). .TP .I "-t seconds" Set the connect timeout. Default is @CONNECT_TIMEOUT@ seconds. This option may also be set via the PDSH_CONNECT_TIMEOUT environment variable. .TP .I "-u seconds" Set a limit on the amount of time a remote command is allowed to execute. Default is no limit. See note in LIMITATIONS if using \fI-u\fR with ssh. This option may also be set via the PDSH_COMMAND_TIMEOUT environment variable. .TP .I "-f number" Set the maximum number of simultaneous remote commands to \fInumber\fR. The default is @FANOUT@. .TP .I "-R name" Set rcmd module to \fIname\fR. This option may also be set via the PDSH_RCMD_TYPE environment variable. A list of available rcmd modules may be obtained via the \fI-h\fR, \fI-V\fR, or \fI-L\fR options. The default will be listed with \fI-h\fR or \fI-V\fI. .TP .I "-M name,..." When multiple \fBmisc\fR modules provide the same options to \fBpdsh\fR, the first module initialized "wins" and subsequent modules are not loaded. The \fI-M\fR option allows a list of modules to be specified that will be force-initialized before all others, in-effect ensuring that they load without conflict (unless they conflict with eachother). This option may also be set via the PDSH_MISC_MODULES environment variable. .TP .I "-L" List info on all loaded \fBpdsh\fR modules and quit. .TP .I "-N" Disable hostname: prefix on lines of output. .TP .I "-d" Include more complete thread status when SIGINT is received, and display connect and command time statistics on stderr when done. Also, display any module loading errors to stderr, including option conflicts. .TP .I "-V" Output \fBpdsh\fR version information, along with list of currently loaded modules, and exit. .SH "machines module options" .TP .I "-a" Target all nodes from machines file. .SH "genders module options" .LP In addition to the genders options presented below, the genders attribute pdsh_rcmd_type may also be used in the genders database to specify an alternate rcmd connect type than the pdsh default for hosts with this attribute. For example, the following line in the genders file .nf host0 pdsh_rcmd_type=ssh .fi would cause \fBpdsh\fR to use ssh to connect to host0, even if rsh were the default. This can be overridden on the commandline with the "rcmd_type:host0" syntax. .TP .I "-A" Target all nodes in genders database. The \fI-A\fR option will target every host listed in genders -- if you want to omit some hosts by default, see the \fI-a\fR option below. .TP .I "-a" Target all nodes in genders database except those with the "pdsh_all_skip" attribute. This is shorthand for running "pdsh -A -X pdsh_all_skip ..." .TP .I "-g attr[=val][,attr[=val],...]" Target nodes that match any of the specified genders attributes (with optional values). Conflicts with the \fI\-a\fR option. If used in combination with other node selection options like \fI-w\fR, the \fI-g\fR option will select from the supplied node list, instead of from the genders file as a whole. Otherwise, This option targets the alternate hostnames in the genders database by default. The \fI\-i\fR option provided by the genders module may be used to translate these to the canonical genders hostnames. If the installed version of genders supports it, attributes supplied to \fI-g\fR may also take the form of genders \fBqueries\fR. Genders \fBqueries\fR will query the genders database for the union, intersection, difference, or complement of genders attributes and values. The set operation union is represented by two pipe symbols ('||'), intersection by two ampersand symbols ('&&'), difference by two minus symbols ('--'), and complement by a tilde ('~'). Parentheses may be used to change the order of operations. See the nodeattr(1) manpage for examples of genders \fBqueries\fR. .TP .I "-X attr[=val][,attr[=val],...]" Exclude nodes that match any of the specified genders attributes (optionally with values). This option may be used in combination with any other of the node selection options (e.g. \fI-w\fR, \fI-g\fR, \fI-a\fR, ...). If the installed version of genders supports it, arguments to \fI-X\fR may also take the form of genders \fBqueries\fR. Please see documentation for the genders \fI-g\fR option for more information about genders \fBqueries\fR. .TP .I "-i" Request translation between canonical and alternate hostnames. .TP .I "-F filename" Read genders information from \fIfilename\fR instead of the system default genders file. If \fIfilename\fR doesn't specify an absolute path then it is taken to be relative to the directory specified by the \fIPDSH_GENDERS_DIR\fR environment variable (/etc by default). An alternate genders file may also be specified via the \fIPDSH_GENDERS_FILE\fR environment variable. .SH "nodeupdown module options" .TP .I "-v" Eliminate target nodes that are considered "down" by libnodeupdown. .SH "slurm module options" The \fBslurm\fI module allows \fBpdsh\fR to target nodes based on currently running SLURM jobs. The \fBslurm\fR module is typically called after all other node selection options have been processed, and if no nodes have been selected, the module will attempt to read a running jobid from the SLURM_JOBID environment variable (which is set when running under a SLURM allocation). If SLURM_JOBID references an invalid job, it will be silently ignored. .TP .I "-j jobid[,jobid,...]" Target list of nodes allocated to the SLURM job \fIjobid\fR. This option may be used multiple times to target multiple SLURM jobs. The special argument "all" can be used to target all nodes running SLURM jobs, e.g. \fI-j all\fR. .TP .I "-P partition[,partition,...]" Target list of nodes containing in the SLURM partition \fIpartition\fR. This option may be used multiple times to target multiple SLURM partitions and/or partitions may be given in a comma-delimited list. .TP .I "-C feature[,feature,...]" Restrict selected nodes to those SLURM nodes that have one of the specified \fIfeature\fRs active. This option should be used in combination with other node selectors, and filters the resulting list. Any nodes that do not have one of the specified features (or is not a SLURM node) will be excluded. .SH "torque module options" The \fBtorque\fI module allows \fBpdsh\fR to target nodes based on currently running Torque/PBS jobs. Similar to the slurm module, the \fBtorque\fR module is typically called after all other node selection options have been processed, and if no nodes have been selected, the module will attempt to read a running jobid from the PBS_JOBID environment variable (which is set when running under a Torque allocation). .TP .I "-j jobid[,jobid,...]" Target list of nodes allocated to the Torque job \fIjobid\fR. This option may be used multiple times to target multiple Torque jobs. .SH "dshgroup module options" The dshgroup module allows pdsh to use dsh (or Dancer's shell) style group files from /etc/dsh/group/ or ~/.dsh/group/. The default search path may be overridden with the DSHGROUP_PATH environment variable, a colon-separated list of directories to search. The default value for DSHGROUP_PATH is /etc/dsh/group. .TP .I "-g groupname,..." Target nodes in dsh group file "groupname" found in either ~/.dsh/group/groupname or /etc/dsh/group/groupname. .TP .I "-X groupname,..." Exclude nodes in dsh group file "groupname." .PP As an enhancement in \fBpdsh\fR, dshgroup files may optionally include other dshgroup files via a special \fI#include STRING\fR syntax. The argument to \fI#include\fR may be either a file path, or a group name, in which case the path used to search for the group file is the same as if the group had been specified to \fI-g\fR. .SH "netgroup module options" The netgroup module allows pdsh to use standard netgroup entries to build lists of target hosts. (/etc/netgroup or NIS) .TP .I "-g groupname,..." Target nodes in netgroup "groupname." .TP .I "-X groupname,..." Exclude nodes in netgroup "groupname." .SH "ENVIRONMENT VARIABLES" .PP .TP PDSH_RCMD_TYPE Equivalent to the \fI-R\fR option, the value of this environment variable will be used to set the default rcmd module for pdsh to use (e.g. ssh, rsh). .TP PDSH_SSH_ARGS Override the standard arguments that \fBpdsh\fR passes to the ssh(1) command ("-2 -a -x -l%u %h"). The use of the parameters \fB%u\fR, \fB%h\fR, and \fB%n\fR (as documented in the \fBrcmd/exec\fR section above) is optional. If these parameters are missing, \fBpdsh\fR will append them to the ssh commandline because it is assumed they are mandatory. .TP PDSH_SSH_ARGS_APPEND Append additional options to the ssh(1) command invoked by \fBpdsh\fR. For example, PDSH_SSH_ARGS_APPEND="-q" would run ssh in quiet mode, or "-v" would increase the verbosity of ssh. (Note: these arguments are actually prepended to the ssh commandline to ensure they appear before any target hostname argument to ssh.) .TP WCOLL If no other node selection option is used, the WCOLL environment variable may be set to a filename from which a list of target hosts will be read. The file should contain a list of hosts, one per line (though each line may contain a hostlist expression. See \fIHOSTLIST EXPRESSIONS\fR section below). .TP DSHPATH If set, the path in DSHPATH will be used as the PATH for the remote processes. .TP FANOUT Set the \fBpdsh\fR fanout (See description of \fI-f\fR above). .SH "HOSTLIST EXPRESSIONS" As noted in sections above \fBpdsh\fR accepts lists of hosts the general form: prefix[n-m,l-k,...], where n < m and l < k, etc., as an alternative to explicit lists of hosts. This form should not be confused with regular expression character classes (also denoted by ``[]''). For example, foo[19] does not represent an expression matching foo1 or foo9, but rather represents the degenerate hostlist: foo19. The hostlist syntax is meant only as a convenience on clusters with a "prefixNNN" naming convention and specification of ranges should not be considered necessary -- the list foo1,foo9 could be specified as such, or by the hostlist foo[1,9]. Some examples of usage follow: .nf Run command on foo01,foo02,...,foo05 pdsh -w foo[01-05] command Run command on foo7,foo9,foo10 pdsh -w foo[7,9-10] command Run command on foo0,foo4,foo5 pdsh -w foo[0-5] -x foo[1-3] command .fi A suffix on the hostname is also supported: .nf Run command on foo0-eth0,foo1-eth0,foo2-eth0,foo3-eth0 pdsh -w foo[0-3]-eth0 command .fi As a reminder to the reader, some shells will interpret brackets ('[' and ']') for pattern matching. Depending on your shell, it may be necessary to enclose ranged lists within quotes. For example, in tcsh, the first example above should be executed as: pdsh -w "foo[01-05]" command .SH "ORIGIN" Originally a rewrite of IBM dsh(1) by Jim Garlick on LLNL's ASCI Blue-Pacific IBM SP system. It is now used on Linux clusters at LLNL. .SH "LIMITATIONS" .LP When using \fBssh\fR for remote execution, expect the stderr of ssh to be folded in with that of the remote command. When invoked by \fBpdsh\fR, it is not possible for \fBssh\fR to prompt for passwords if RSA/DSA keys are configured properly, etc.. For \fBssh\fR implementations that suppport a connect timeout option, \fBpdsh\fR attempts to use that option to enforce the timeout (e.g. -oConnectTimeout=T for OpenSSH), otherwise connect timeouts are not supported when using \fBssh\fR. Finally, there is no reliable way for \fBpdsh\fR to ensure that remote commands are actually terminated when using a command timeout. Thus if \fI-u\fR is used with \fBssh\fR commands may be left running on remote hosts even after timeout has killed local \fBssh\fR processes. The number of nodes that \fBpdsh\fR can simultaneously execute remote jobs on is limited by the maximum number of threads that can be created concurrently, as well as the availability of reserved ports in the rsh module. On systems that implement Posix threads, the limit is typically defined by the constant PTHREADS_THREADS_MAX. .SH "FILES" .SH "SEE ALSO" rsh(1), ssh(1), dshbak(1), pdcp(1) pdsh-2.36/doc/pdcp.1.in0000664€^–Á €^–Á 0000002270215131211226022673 0ustar arif.ali@canonical.comarif.ali@canonical.com\." $Id$ .\" .\" .\" .TH "@PACKAGE@" "1" "@host_os@" "@PDSH_VERSION@" .SH NAME pdcp \- copy files to groups of hosts in parallel .br rpdcp \- (reverse pdcp) copy files from a group of hosts in parallel .SH SYNOPSIS \fBpdcp\fR [\fIoptions\fR]... src [src2...] dest .br \fBrpdcp\fR [\fIoptions\fR]... src [src2...] dir .SH DESCRIPTION \fBpdcp\fR is a variant of the rcp(1) command. Unlike rcp(1), which copies files to a single remote host, \fBpdcp\fR can copy files to multiple remote hosts in parallel. However, \fBpdcp\fR does not recognize files in the format ``rname@rhost:path,'' therefore all source files must be on the local host machine. Destination nodes must be listed on the \fBpdcp\fR command line using a suitable target nodelist option (See the \fIOPTIONS\fR section below). Each destination node listed must have \fBpdcp\fR installed for the copy to succeed. .LP When \fBpdcp\fR receives SIGINT (ctrl-C), it lists the status of current threads. A second SIGINT within one second terminates the program. Pending threads may be canceled by issuing ctrl-Z within one second of ctrl-C. Pending threads are those that have not yet been initiated, or are still in the process of connecting to the remote host. .LP Like pdsh(1), the functionality of \fBpdcp\fR may be supplemented by dynamically loadable modules. In \fBpdcp\fR, the modules may provide a new connect protocol (replacing the standard rsh(1) protocol), filtering options (e.g. excluding hosts that are down), and/or host selection options (e.g. \fI-a\fR selects all nodes from a local config file). By default, \fBpdcp\fR requires at least one "rcmd" module to be loaded (to provide the channel for remote copy). .SH "REVERSE PDCP" \fBrpdcp\fR performs a reverse parallel copy. Rather than copying files to remote hosts, files are retrieved from remote hosts and stored locally. All directories or files retrieved will be stored with their remote hostname appended to the filename. The destination file must be a directory when this option is used. .LP In other respects, \fBrpdcp\fR is exactly like \fBpdcp\fR, and further statements regarding \fBpdcp\fR in this manual also apply to \fBrpdcp\fR. .SH "RCMD MODULES" The method by which \fBpdcp\fR connects to remote hosts may be selected at runtime using the \fI-R\fR option (See \fIOPTIONS\fR below). This functionality is ultimately implemented via dynamically loadable modules, and so the list of available options may be different from installation to installation. A list of currently available rcmd modules is printed when using any of the \fI-h\fR, \fI-V\fR, or \fI-L\fR options. The default rcmd module will also be displayed with the \fI-h\fR and \fI-V\fR options. .LP A list of \fIrcmd\fR modules currently distributed with \fBpdcp\fR follows. .TP 8 rsh Uses an internal, thread-safe implementation of BSD rcmd(3) to run commands using the standard rsh(1) protocol. .TP ssh Uses a variant of popen(3) to run multiple copies of the ssh(1) command. .TP mrsh This module uses the mrsh(1) protocol to execute jobs on remote hosts. The mrsh protocol uses a credential based authentication, forgoing the need to allocate reserved ports. In other aspects, it acts just like rsh. .TP krb4 The krb4 module allows users to execute remote commands after authenticating with kerberos. Of course, the remote rshd daemons must be kerberized. .TP xcpu The xcpu module uses the xcpu service to execute remote commands. .SH OPTIONS The list of available \fBpdcp\fR options is determined at runtime by supplementing the list of standard \fBpdcp\fR options with any options provided by loaded \fIrcmd\fR and \fImisc\fR modules. In some cases, options provided by modules may conflict with each other. In these cases, the modules are incompatible and the first module loaded wins. .SH "Standard target nodelist options" .TP \fB\-w\fR \fITARGETS,...\fR Target and or filter the specified list of hosts. Do not use with any other node selection options (e.g. \fI\-a\fR, \fI\-g\fR, if they are available). No spaces are allowed in the comma-separated list. Arguments in the \fITARGETS\fR list may include normal host names, a range of hosts in hostlist format (See \fBHOSTLIST EXPRESSIONS\fR), or a single `-' character to read the list of hosts on stdin. If a host or hostlist is preceded by a `-' character, this causes those hosts to be explicitly excluded. If the argument is preceded by a single `^' character, it is taken to be the path to file containing a list of hosts, one per line. If the item begins with a `/' character, it is taken as a regular expression on which to filter the list of hosts (a regex argument may also be optionally trailed by another '/', e.g. /node.*/). A regex or file name argument may also be preceeded by a minus `-' to exclude instead of include thoses hosts. A list of hosts may also be preceded by "user@" to specify a remote username other than the default, or "rcmd_type:" to specify an alternate rcmd connection type for these hosts. When used together, the rcmd type must be specified first, e.g. "ssh:user1@host0" would use ssh to connect to host0 as user "user1." .TP .I \fB-x\fR \fIhost,host,...\fR Exclude the specified hosts. May be specified in conjunction with other target node list options such as \fI\-a\fR and \fI\-g\fR (when available). Hostlists may also be specified to the \fI\-x\fR option (see the \fBHOSTLIST EXPRESSIONS\fR section below). Arguments to \fI-x\fR may also be preceeded by the filename (`^') and regex ('/') characters as described above, in which case the resulting hosts are excluded as if they had been given to \fB\-w\fR and preceeded with the minus `-' character. .SH "Standard pdcp options" .TP .I "-h" Output usage menu and quit. A list of available rcmd modules will be printed at the end of the usage message. .TP .I "-q" List option values and the target nodelist and exit without action. .TP .I "-b" Disable ctrl-C status feature so that a single ctrl-C kills parallel copy. (Batch Mode) .TP .I "-r" Copy directories recursively. .TP .I "-p" Preserve modification time and modes. .TP .I "-e PATH" Explicitly specify path to remote \fBpdcp\fR binary instead of using the locally executed path. Can also be set via the environment variable PDSH_REMOTE_PDCP_PATH. .TP .I "-l user" This option may be used to copy files as another user, subject to authorization. For BSD rcmd, this means the invoking user and system must be listed in the user\'s .rhosts file (even for root). .TP .I "-t seconds" Set the connect timeout. Default is @CONNECT_TIMEOUT@ seconds. .TP .I "-f number" Set the maximum number of simultaneous remote copies to \fInumber\fR. The default is @FANOUT@. .TP .I "-R name" Set rcmd module to \fIname\fR. This option may also be set via the PDSH_RCMD_TYPE environment variable. A list of available rcmd modules may be obtained via either the \fI-h\fR or \fI-L\fR options. .TP .I "-M name,..." When multiple \fBmisc\fR modules provide the same options to \fBpdsh\fR, the first module initialized "wins" and subsequent modules are not loaded. The \fI-M\fR option allows a list of modules to be specified that will be force-initialized before all others, in-effect ensuring that they load without conflict (unless they conflict with eachother). This option may also be set via the PDSH_MISC_MODULES environment variable. .TP .I "-L" List info on all loaded \fBpdcp\fR modules and quit. .TP .I "-d" Include more complete thread status when SIGINT is received, and display connect and command time statistics on stderr when done. Also, display any module loading errors on stderr, include option conflicts. .TP .I "-V" Output \fBpdcp\fR version information, along with list of currently loaded modules, and exit. .SH "HOSTLIST EXPRESSIONS" As noted in sections above, .B pdcp accepts ranges of hostnames in the general form: prefix[n-m,l-k,...], where n < m and l < k, etc., as an alternative to explicit lists of hosts. This form should not be confused with regular expression character classes (also denoted by ``[]''). For example, foo[19] does not represent foo1 or foo9, but rather represents a degenerate range: foo19. This range syntax is meant only as a convenience on clusters with a prefixNN naming convention and specification of ranges should not be considered necessary -- the list foo1,foo9 could be specified as such, or by the range foo[1,9]. Some examples of range usage follow: .nf Copy /etc/hosts to foo01,foo02,...,foo05 pdcp -w foo[01-05] /etc/hosts /etc Copy /etc/hosts to foo7,foo9,foo10 pdcp -w foo[7,9-10] /etc/hosts /etc Copy /etc/hosts to foo0,foo4,foo5 pdcp -w foo[0-5] -x foo[1-3] /etc/hosts /etc .fi As a reminder to the reader, some shells will interpret brackets ('[' and ']') for pattern matching. Depending on your shell, it may be necessary to enclose ranged lists within quotes. For example, in tcsh, the first example above should be executed as: pdcp -w "foo[01-05]" /etc/hosts /etc .SH "ORIGIN" Pdsh/pdcp was originally a rewrite of IBM dsh(1) by Jim Garlick on LLNL's ASCI Blue-Pacific IBM SP system. It is now also used on Linux clusters at LLNL. .SH "LIMITATIONS" When using .B ssh for remote execution, stderr of ssh to be folded in with that of the remote command. When invoked by pdcp, it is not possible for ssh to prompt for confirmation if a host key changes, prompt for passwords if RSA keys are not configured properly, etc.. Finally, the connect timeout is only adjustable with ssh when the underlying ssh implementation supports it, and pdsh has been built to use the correct option. .SH "SEE ALSO" pdsh(1) pdsh-2.36/DISCLAIMER.LLNS0000664€^–Á €^–Á 0000000256615131211226022625 0ustar arif.ali@canonical.comarif.ali@canonical.comThis work was produced at the Lawrence Livermore National Laboratory (LLNL) under Contract No. DE-AC52-07NA27344 (Contract 44) between the U.S. Department of Energy (DOE) and Lawrence Livermore National Security, LLC (LLNS) for the operation of LLNL. This work was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor Lawrence Livermore National Security, LLC nor any of their employees, makes any warranty, express or implied, or assumes any liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately-owned rights. Reference herein to any specific commercial products, process, or services by trade name, trademark, manufacturer or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or Lawrence Livermore National Security, LLC. The views and opinions of authors expressed herein do not necessarily state or reflect those of the Untied States Government or Lawrence Livermore National Security, LLC, and shall not be used for advertising or product endorsement purposes. The precise terms and conditions for copying, distribution, and modification are specified in the files "COPYING" and "COPYING.LESSER". pdsh-2.36/scripts/0000775€^–Á €^–Á 0000000000015131211226022175 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/scripts/Makefile.am0000664€^–Á €^–Á 0000000053315131211226024232 0ustar arif.ali@canonical.comarif.ali@canonical.com##**************************************************************************** ## $Id$ ##**************************************************************************** ## Process this file with automake to produce Makefile.in. ##**************************************************************************** bin_SCRIPTS = dshbak EXTRA_DIST = dshbak pdsh-2.36/scripts/dshbak0000775€^–Á €^–Á 0000002072715131211226023367 0ustar arif.ali@canonical.comarif.ali@canonical.com#!/usr/bin/perl -w ############################################################################# # $Id$ ############################################################################# # # Copyright (C) 2001-2006 The Regents of the University of California. # Copyright (C) 2007-2011 Lawrence Livermore National Security, LLC. # Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). # Written by Jim Garlick . # UCRL-CODE-2003-005. # # This file is part of Pdsh, a parallel remote shell program. # For details, see . # # Pdsh is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your option) # any later version. # # Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along # with Pdsh; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # ############################################################################# require 5.003; use strict; use Getopt::Std; use File::Basename qw/ basename /; use File::Path; use constant GETOPTS_ARGS => "chfd:"; use vars map { "\$opt_$_" } split(/:*/, GETOPTS_ARGS); ############################################################################# my $prog = basename $0; my $usage = <) { my ($tag, $data) = m/^\s*(\S+?)\s*: ?(.*\n)$/; # Ignore lines that aren't prefixed with a hostname: next unless defined $tag and "$tag" ne ""; push(@{$lines{$tag}}, $data); } return %lines; } # # Print the standard dshbak header # sub print_header { my $div = "----------------\n"; print $div, join (",", @_), "\n", $div } # # Normal output function # sub do_output_normal { my ($tag) = @_; &print_header ($tag); print @{$lines{$tag}}; } # # Put each host output into separate files in directory # specified by $opt_d. # sub do_output_per_file { my ($tag) = @_; my $file = "$opt_d/$tag"; open (OUTPUT, ">$file") || &log_fatal ("Failed to open output file '$file': $!\n"); print OUTPUT @{$lines{$tag}}; } # # Print identical output only once, tagged with the list of # hosts producing matching data. # sub do_output_coalesced { my ($tag) = @_; my @identical = (); # # Ignore any deleted tags, lines from these hosts has already # been printed: # return if not defined ($lines{$tag}); # # Look for other hosts with identical output: # for my $tag2 (keys %lines) { next if ($tag2 eq $tag); next unless (cmp_list ($lines{$tag}, $lines{$tag2})); # # Output is identical -- stash the tag of this match and # delete it from further processing: # push (@identical, $tag2); delete ($lines{$tag2}); } &print_header (compress (sort (@identical, $tag))); print @{$lines{$tag}}; } # # Compare two lists-o-strings # \@l1 (IN) list1 # \@l2 (IN) list2 # RETURN 1 if match, 0 if not # sub cmp_list { my ($l1, $l2) = @_; my ($i, $retval); $retval = 1; if ($#{$l1} != $#{$l2}) { return 0; } for ($i = 0; $i <= $#{$l1} && $retval == 1; $i++) { if (!defined(${$l2}[$i]) || ${$l1}[$i] ne ${$l2}[$i]) { $retval = 0; } } return $retval; } sub usage { my ($rc) = $@ ? $@ : 0; printf STDERR $usage; exit $rc; } # # Try to compress a list of hosts into a host range # sub compress { my %suffixes = (); my @list = (); # Each suffix key points to a list of hostnames with corresponding # suffix stripped off. push (@{$suffixes{$$_[1]}}, $$_[0]) for map { [/(.*?\d*)(\D*)$/] } sortn (@_); # # For each suffix, run compress on hostnames without suffix, then # reapply suffix name. for my $suffix (keys %suffixes) { map { push (@list, "$_$suffix") } compress_inner (@{$suffixes{$suffix}}); } local $"=","; return wantarray ? @list : "@list"; } sub compress_inner { my %rng = comp(@_); my @list = (); local $"=","; @list = map { $_ . (@{$rng{$_}}>1 || ${$rng{$_}}[0] =~ /-/ ? "[@{$rng{$_}}]" : "@{$rng{$_}}" ) } sort keys %rng; return wantarray ? @list : "@list"; } # # Return the zeropadded width of $n, where the zero-padded # width is the minimum format width a number with the given # zero-padding. That is, no zero-padding is 1, because 0-9 # have a minimum width of 1, "01" has a width of 2, 010 has # a width of 3 and so on. # sub zeropadwidth { my ($n) = @_; # # zeropad width is the length of $n if there are any leading # zeros and the number is not zero itself. # return length $n if (($n =~ /^0/) and ($n ne "0")); # # If no leading zeros (or $n == 0) then the width is always '1' # return 1; } sub comp { my (%i) = (); my (%s) = (); # turn off warnings here to avoid perl complaints about # uninitialized values for members of %i and %s local ($^W) = 0; for my $host (sortn (@_)) { my ($p, $n) = $host =~ /(.*?)(\d*)$/; my $zp = &zeropadwidth ($n); # # $s{$p} is a reference to an array of arrays # that indicate individual range elements of # the form [ N_start, N_end]. If only one element # is present then the range element is a singleton. # # $i{$p}{$zp}${n} tracks the index of prefix $p and suffix $n # with zero-padding $zp into the @{$s{$p}} array. # # Need to check if $n-1 exists in the $s{$p} array, but the # zero-padded width must be compatible. e.g.. "9" and "09" # are compatible with 10, but not with 010. # my $idx = $i{$p}{$zp}{$n-1}; # # If the current zeropad is 1, and the length of $n is > 1, # then we check for a previous number with either zp == 1 or # zp == length. This catches 09-10, 099-100, etc . # if (!defined $idx && $zp == 1) { $idx = $i{$p}{length $n}{$n-1}; } if (defined $idx) { # # $n - 1 is already in array, so update END: # $s{$p}[$idx][1] = "$n"; $i{$p}{$zp}{$n-0} = $idx; } else { # # Otherwise, we create a new single entry # and update $i{} (Use $n-0 to force a number) # push (@{$s{$p}}, [ $n ]); $i{$p}{$zp}{$n-0} = $#{$s{$p}}; } } # # # Now return $s{} as a hash of prefixes with a list of range elemts: # e.g. $s{"host"} = [ "1-10", "25", "100-101" ] # for my $key (keys %s) { @{$s{$key}} = map { $#$_>0 ? "$$_[0]-$$_[$#$_]" : "$$_[0]" } @{$s{$key}}; } return %s; } # sortn: # # sort a group of alphanumeric strings by the last group of digits on # those strings, if such exists (good for numerically suffixed host lists) # sub sortn { map {$$_[0]} sort {($$a[1]||0)<=>($$b[1]||0)} map {[$_,/(\d*)$/]} @_; } pdsh-2.36/INSTALL0000664€^–Á €^–Á 0000002225115131211226021541 0ustar arif.ali@canonical.comarif.ali@canonical.comCopyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 0. `cd' to the directory containing the package's source code and type ./bootstrap to make the build scripts. Your system must have autoconf, automake, and libtool installed to properly build the build scripts. 1. Type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for variables by setting them in the environment. You can do that on the command line like this: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. pdsh-2.36/.github/0000775€^–Á €^–Á 0000000000015131211226022046 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/.github/workflows/0000775€^–Á €^–Á 0000000000015131211226024103 5ustar arif.ali@canonical.comarif.ali@canonical.compdsh-2.36/.github/workflows/codeql.yml0000664€^–Á €^–Á 0000000150315131211226026074 0ustar arif.ali@canonical.comarif.ali@canonical.comname: "CodeQL" on: push: branches: [ "master" ] pull_request: branches: [ "master" ] schedule: - cron: "29 19 * * 2" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ cpp ] steps: - name: Checkout uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{ matrix.language }}" pdsh-2.36/.github/workflows/main.yml0000664€^–Á €^–Á 0000000426515131211226025561 0ustar arif.ali@canonical.comarif.ali@canonical.comon: [push, pull_request] name: ci jobs: check-pr: name: validate commits runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - run: git fetch origin master - uses: flux-framework/pr-validator@master ubuntu: name: ubuntu runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: install dependencies run: sudo apt install libgenders0-dev libslurm-dev - name: fix permissions run: chmod -R o-w /home/runner || true - name: configure run: > ./bootstrap && ./configure --with-ssh --with-exec --with-netgroup --with-dshgroups --with-genders --with-machines --with-slurm - name: make run: make -j 2 CFLAGS="-Werror" - name: make check run: make -j 2 check macos: name: macos runs-on: macos-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: install dependencies run: brew install autoconf automake libtool - name: fix permissions run: chmod -R o-w /home/runner || true - name: configure macos run: > ./bootstrap && ./configure --with-ssh --with-exec --with-netgroup --with-dshgroups - name: make check run: make -j 2 check coverage: name: coverage runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: install dependencies run: sudo apt install libgenders0-dev lcov - name: fix permissions run: chmod -R o-w /home/runner || true - name: configure run: > ./bootstrap && ./configure --with-ssh --with-exec --with-netgroup --with-dshgroups --with-genders --with-machines --enable-code-coverage - name: make check-code-coverage run: make check-code-coverage - name: coverage report uses: codecov/codecov-action@v5