This scsh script logs into a specified list of hosts using OpenSSH and runs a command there. For example:
sshfe host1 host2 host3 % rm -rf /
The % character is used to seperate the list of host names and the command (and its arguments) to run. The code assumes that Sunterlib is installed because it uses the Scheme implementation of the Concurrent ML API.
#!/bin/sh
exec scsh -lel cml/load.scm -o srfi-1 -o threads \
-o rendezvous -o rendezvous-channels -s "$0" "$@"
!#
(define-syntax run/strings-status
(syntax-rules ()
((_ epf ...)
(call-with-values
(lambda ()
(run/port+proc epf ...))
(lambda (port proc)
(let ((string-list (port->string-list port))
(status (wait proc)))
(close-input-port port)
(values string-list status)))))))
(define display*
(lambda things
(for-each display things)
(newline)))
(define (run-processes hosts command)
(map (lambda (host)
(let ((channel (make-channel)))
(spawn
(lambda ()
(send channel
(call-with-values
(lambda ()
(run/strings-status
("ssh" ,host ,@command) (= 2 1)))
(lambda (strings status)
(list strings status host))))))
channel))
hosts))
(define (handle-result lst)
(call-with-values
(lambda ()
(apply values lst))
(lambda (strings status host)
(newline)
(display* "result for " host " exited with "
status ": ")
(for-each display* strings))))
(define (collect-results channels)
(let lp ((channels channels))
(if (null? channels)
(values)
(apply
select
(map (lambda (channel)
(wrap (receive-rv channel)
(lambda (lst)
(handle-result lst)
(lp (delete channel channels)))))
channels)))))
(define (parse-arguments args)
(let lp ((args args) (hosts '()))
(cond
((null? args)
(values hosts '()))
((string=? (car args) "%")
(values hosts (cdr args)))
(else
(lp (cdr args) (cons (car args) hosts))))))
(call-with-values
(lambda ()
(parse-arguments command-line-arguments))
(lambda (hosts command/args)
(collect-results
(run-processes hosts command/args))))
;;; Local Variables:
;;; mode:scheme
;;; End: