Skip to content

Instantly share code, notes, and snippets.

@nealey
Last active September 30, 2016 20:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nealey/e7b3eb06fc1f7a31d76f142ea5a1e6c8 to your computer and use it in GitHub Desktop.
Save nealey/e7b3eb06fc1f7a31d76f142ea5a1e6c8 to your computer and use it in GitHub Desktop.
;;
;; ssh
;;
(require 'comint)
(require 'shell)
(defgroup ssh nil
"Secure shell interface"
:group 'processes
:group 'unix)
(defcustom ssh-program (if (eq system-type 'windows-nt) "plink" "ssh")
"*Name of program to invoke SSH"
:type 'string
:group 'ssh)
(defcustom ssh-explicit-args '()
"*List of arguments to pass to ssh on the command line."
:type '(repeat (string :tag "Argument"))
:group 'ssh)
(defcustom ssh-default-host nil
"*Default hostname (the one you go to the most frequently)"
:type 'string
:group 'ssh)
(defcustom ssh-frequent-hosts '()
"*Hosts to pre-populate history"
:type '(repeat (string :tag "Host"))
:group 'ssh)
(defvar ssh-mode-map '())
(cond
((null ssh-mode-map)
(setq ssh-mode-map (if (consp shell-mode-map)
(cons 'keymap shell-mode-map)
(copy-keymap shell-mode-map)))
(define-key ssh-mode-map "\C-c\C-c" 'ssh-send-Ctrl-C)
(define-key ssh-mode-map "\C-c\C-d" 'ssh-send-Ctrl-D)
(define-key ssh-mode-map "\C-c\C-z" 'ssh-send-Ctrl-Z)
(define-key ssh-mode-map "\C-c\C-\\" 'ssh-send-Ctrl-backslash)
(define-key ssh-mode-map "\C-d" 'ssh-delchar-or-send-Ctrl-D)
(define-key ssh-mode-map "\C-i" 'ssh-tab-or-complete)))
(defun ssh-known-hosts ()
"Return a list of hosts for completion"
(if (file-readable-p "~/.ssh/known_hosts")
(with-temp-buffer
(insert-file-contents-literally "~/.ssh/known_hosts")
(let ((ssh-hosts-list) '())
(while (not (eobp))
(add-to-list 'ssh-hosts-list (buffer-substring (point) (- (search-forward-regexp "[, ]") 1)))
(forward-line))
ssh-hosts-list))
'()))
(defvar ssh-host-history nil)
(defvar ssh-remote nil)
(defun ssh (remote)
(interactive
(list
(completing-read (format "Remote host (default %s): " ssh-default-host)
(append ssh-frequent-hosts (ssh-known-hosts))
nil nil "" 'ssh-host-history)))
(if (string= remote "")
(setq remote ssh-default-host))
(let* ((buffer-name (generate-new-buffer-name (format "*ssh %s*" remote)))
(buffer (get-buffer-create buffer-name))
(process-connection-type t)
(default-directory "/tmp")
(args (append ssh-explicit-args (list remote))))
(pop-to-buffer buffer)
(with-current-buffer buffer
(comint-exec buffer buffer-name ssh-program nil args)
(ssh-mode)
(setq-local ssh-remote remote)
(setq-local dabbrev-abbrev-char-regexp "\\sw\\|\\s_\\|[-._,]")
(cond
((string-match "\.ilo$" remote)
(setq-local comint-input-sender 'ssh-comint-cr-send))
((string-equal ssh-program "plink")
(setq-local comint-input-sender 'ssh-comint-plink-send))))))
(global-set-key (kbd "C-c s") 'ssh)
(put 'ssh-mode 'mode-class 'special)
(defun ssh-mode ()
"Set major-mode for ssh sessions."
(interactive)
(kill-all-local-variables)
(shell-mode)
(setq major-mode 'ssh-mode)
(setq mode-name "ssh")
(use-local-map ssh-mode-map)
(run-hooks 'ssh-mode-hook))
;;
;; Kill old buffers; not sure this is a fantastic idea
;;
(defun ssh/shell-kill-buffer-sentinel (process event)
(when (memq (process-status process) '(exit signal))
(kill-buffer (process-buffer process))))
(defun ssh/kill-process-buffer-on-exit ()
(set-process-sentinel (get-buffer-process (current-buffer))
#'ssh/shell-kill-buffer-sentinel))
(add-hook 'comint-exec-hook 'ssh/kill-process-buffer-on-exit)
(defun ssh-comint-send-line (proc string newline)
"Send a string terminated by `newline` (if appropriate)"
(let ((send-string
(if comint-input-sender-no-newline
string
(concat string newline))))
(comint-send-string proc send-string))
(if (and comint-input-sender-no-newline
(not (string-equal string "")))
(process-send-eof)))
(defun ssh-comint-cr-send (proc string)
"Send a comint string terminated with carriage return
Some machines (HP iLO) need this, because reasons."
(ssh-comint-send-line proc string "\r"))
(defun ssh-comint-plink-send (proc string)
"Send a string to plink
Plink wants \\n when it asks for a password,
but if you send \\n in a session, that winds up being two \\n on the server,
so you have to send \\r.
This is very strange."
(let ((newline "\r"))
(with-current-buffer (process-buffer proc)
;; Maybe there's a better way to do this :(
(when (< (count-lines (point-min) (point-max)) 8)
(message "Sending \\n instead of \\r since the buffer is small...")
(setq newline "\n")))
(ssh-comint-send-line proc string newline)))
(provide 'ssh)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment