diff options
| author | Juanma Barranquero | 2006-10-30 23:24:18 +0000 |
|---|---|---|
| committer | Juanma Barranquero | 2006-10-30 23:24:18 +0000 |
| commit | 337e3c70f820aae9e34fccef009cfaaec7bd3cb2 (patch) | |
| tree | 506f3c0423d3702cffc06f56ea713211e5ad9e27 | |
| parent | 41c98a5ec03967dfb58dd7a3864bbf387745cd29 (diff) | |
| download | emacs-337e3c70f820aae9e34fccef009cfaaec7bd3cb2.tar.gz emacs-337e3c70f820aae9e34fccef009cfaaec7bd3cb2.zip | |
Add support for TCP sockets.
(server-use-tcp, server-host, server-auth-dir): New options.
(server-auth-key): New variable.
(server-ensure-safe-dir): Create nonexistent parent dirs. Ignore Unix-style
file modes on Windows.
(server-start): Crete a TCP or Unix socket according to the value of
`server-use-tcp'. For TCP sockets, create the id/auth file in `server-auth-dir'
directory.
(server-process-filter): Delete process if authentication fails (which never
happens for Unix sockets).
| -rw-r--r-- | lisp/ChangeLog | 13 | ||||
| -rw-r--r-- | lisp/server.el | 209 |
2 files changed, 154 insertions, 68 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index c1902d0a1a4..38a904eee9c 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,16 @@ | |||
| 1 | 2006-10-31 Juanma Barranquero <lekktu@gmail.com> | ||
| 2 | |||
| 3 | * server.el: Add support for TCP sockets. | ||
| 4 | (server-use-tcp, server-host, server-auth-dir): New options. | ||
| 5 | (server-auth-key): New variable. | ||
| 6 | (server-ensure-safe-dir): Create nonexistent parent dirs. Ignore | ||
| 7 | Unix-style file modes on Windows. | ||
| 8 | (server-start): Crete a TCP or Unix socket according to the value | ||
| 9 | of `server-use-tcp'. For TCP sockets, create the id/auth file in | ||
| 10 | `server-auth-dir' directory. | ||
| 11 | (server-process-filter): Delete process if authentication | ||
| 12 | fails (which never happens for Unix sockets). | ||
| 13 | |||
| 1 | 2006-10-30 David Kastrup <dak@gnu.org> | 14 | 2006-10-30 David Kastrup <dak@gnu.org> |
| 2 | 15 | ||
| 3 | * subr.el (add-to-list): Don't continue checking if a match has | 16 | * subr.el (add-to-list): Don't continue checking if a match has |
diff --git a/lisp/server.el b/lisp/server.el index 4b770b2caeb..c5abf38c2b9 100644 --- a/lisp/server.el +++ b/lisp/server.el | |||
| @@ -82,6 +82,40 @@ | |||
| 82 | "Emacs running as a server process." | 82 | "Emacs running as a server process." |
| 83 | :group 'external) | 83 | :group 'external) |
| 84 | 84 | ||
| 85 | (defcustom server-use-tcp nil | ||
| 86 | "If non-nil, use TCP sockets instead of local sockets." | ||
| 87 | :set #'(lambda (sym val) | ||
| 88 | (unless (featurep 'make-network-process '(:family local)) | ||
| 89 | (setq val t) | ||
| 90 | (unless load-in-progress | ||
| 91 | (message "Local sockets unsupported, using TCP sockets"))) | ||
| 92 | (when val (random t)) | ||
| 93 | (set-default sym val)) | ||
| 94 | :group 'server | ||
| 95 | :type 'boolean | ||
| 96 | :version "22.1") | ||
| 97 | |||
| 98 | (defcustom server-host nil | ||
| 99 | "The name or IP address to use as host address of the server process. | ||
| 100 | If set, the server accepts remote connections; otherwise it is local." | ||
| 101 | :group 'server | ||
| 102 | :type '(choice | ||
| 103 | (string :tag "Name or IP address") | ||
| 104 | (const :tag "Local" nil)) | ||
| 105 | :version "22.1") | ||
| 106 | (put 'server-host 'risky-local-variable t) | ||
| 107 | |||
| 108 | (defcustom server-auth-dir "~/.emacs.d/server/" | ||
| 109 | "Directory for server authentication files." | ||
| 110 | :group 'server | ||
| 111 | :type 'directory | ||
| 112 | :version "22.1") | ||
| 113 | (put 'server-auth-dir 'risky-local-variable t) | ||
| 114 | |||
| 115 | (defvar server-auth-key nil | ||
| 116 | "The current server authentication key.") | ||
| 117 | (put 'server-auth-key 'risky-local-variable t) | ||
| 118 | |||
| 85 | (defcustom server-visit-hook nil | 119 | (defcustom server-visit-hook nil |
| 86 | "*Hook run when visiting a file for the Emacs server." | 120 | "*Hook run when visiting a file for the Emacs server." |
| 87 | :group 'server | 121 | :group 'server |
| @@ -166,13 +200,13 @@ are done with it in the server.") | |||
| 166 | 200 | ||
| 167 | (defun server-log (string &optional client) | 201 | (defun server-log (string &optional client) |
| 168 | "If a *server* buffer exists, write STRING to it for logging purposes." | 202 | "If a *server* buffer exists, write STRING to it for logging purposes." |
| 169 | (if (get-buffer "*server*") | 203 | (when (get-buffer "*server*") |
| 170 | (with-current-buffer "*server*" | 204 | (with-current-buffer "*server*" |
| 171 | (goto-char (point-max)) | 205 | (goto-char (point-max)) |
| 172 | (insert (current-time-string) | 206 | (insert (current-time-string) |
| 173 | (if client (format " %s:" client) " ") | 207 | (if client (format " %s:" client) " ") |
| 174 | string) | 208 | string) |
| 175 | (or (bolp) (newline))))) | 209 | (or (bolp) (newline))))) |
| 176 | 210 | ||
| 177 | (defun server-sentinel (proc msg) | 211 | (defun server-sentinel (proc msg) |
| 178 | (let ((client (assq proc server-clients))) | 212 | (let ((client (assq proc server-clients))) |
| @@ -253,11 +287,12 @@ Creates the directory if necessary and makes sure: | |||
| 253 | (setq dir (directory-file-name dir)) | 287 | (setq dir (directory-file-name dir)) |
| 254 | (let ((attrs (file-attributes dir))) | 288 | (let ((attrs (file-attributes dir))) |
| 255 | (unless attrs | 289 | (unless attrs |
| 256 | (letf (((default-file-modes) ?\700)) (make-directory dir)) | 290 | (letf (((default-file-modes) ?\700)) (make-directory dir t)) |
| 257 | (setq attrs (file-attributes dir))) | 291 | (setq attrs (file-attributes dir))) |
| 258 | ;; Check that it's safe for use. | 292 | ;; Check that it's safe for use. |
| 259 | (unless (and (eq t (car attrs)) (eq (nth 2 attrs) (user-uid)) | 293 | (unless (and (eq t (car attrs)) (eq (nth 2 attrs) (user-uid)) |
| 260 | (zerop (logand ?\077 (file-modes dir)))) | 294 | (or (eq system-type 'windows-nt) |
| 295 | (zerop (logand ?\077 (file-modes dir))))) | ||
| 261 | (error "The directory %s is unsafe" dir)))) | 296 | (error "The directory %s is unsafe" dir)))) |
| 262 | 297 | ||
| 263 | ;;;###autoload | 298 | ;;;###autoload |
| @@ -270,13 +305,15 @@ Emacs distribution as your standard \"editor\". | |||
| 270 | 305 | ||
| 271 | Prefix arg means just kill any existing server communications subprocess." | 306 | Prefix arg means just kill any existing server communications subprocess." |
| 272 | (interactive "P") | 307 | (interactive "P") |
| 308 | (when server-process | ||
| 273 | ;; kill it dead! | 309 | ;; kill it dead! |
| 274 | (if server-process | 310 | (ignore-errors (delete-process server-process)) |
| 275 | (condition-case () (delete-process server-process) (error nil))) | 311 | (ignore-errors |
| 276 | ;; Delete the socket files made by previous server invocations. | 312 | ;; Delete the socket or authentication files made by previous server invocations. |
| 277 | (condition-case () | 313 | (if (eq (process-contact server-process :family) 'local) |
| 278 | (delete-file (expand-file-name server-name server-socket-dir)) | 314 | (delete-file (expand-file-name server-name server-socket-dir)) |
| 279 | (error nil)) | 315 | (setq server-auth-key nil) |
| 316 | (delete-file (expand-file-name server-name server-auth-dir))))) | ||
| 280 | ;; If this Emacs already had a server, clear out associated status. | 317 | ;; If this Emacs already had a server, clear out associated status. |
| 281 | (while server-clients | 318 | (while server-clients |
| 282 | (let ((buffer (nth 1 (car server-clients)))) | 319 | (let ((buffer (nth 1 (car server-clients)))) |
| @@ -284,19 +321,43 @@ Prefix arg means just kill any existing server communications subprocess." | |||
| 284 | ;; Now any previous server is properly stopped. | 321 | ;; Now any previous server is properly stopped. |
| 285 | (unless leave-dead | 322 | (unless leave-dead |
| 286 | ;; Make sure there is a safe directory in which to place the socket. | 323 | ;; Make sure there is a safe directory in which to place the socket. |
| 287 | (server-ensure-safe-dir server-socket-dir) | 324 | (server-ensure-safe-dir (if server-use-tcp server-auth-dir server-socket-dir)) |
| 288 | (if server-process | 325 | (when server-process |
| 289 | (server-log (message "Restarting server"))) | 326 | (server-log (message "Restarting server"))) |
| 290 | (letf (((default-file-modes) ?\700)) | 327 | (letf (((default-file-modes) ?\700)) |
| 291 | (setq server-process | 328 | (setq server-process |
| 292 | (make-network-process | 329 | (apply #'make-network-process |
| 293 | :name "server" :family 'local :server t :noquery t | 330 | :name server-name |
| 294 | :service (expand-file-name server-name server-socket-dir) | 331 | :server t |
| 295 | :sentinel 'server-sentinel :filter 'server-process-filter | 332 | :noquery t |
| 333 | :sentinel 'server-sentinel | ||
| 334 | :filter 'server-process-filter | ||
| 296 | ;; We must receive file names without being decoded. | 335 | ;; We must receive file names without being decoded. |
| 297 | ;; Those are decoded by server-process-filter according | 336 | ;; Those are decoded by server-process-filter according |
| 298 | ;; to file-name-coding-system. | 337 | ;; to file-name-coding-system. |
| 299 | :coding 'raw-text))))) | 338 | :coding 'raw-text |
| 339 | ;; The rest of the arguments depend on the kind of socket used | ||
| 340 | (if server-use-tcp | ||
| 341 | (list :family nil | ||
| 342 | :service t | ||
| 343 | :host (or server-host 'local) | ||
| 344 | :plist '(:authenticated nil)) | ||
| 345 | (list :family 'local | ||
| 346 | :service (expand-file-name server-name server-socket-dir) | ||
| 347 | :plist '(:authenticated t)))))) | ||
| 348 | (unless server-process (error "Could not start server process")) | ||
| 349 | (when server-use-tcp | ||
| 350 | (setq server-auth-key | ||
| 351 | (loop | ||
| 352 | ;; The auth key is a 64-byte string of random chars in the range `!'..`~'. | ||
| 353 | for i below 64 | ||
| 354 | collect (+ 33 (random 94)) into auth | ||
| 355 | finally return (concat auth))) | ||
| 356 | (with-temp-file (expand-file-name server-name server-auth-dir) | ||
| 357 | (set-buffer-multibyte nil) | ||
| 358 | (setq buffer-file-coding-system 'no-conversion) | ||
| 359 | (insert (format-network-address (process-contact server-process :local)) | ||
| 360 | "\n" server-auth-key))))) | ||
| 300 | 361 | ||
| 301 | ;;;###autoload | 362 | ;;;###autoload |
| 302 | (define-minor-mode server-mode | 363 | (define-minor-mode server-mode |
| @@ -311,14 +372,26 @@ Server mode runs a process that accepts commands from the | |||
| 311 | ;; nothing if there is one (for multiple Emacs sessions)? | 372 | ;; nothing if there is one (for multiple Emacs sessions)? |
| 312 | (server-start (not server-mode))) | 373 | (server-start (not server-mode))) |
| 313 | 374 | ||
| 314 | (defun server-process-filter (proc string) | 375 | (defun* server-process-filter (proc string) |
| 315 | "Process a request from the server to edit some files. | 376 | "Process a request from the server to edit some files. |
| 316 | PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"." | 377 | PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"." |
| 378 | ;; First things first: let's check the authentication | ||
| 379 | (unless (process-get proc :authenticated) | ||
| 380 | (if (and (string-match "-auth \\(.*?\\)\n" string) | ||
| 381 | (string= (match-string 1 string) server-auth-key)) | ||
| 382 | (progn | ||
| 383 | (setq string (substring string (match-end 0))) | ||
| 384 | (process-put proc :authenticated t) | ||
| 385 | (server-log "Authentication successful" proc)) | ||
| 386 | (server-log "Authentication failed" proc) | ||
| 387 | (delete-process proc) | ||
| 388 | ;; We return immediately | ||
| 389 | (return-from server-process-filter))) | ||
| 317 | (server-log string proc) | 390 | (server-log string proc) |
| 318 | (let ((prev (process-get proc 'previous-string))) | 391 | (let ((prev (process-get proc :previous-string))) |
| 319 | (when prev | 392 | (when prev |
| 320 | (setq string (concat prev string)) | 393 | (setq string (concat prev string)) |
| 321 | (process-put proc 'previous-string nil))) | 394 | (process-put proc :previous-string nil))) |
| 322 | ;; If the input is multiple lines, | 395 | ;; If the input is multiple lines, |
| 323 | ;; process each line individually. | 396 | ;; process each line individually. |
| 324 | (while (string-match "\n" string) | 397 | (while (string-match "\n" string) |
| @@ -329,7 +402,7 @@ PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"." | |||
| 329 | client nowait eval | 402 | client nowait eval |
| 330 | (files nil) | 403 | (files nil) |
| 331 | (lineno 1) | 404 | (lineno 1) |
| 332 | (tmp-frame nil) ; Sometimes used to embody the selected display. | 405 | (tmp-frame nil) ;; Sometimes used to embody the selected display. |
| 333 | (columnno 0)) | 406 | (columnno 0)) |
| 334 | ;; Remove this line from STRING. | 407 | ;; Remove this line from STRING. |
| 335 | (setq string (substring string (match-end 0))) | 408 | (setq string (substring string (match-end 0))) |
| @@ -359,8 +432,8 @@ PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"." | |||
| 359 | ;; for certain special characters. | 432 | ;; for certain special characters. |
| 360 | (setq arg (server-unquote-arg arg)) | 433 | (setq arg (server-unquote-arg arg)) |
| 361 | ;; Now decode the file name if necessary. | 434 | ;; Now decode the file name if necessary. |
| 362 | (if coding-system | 435 | (when coding-system |
| 363 | (setq arg (decode-coding-string arg coding-system))) | 436 | (setq arg (decode-coding-string arg coding-system))) |
| 364 | (if eval | 437 | (if eval |
| 365 | (let* (errorp | 438 | (let* (errorp |
| 366 | (v (condition-case errobj | 439 | (v (condition-case errobj |
| @@ -407,13 +480,13 @@ PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"." | |||
| 407 | (server-unselect-display tmp-frame)))) | 480 | (server-unselect-display tmp-frame)))) |
| 408 | ;; Save for later any partial line that remains. | 481 | ;; Save for later any partial line that remains. |
| 409 | (when (> (length string) 0) | 482 | (when (> (length string) 0) |
| 410 | (process-put proc 'previous-string string))) | 483 | (process-put proc :previous-string string))) |
| 411 | 484 | ||
| 412 | (defun server-goto-line-column (file-line-col) | 485 | (defun server-goto-line-column (file-line-col) |
| 413 | (goto-line (nth 1 file-line-col)) | 486 | (goto-line (nth 1 file-line-col)) |
| 414 | (let ((column-number (nth 2 file-line-col))) | 487 | (let ((column-number (nth 2 file-line-col))) |
| 415 | (if (> column-number 0) | 488 | (when (> column-number 0) |
| 416 | (move-to-column (1- column-number))))) | 489 | (move-to-column (1- column-number))))) |
| 417 | 490 | ||
| 418 | (defun server-visit-files (files client &optional nowait) | 491 | (defun server-visit-files (files client &optional nowait) |
| 419 | "Find FILES and return the list CLIENT with the buffers nconc'd. | 492 | "Find FILES and return the list CLIENT with the buffers nconc'd. |
| @@ -485,33 +558,33 @@ FOR-KILLING if non-nil indicates that we are called from `kill-buffer'." | |||
| 485 | (server-log "Close" (car client)) | 558 | (server-log "Close" (car client)) |
| 486 | (setq server-clients (delq client server-clients)))) | 559 | (setq server-clients (delq client server-clients)))) |
| 487 | (setq old-clients (cdr old-clients))) | 560 | (setq old-clients (cdr old-clients))) |
| 488 | (if (and (bufferp buffer) (buffer-name buffer)) | 561 | (when (and (bufferp buffer) (buffer-name buffer)) |
| 489 | ;; We may or may not kill this buffer; | 562 | ;; We may or may not kill this buffer; |
| 490 | ;; if we do, do not call server-buffer-done recursively | 563 | ;; if we do, do not call server-buffer-done recursively |
| 491 | ;; from kill-buffer-hook. | 564 | ;; from kill-buffer-hook. |
| 492 | (let ((server-kill-buffer-running t)) | 565 | (let ((server-kill-buffer-running t)) |
| 493 | (with-current-buffer buffer | 566 | (with-current-buffer buffer |
| 494 | (setq server-buffer-clients nil) | 567 | (setq server-buffer-clients nil) |
| 495 | (run-hooks 'server-done-hook)) | 568 | (run-hooks 'server-done-hook)) |
| 496 | ;; Notice whether server-done-hook killed the buffer. | 569 | ;; Notice whether server-done-hook killed the buffer. |
| 497 | (if (null (buffer-name buffer)) | 570 | (if (null (buffer-name buffer)) |
| 571 | (setq killed t) | ||
| 572 | ;; Don't bother killing or burying the buffer | ||
| 573 | ;; when we are called from kill-buffer. | ||
| 574 | (unless for-killing | ||
| 575 | (when (and (not killed) | ||
| 576 | server-kill-new-buffers | ||
| 577 | (with-current-buffer buffer | ||
| 578 | (not server-existing-buffer))) | ||
| 498 | (setq killed t) | 579 | (setq killed t) |
| 499 | ;; Don't bother killing or burying the buffer | 580 | (bury-buffer buffer) |
| 500 | ;; when we are called from kill-buffer. | 581 | (kill-buffer buffer)) |
| 501 | (unless for-killing | 582 | (unless killed |
| 502 | (when (and (not killed) | 583 | (if (server-temp-file-p buffer) |
| 503 | server-kill-new-buffers | 584 | (progn |
| 504 | (with-current-buffer buffer | 585 | (kill-buffer buffer) |
| 505 | (not server-existing-buffer))) | 586 | (setq killed t)) |
| 506 | (setq killed t) | 587 | (bury-buffer buffer))))))) |
| 507 | (bury-buffer buffer) | ||
| 508 | (kill-buffer buffer)) | ||
| 509 | (unless killed | ||
| 510 | (if (server-temp-file-p buffer) | ||
| 511 | (progn | ||
| 512 | (kill-buffer buffer) | ||
| 513 | (setq killed t)) | ||
| 514 | (bury-buffer buffer))))))) | ||
| 515 | (list next-buffer killed))) | 588 | (list next-buffer killed))) |
| 516 | 589 | ||
| 517 | (defun server-temp-file-p (&optional buffer) | 590 | (defun server-temp-file-p (&optional buffer) |
| @@ -538,10 +611,10 @@ specifically for the clients and did not exist before their request for it." | |||
| 538 | (let ((version-control nil) | 611 | (let ((version-control nil) |
| 539 | (buffer-backed-up nil)) | 612 | (buffer-backed-up nil)) |
| 540 | (save-buffer)) | 613 | (save-buffer)) |
| 541 | (if (and (buffer-modified-p) | 614 | (when (and (buffer-modified-p) |
| 542 | buffer-file-name | 615 | buffer-file-name |
| 543 | (y-or-n-p (concat "Save file " buffer-file-name "? "))) | 616 | (y-or-n-p (concat "Save file " buffer-file-name "? "))) |
| 544 | (save-buffer))) | 617 | (save-buffer))) |
| 545 | (server-buffer-done (current-buffer)))) | 618 | (server-buffer-done (current-buffer)))) |
| 546 | 619 | ||
| 547 | ;; Ask before killing a server buffer. | 620 | ;; Ask before killing a server buffer. |
| @@ -561,8 +634,8 @@ specifically for the clients and did not exist before their request for it." | |||
| 561 | (tail server-clients)) | 634 | (tail server-clients)) |
| 562 | ;; See if any clients have any buffers that are still alive. | 635 | ;; See if any clients have any buffers that are still alive. |
| 563 | (while tail | 636 | (while tail |
| 564 | (if (memq t (mapcar 'stringp (mapcar 'buffer-name (cdr (car tail))))) | 637 | (when (memq t (mapcar 'stringp (mapcar 'buffer-name (cdr (car tail))))) |
| 565 | (setq live-client t)) | 638 | (setq live-client t)) |
| 566 | (setq tail (cdr tail))) | 639 | (setq tail (cdr tail))) |
| 567 | (or (not live-client) | 640 | (or (not live-client) |
| 568 | (yes-or-no-p "Server buffers still have clients; exit anyway? ")))) | 641 | (yes-or-no-p "Server buffers still have clients; exit anyway? ")))) |
| @@ -628,8 +701,8 @@ Arg NEXT-BUFFER is a suggestion; if it is a live buffer, use it." | |||
| 628 | (if (and win (not server-window)) | 701 | (if (and win (not server-window)) |
| 629 | ;; The buffer is already displayed: just reuse the window. | 702 | ;; The buffer is already displayed: just reuse the window. |
| 630 | (let ((frame (window-frame win))) | 703 | (let ((frame (window-frame win))) |
| 631 | (if (eq (frame-visible-p frame) 'icon) | 704 | (when (eq (frame-visible-p frame) 'icon) |
| 632 | (raise-frame frame)) | 705 | (raise-frame frame)) |
| 633 | (select-window win) | 706 | (select-window win) |
| 634 | (set-buffer next-buffer)) | 707 | (set-buffer next-buffer)) |
| 635 | ;; Otherwise, let's find an appropriate window. | 708 | ;; Otherwise, let's find an appropriate window. |
| @@ -637,11 +710,11 @@ Arg NEXT-BUFFER is a suggestion; if it is a live buffer, use it." | |||
| 637 | (window-live-p server-window)) | 710 | (window-live-p server-window)) |
| 638 | (select-window server-window)) | 711 | (select-window server-window)) |
| 639 | ((framep server-window) | 712 | ((framep server-window) |
| 640 | (if (not (frame-live-p server-window)) | 713 | (unless (frame-live-p server-window) |
| 641 | (setq server-window (make-frame))) | 714 | (setq server-window (make-frame))) |
| 642 | (select-window (frame-selected-window server-window)))) | 715 | (select-window (frame-selected-window server-window)))) |
| 643 | (if (window-minibuffer-p (selected-window)) | 716 | (when (window-minibuffer-p (selected-window)) |
| 644 | (select-window (next-window nil 'nomini 0))) | 717 | (select-window (next-window nil 'nomini 0))) |
| 645 | ;; Move to a non-dedicated window, if we have one. | 718 | ;; Move to a non-dedicated window, if we have one. |
| 646 | (when (window-dedicated-p (selected-window)) | 719 | (when (window-dedicated-p (selected-window)) |
| 647 | (select-window | 720 | (select-window |