RSYNC: Synchronize select folders/files using rsync
, either over a home network or via ssh
to a remote server.
rsync
is a one-way synchronization utility that is able to delete files on the target, so please be sure to back-up your data on both the source and target locations, and thoroughly test using the --dry-run
option, before doing the real thing.
To read about how to properly configure the .authinfo
file, see https://www.gnu.org/software/emacs/manual/auth.html An example .authinfo
file contents (which may contain multiple different entries) is as follows:
machine mymachine login myloginname password mypassword port myport
Configure and use the function rsync-remote
to synchronize over ssh
to a remote server. Or, use the function rsync-local
to synchronize on the same computer or over a trusted home network.
(require 'auth-source)
;;; EXAMPLE:
;;; (get-auth-info "12.34.567.89" "username")
;;; (get-auth-info "localhost" "root")
(defun get-auth-info (host user &optional port)
(let ((info (nth 0 (auth-source-search
:host host
:user user
:port port
:require '(:user :secret)
:create t))))
(if info
(let* ((port (plist-get info :port))
(secret-maybe (plist-get info :secret))
(secret
(if (functionp secret-maybe)
(funcall secret-maybe)
secret-maybe)))
(list port secret))
nil)))
(defun rsync-filter (proc string)
(cond
((string-match
"^\\([a-zA-Z0-9_\\-\\.]+\\)@\\([a-zA-Z0-9_\\-\\.]+\\)'s password: "
string)
(let* ((user (substring string (match-beginning 1) (match-end 1)))
(host (substring string (match-beginning 2) (match-end 2)))
(password (car (cdr (get-auth-info host user)))))
(process-send-string proc (concat password "\n"))))
((not (or (string-match "files\\.\\.\\.\r" string)
(string-match "files to consider\n" string)))
(with-current-buffer (messages-buffer)
(let ((inhibit-read-only t))
(goto-char (point-max))
(when (not (bolp))
(insert "\n"))
(insert string)
(when (not (bolp))
(insert "\n")))))))
(defun rsync-remote ()
"Use rsync to a remote server via ssh. Back-up your data first!!!"
(interactive)
(let* (
(host "localhost")
(username "root")
(port (or (car (get-auth-info host username))
(number-to-string (read-number "Port: "))))
(source
(let ((dir (expand-file-name (locate-user-emacs-file "elpa/"))))
(if (file-directory-p dir)
dir
(let ((debug-on-quit nil)
(msg (format "`%s` is not a valid directory." dir)))
(signal 'quit `(,msg))))))
(target "/private/var/mobile/elpa/")
(ssh "/usr/bin/ssh")
(rsync "/usr/bin/rsync")
(rsync-include-file "/path/to/include-file.txt")
(rsync-exclude-file "/path/to/exclude-file.txt")
(rsh (concat "--rsh=ssh -p " port " -l " username))
(host+target (concat host ":" target)))
(start-process
"rsync-process"
nil
rsync
"-avr" ;; must specify the `-r` argument when using `--files-from`
"--delete"
;; The paths inside the exclusion file must be relative, NOT absolute.
;;; (concat "--files-from=" rsync-include-file)
;;; (concat "--exclude-from=" rsync-exclude-file)
rsh
source
host+target)
(set-process-filter (get-process "rsync-process") 'rsync-filter)
(set-process-sentinel
(get-process "rsync-process")
(lambda (p e) (when (= 0 (process-exit-status p))
(message "rsync-remote: synchronizing ... done."))))))
(defun rsync-local ()
"Use rsync locally -- e.g., over a trusted home network.
Back-up your data first!!!"
(interactive)
(let (
(rsync-program "/usr/bin/rsync")
(source
(let ((dir (expand-file-name
(file-name-as-directory
(read-directory-name "Source Directory: " nil nil nil nil)))))
(if (file-directory-p dir)
dir
(let ((debug-on-quit nil)
(msg (format "`%s` is not a valid directory." dir)))
(signal 'quit `(,msg))))))
(target (expand-file-name
(file-name-as-directory
(read-directory-name "Target Directory: " nil nil nil nil)))))
(unless (y-or-n-p (format "SOURCE: %s | TARGET: %s" source target))
(let ((debug-on-quit nil))
(signal 'quit `("You have exited the function."))))
(start-process "rsync-process"
nil
rsync-program
"--delete"
"-arzhv"
source
target)
(set-process-filter (get-process "rsync-process") #'rsync-process-filter)
(set-process-sentinel
(get-process "rsync-process")
(lambda (p e)
(when (= 0 (process-exit-status p))
(message "Done!"))))))