Edit

thodg/cl-unix-cybernetics/host.lisp

Branch :

  • Show log

    Commit

  • Author : Thomas de Grivel
    Date : 2014-10-03 11:42:57
    Hash : 79277171
    Message : Run commands on local or remote hosts using sh or ssh.

  • host.lisp
  • ;;
    ;;  adams  -  Remote system administration tools
    ;;
    ;;  Copyright 2013 Thomas de Grivel <billitch@gmail.com>
    ;;
    ;;  Permission to use, copy, modify, and distribute this software for any
    ;;  purpose with or without fee is hereby granted, provided that the above
    ;;  copyright notice and this permission notice appear in all copies.
    ;;
    ;;  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    ;;  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    ;;  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ;;  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    ;;  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    ;;  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    ;;  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    ;;
    
    (in-package :adams)
    
    ;;  Host
    
    (defclass host ()
      ((name :type string
    	 :initarg :hostname
    	 :reader hostname)
       (shell :type shell
    	  :initarg :shell)
       (manifest :type manifest
    	     :initarg :manifest
    	     :reader host-manifest)))
    
    (defmethod print-object ((host host) stream)
      (print-unreadable-object (host stream :type t :identity t)
        (write-string (hostname host) stream)))
    
    ;;  Host shell
    
    (defgeneric host-connect (host))
    (defgeneric host-disconnect (host))
    (defgeneric host-shell (host))
    (defgeneric (setf host-shell) (shell host))
    
    (defmethod host-shell ((host host))
      (if (slot-boundp host 'shell)
          (slot-value host 'shell)
          (setf (slot-value host 'shell) (host-connect host))))
    
    (defmethod (setf host-shell) ((shell shell) (host host))
      (setf (slot-value host 'shell) shell))
    
    (defmethod host-disconnect ((host host))
      (when (slot-boundp host 'shell)
        (shell-close (host-shell host))
        (slot-makunbound host 'shell)))
    
    (defmacro with-connected-host ((var hostname) &body body)
      (let ((g!host (gensym "HOST-")))
        `(let ((,g!host (make-instance 'ssh-host :hostname ,hostname)))
           (unwind-protect (let ((,var ,g!host)) ,@body)
    	 (host-disconnect ,g!host)))))
    
    (defun host-run (host command &rest format-args)
      (let ((shell (host-shell host)))
        (when (shell-closed-p shell)
          (setq shell (host-connect host)))
        (apply #'shell-run shell command format-args)))
    
    ;;  Host manifest
    
    (defmethod slot-unbound (class (host host) (slot-name (eql 'manifest)))
      (setf (slot-value host 'manifest) (manifest (hostname host))))
    
    ;;  localhost
    
    (defvar *localhost* (load-time-value
    		     (make-instance 'host
    				    :hostname "localhost")))
    
    (defmethod host-connect ((host (eql *localhost*)))
      (assert (null (host-shell host)))
      (setf (host-shell host) (make-shell)))
    
    ;;  SSH host
    
    (defclass ssh-host (host) ())
    
    (defmethod host-connect ((host ssh-host))
      (assert (null (host-shell host)))
      (setf (host-shell host) (make-shell "/usr/bin/ssh" (hostname host))))
    
    ;;  High level API
    
    (defvar *host* *localhost*)
    
    (defmacro with-host (hostname &body body)
      `(with-connected-host (*host* ,hostname)
         (let ((*manifest* (host-manifest *host*)))
           ,@body)))
    
    (defun run (command &rest format-args)
      (apply #'host-run *host* command format-args))