telnetlib
telnetlib is a networking library for doing Telnet. It is ported from Python's telnetlib library and quite useful for writing automatical scripts.

;; -*- MODE: LISP -*-
;;
;; Brian Jiang (brianjcj AT gmail.com)
;; 2007-11
;;
;; TelnetLib
;; =========
;; In our ordinary work, we often write some automatically scripts
;; which login in the server to run some commands and performce some
;; actions based on the result of the commands. In this case, a telnet
;; client library will be very helpful. Such a library is very handy
;; in Perl, Python and Ruby. But I googled it a lot and failed to find
;; one in Common Lisp. So I decided to port the Telnetlib from Python
;; to Common Lisp. Why port from Python? Because I am more familiar
;; with Python and have use its TelnetLib before :-) 
;;
;; The functionality of this library is almost the same as Python's
;; one.  But the interface is a little different.


;; Supported Lisp implementations
;; ==============================
;; - SBCL
;; - LispWorks
;; - Allegro CL
;; - CLISP
;;
;; TelnetLib also supports other Lisp implementations (require USOCKET
;; and FLEXI-STREAMS) . But I have never done any testing for them.

;; Testing:
;; --------
;; TelnetLib has been tested in following environment:
;;   - sbcl-1.0.11-x86-linux
;;   - LispWorks in Windows XP
;;   - Allegro CL in Windows XP
;;   - CLISP 2.43 in Linux
;;   - CLISP 2.41 in Windows XP
;;
;; But it failed in sbcl-1.0.9 in Windows XP. Error occurs
;; when writing the socket stream.


;; Library Dependency
;; ==================
;; For SBCL, LispWorks, Allegro CL and CLISP, it only depends on
;; CL-PPCRE.
;; For other Lisp implementations, USOCKET and FLEXI-STREAMS (to
;; set the external-format) are also required.

;; Known Problem
;; =============
;; Don't work well when telent localhost. It seems it is due to the process/thread
;; schedule mechanism. Will try to fix it in the future.

;; How to use it
;; =============

;; Export functions/macros:
;; ------------------------
(defun open-telnet-session (host &optional port)...)
(defun close-telnet-session (tn)....)
(defmacro with-telnet-session ((tn host &optional port) &body body)....)
(defun set-telnet-session-option (tn
                                  &key (remove-return-char nil r-r-c-p)
                                  (debug-on nil debug-on-p)
                                  (char-callback nil char-callback-p)
                                  (option-callback nil option-callback-p)
                                  (sb-option-callback nil sb-option-callback-p))...)
(defun peek-available-data (tn &optional block-read)....)
(defun read-available-data (tn &optional block-read)....
(defun read-until (tn str &key (timeout 600) case-insensitive-mode)....)
(defun read-until-2 (tn strings &key (timeout 600) case-insensitive-mode)....)
(defun read-until-2-ind (tn strings &key (timeout 600) case-insensitive-mode)....)
(defun expect (tn regexp &optional (timeout 600))....)
(defun format-tn (tn control-string &rest format-arguments)....)
(defun write-ln (tn str)....)
(defun write-ln-crlr (tn str)....)


;; Example:
;; --------
(defun example-1 ()
  (with-telnet-session (tn "202.38.33.94")

    (set-telnet-session-option tn :remove-return-char t)
    
    (read-until tn "ogin:")
    (write-ln tn "brianjcj")

    (read-until tn "assword:")
    (write-ln tn "abcdefg12")
    (read-until tn ">")

    (format-tn tn "~A~%" "pwd") ;; stupid demo :-)
    (read-until tn ">")

    (write-ln tn "cmd1")
    (read-until-2 tn (list "Done." "Error" "Pending.")
                  :case-insensitive-mode t)
    (read-until tn ">")

    
    (write-ln tn "cmd2")
    (expect tn
            (cl-ppcre:create-scanner
             "OK\|NO"
             :case-insensitive-mode t))
    (read-until tn ">")

    
    (write-ln tn "cmd3")
    (expect tn "Done\|Try later.")
    (read-until tn ">")

    (write-ln tn-vmap "unload testci")
    (case (read-until-2-ind tn (list "Please confirm (" "has not been loaded yet"))
      ((0)
       (read-until tn *prompt*)
       (sleep *rest-time*)
       (write-ln tn "Y")
       (read-until tn *prompt*))
      ((1)
       (read-until tn-vmap *prompt*)))
    
    (write-ln tn "exit")
    
    (loop until (eof tn) do
         (read-available-data tn t))

    ))

(defun example-2 ()

  (with-telnet-session (tn "202.38.33.94")

    (set-telnet-session-option tn :char-callback nil)

    
    (princ (read-until tn "ogin:"))
    (write-ln tn "brianjcj")

    (princ (read-until tn "PassWord:"
                       :case-insensitive-mode t))
    (write-ln tn "zaq12WSX")
    (princ (read-until tn ">"))

    
    (write-ln tn "ls")
    (princ (read-until tn ">"))

    (write-ln tn "cmd1")
    (princ (expect tn "Done\|Error"))
    (princ (read-until tn ">"))

    (write-ln tn "cmd2")
    (princ (read-until-2
            tn
            (list "OK." "Try again" "Later.")
            :timeout 10))
    (princ (read-until tn ">"))


    (write-ln tn "cmd3")
    (princ (read-until-2
            tn (list "Right" "Wrong")
            :timeout 10 :case-insensitive-mode t))
    (princ (read-until tn ">"))

    (write-ln tn "cmd4")
    (princ (expect
            tn
            (cl-ppcre:create-scanner
             "Right\|Wrong"
             :case-insensitive-mode t)))
    (princ (read-until tn ">"))
    
    (write-ln tn "exit")
    
    (loop until (eof tn) do
         (princ (read-available-data tn t)))

    ))

License: LGPL