From 0ff9634cb2ae3170ef1936eb557ac0647d1019e1 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Thu, 29 Dec 2016 14:10:36 +0100 Subject: [PATCH] clusterd: Implement --exec @node cmd [args...] This command might be usefull, e.g. if sshd is not responding. Examples: clusterd --exec @theinternet restart sshd.service clusterd --exec @theinternet dmesg clusterd --exec @theinternet 'grep sshd /var/log/messages|tail -100' The remote command is executed by bash, so the pipe in the last example is on the remote node stdout and stderr are delivered seperatly and might be redirected to different channels on the local side. If the remote command exits with a non-zero exit status, the local command fails with exit status 1. The slave part already existed, because we once had a remote execution command. We removed it, because it was considered to be to dangerous. The former remote execution command allowed parallel execution on all nodes, which enabled an admin to kill all systems by mistake in an instance. Now we only enable remote execution on a single node. --- clusterd/clusterd | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/clusterd/clusterd b/clusterd/clusterd index 5ec48f5..9b01eb3 100755 --- a/clusterd/clusterd +++ b/clusterd/clusterd @@ -1274,6 +1274,22 @@ sub expand_netgroup_hosts { return @out; } +sub exec_at { + my ($host,@cmd)=@_; + sync_cluster_pw() or die "$CLUSTER_PW_FILE: $!\n"; + my $s=new IO::Socket::INET(PeerAddr=>$host,PeerPort=>$CLP_PORT); + unless (defined $s) { + die "$host: $!\n"; + } + clp_send_message($s,'CMD',@cmd); + my $pbuffer=''; + my $olbuffer=''; + my $elbuffer=''; + Donald::Select::reader($s,\&cmd_rx,$host,$s,\$pbuffer,\$olbuffer,\$elbuffer); + $slave=1; + Donald::Select::run() if $slave;; +} + sub lsof { my ($pattern)=@_; sync_cluster_pw() or die "$CLUSTER_PW_FILE: $!\n"; @@ -1367,13 +1383,13 @@ sub cmd_msg { } elsif ($channel eq 'O') { my $msg=$$olbufref.substr($data,1); for ($msg=~/([^\n]*\n)/gs) { - print "$host $_"; + print "$host: $_"; } ($$olbufref)=$msg=~/([^\n]*)\z/; } elsif ($channel eq 'E') { my $msg=$$elbufref.substr($data,1); for ($msg=~/([^\n]*\n)/gs) { - print STDERR "$host $_"; + print STDERR "$host: $_"; } ($$elbufref)=$msg=~/([^\n]*)\z/; } @@ -1389,6 +1405,7 @@ usage: $0 [options] --push-amd-tar # broadcast /etc/amd --send-restart # broadcast a restart request to all nodes --exec mkmotd # execute /usr/sbin/mkmotd.pl on all nodes + --exec @node cmd [args...] # execute cmd on node --flush-gidcache # flush rpc auth.unix.gid cache on all nodes --lsof=pattern @@ -1423,9 +1440,15 @@ if (defined $options{'push'}) { $donald_s=new Donald::Select::INET(Proto=>'udp') or die "$!\n"; push_file($donald_s,$options{'push'}); } elsif (defined $options{'exec'}) { - sync_cluster_pw() or die "$CLUSTER_PW_FILE: $!\n"; - $donald_s=new Donald::Select::INET(Proto=>'udp') or die "$!\n"; - send_exec($donald_s,$options{'exec'}); + if (substr($options{'exec'},0,1) eq '@') { + length(length($options{'exec'})>1) or die USAGE; + @ARGV>=1 or die USAGE; + exec_at(substr($options{'exec'},1),@ARGV); + } else { + sync_cluster_pw() or die "$CLUSTER_PW_FILE: $!\n"; + $donald_s=new Donald::Select::INET(Proto=>'udp') or die "$!\n"; + send_exec($donald_s,$options{'exec'}); + } } elsif (defined $options{'push_amd_tar'}) { sync_cluster_pw() or die "$CLUSTER_PW_FILE: $!\n"; $donald_s=new Donald::Select::INET(Proto=>'udp') or die "$!\n";