summaryrefslogtreecommitdiff
path: root/lisp/server.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/server.el')
-rw-r--r--lisp/server.el264
1 files changed, 143 insertions, 121 deletions
diff --git a/lisp/server.el b/lisp/server.el
index 42329e853ba..d491a260377 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -96,7 +96,6 @@
(unless load-in-progress
(message "Local sockets unsupported, using TCP sockets")))
(set-default sym val))
- :group 'server
:type 'boolean
:version "22.1")
@@ -108,7 +107,6 @@ DO NOT give this a non-nil value unless you know what you are doing!
On unsecured networks, accepting remote connections is very dangerous,
because server-client communication (including session authentication)
is not encrypted."
- :group 'server
:type '(choice
(string :tag "Name or IP address")
(const :tag "Local" nil))
@@ -121,7 +119,6 @@ is not encrypted."
This variable only takes effect when the Emacs server is using
TCP instead of local sockets. A nil value means to use a random
port number."
- :group 'server
:type '(choice
(string :tag "Port number")
(const :tag "Random" nil))
@@ -138,7 +135,6 @@ NOTE: On FAT32 filesystems, directories are not secure;
files can be read and modified by any user or process.
It is strongly suggested to set `server-auth-dir' to a
directory residing in a NTFS partition instead."
- :group 'server
:type 'directory
:version "22.1")
;;;###autoload
@@ -166,7 +162,6 @@ communications are unencrypted, still apply.
The key must consist of 64 ASCII printable characters except for
space (this means characters from ! to ~; or from code 33 to
126). You can use \\[server-generate-key] to get a random key."
- :group 'server
:type '(choice
(const :tag "Random" nil)
(string :tag "Password"))
@@ -174,23 +169,25 @@ space (this means characters from ! to ~; or from code 33 to
(defcustom server-raise-frame t
"If non-nil, raise frame when switching to a buffer."
- :group 'server
:type 'boolean
:version "22.1")
(defcustom server-visit-hook nil
"Hook run when visiting a file for the Emacs server."
- :group 'server
:type 'hook)
(defcustom server-switch-hook nil
"Hook run when switching to a buffer for the Emacs server."
- :group 'server
:type 'hook)
+(defcustom server-after-make-frame-hook nil
+ "Hook run when the Emacs server creates a client frame.
+The created frame is selected when the hook is called."
+ :type 'hook
+ :version "27.1")
+
(defcustom server-done-hook nil
"Hook run when done editing a buffer for the Emacs server."
- :group 'server
:type 'hook)
(defvar server-process nil
@@ -216,7 +213,6 @@ If it is a frame, use the frame's selected window.
It is not meaningful to set this to a specific frame or window with Custom.
Only programs can do so."
- :group 'server
:version "22.1"
:type '(choice (const :tag "Use selected window"
:match (lambda (widget value)
@@ -226,11 +222,10 @@ Only programs can do so."
(function-item :tag "Use pop-to-buffer" pop-to-buffer)
(function :tag "Other function")))
-(defcustom server-temp-file-regexp "^/tmp/Re\\|/draft$"
+(defcustom server-temp-file-regexp "\\`/tmp/Re\\|/draft\\'"
"Regexp matching names of temporary files.
These are deleted and reused after each edit by the programs that
invoke the Emacs server."
- :group 'server
:type 'regexp)
(defcustom server-kill-new-buffers t
@@ -241,7 +236,6 @@ it with the Emacs server. If nil, kill only buffers as specified by
Please note that only buffers that still have a client are killed,
i.e. buffers visited with \"emacsclient --no-wait\" are never killed
in this way."
- :group 'server
:type 'boolean
:version "21.1")
@@ -251,8 +245,16 @@ This means that the server should not kill the buffer when you say you
are done with it in the server.")
(make-variable-buffer-local 'server-existing-buffer)
-;;;###autoload
-(defcustom server-name "server"
+(defvar server--external-socket-initialized nil
+ "When an external socket is passed into Emacs, we need to call
+`server-start' in order to initialize the connection. This flag
+prevents multiple initializations when an external socket has
+been consumed.")
+
+(defcustom server-name
+ (if internal--daemon-sockname
+ (file-name-nondirectory internal--daemon-sockname)
+ "server")
"The name of the Emacs server, if this Emacs process creates one.
The command `server-start' makes use of this. It should not be
changed while a server is running.
@@ -263,15 +265,19 @@ If this is an absolute file name, it specifies where the socket
file will be created. To have emacsclient connect to the same
socket, use the \"-s\" switch for local non-TCP sockets, and
the \"-f\" switch otherwise."
- :group 'server
:type 'string
:version "23.1")
;; We do not use `temporary-file-directory' here, because emacsclient
;; does not read the init file.
(defvar server-socket-dir
- (and (featurep 'make-network-process '(:family local))
- (format "%s/emacs%d" (or (getenv "TMPDIR") "/tmp") (user-uid)))
+ (if internal--daemon-sockname
+ (file-name-directory internal--daemon-sockname)
+ (and (featurep 'make-network-process '(:family local))
+ (let ((xdg_runtime_dir (getenv "XDG_RUNTIME_DIR")))
+ (if xdg_runtime_dir
+ (format "%s/emacs" xdg_runtime_dir)
+ (format "%s/emacs%d" (or (getenv "TMPDIR") "/tmp") (user-uid))))))
"The directory in which to place the server socket.
If local sockets are not supported, this is nil.")
@@ -361,7 +367,7 @@ Updates `server-clients'."
(server-log "Deleted" proc))))
-(defvar server-log-time-function 'current-time-string
+(defvar server-log-time-function #'current-time-string
"Function to generate timestamps for `server-buffer'.")
(defconst server-buffer " *server*"
@@ -530,13 +536,13 @@ Creates the directory if necessary and makes sure:
(setq attrs (file-attributes dir 'integer)))
;; Check that it's safe for use.
- (let* ((uid (nth 2 attrs))
+ (let* ((uid (file-attribute-user-id attrs))
(w32 (eq system-type 'windows-nt))
(unsafe (cond
- ((not (eq t (car attrs)))
+ ((not (eq t (file-attribute-type attrs)))
(if (null attrs) "its attributes can't be checked"
(format "it is a %s"
- (if (stringp (car attrs))
+ (if (stringp (file-attribute-type attrs))
"symlink" "file"))))
((and w32 (zerop uid)) ; on FAT32?
(display-warning
@@ -628,23 +634,29 @@ the `server-process' variable."
(when server-process
;; kill it dead!
(ignore-errors (delete-process server-process)))
- ;; Delete the socket files made by previous server invocations.
- (if (not (eq t (server-running-p server-name)))
- ;; Remove any leftover socket or authentication file
- (ignore-errors
- (let (delete-by-moving-to-trash)
- (delete-file server-file)))
- (setq server-mode nil) ;; already set by the minor mode code
- (display-warning
- 'server
- (concat "Unable to start the Emacs server.\n"
- (format "There is an existing Emacs server, named %S.\n"
- server-name)
- (substitute-command-keys
- "To start the server in this Emacs process, stop the existing
+ ;; Check to see if an uninitialized external socket has been
+ ;; passed in, if that is the case, skip checking
+ ;; `server-running-p' as this will return the wrong result.
+ (if (and internal--daemon-sockname
+ (not server--external-socket-initialized))
+ (setq server--external-socket-initialized t)
+ ;; Delete the socket files made by previous server invocations.
+ (if (not (eq t (server-running-p server-name)))
+ ;; Remove any leftover socket or authentication file.
+ (ignore-errors
+ (let (delete-by-moving-to-trash)
+ (delete-file server-file)))
+ (setq server-mode nil) ;; already set by the minor mode code
+ (display-warning
+ 'server
+ (concat "Unable to start the Emacs server.\n"
+ (format "There is an existing Emacs server, named %S.\n"
+ server-name)
+ (substitute-command-keys
+ "To start the server in this Emacs process, stop the existing
server or call `\\[server-force-delete]' to forcibly disconnect it."))
- :warning)
- (setq leave-dead t))
+ :warning)
+ (setq leave-dead t)))
;; If this Emacs already had a server, clear out associated status.
(while server-clients
(server-delete-client (car server-clients)))
@@ -658,16 +670,16 @@ server or call `\\[server-force-delete]' to forcibly disconnect it."))
(when server-process
(server-log (message "Restarting server")))
(cl-letf (((default-file-modes) ?\700))
- (add-hook 'suspend-tty-functions 'server-handle-suspend-tty)
- (add-hook 'delete-frame-functions 'server-handle-delete-frame)
+ (add-hook 'suspend-tty-functions #'server-handle-suspend-tty)
+ (add-hook 'delete-frame-functions #'server-handle-delete-frame)
(add-hook 'kill-emacs-query-functions
- 'server-kill-emacs-query-function)
+ #'server-kill-emacs-query-function)
;; We put server's kill-emacs-hook after the others, so that
;; frames are not deleted too early, because doing that
;; would severely degrade our abilities to communicate with
;; the user, while some hooks may wish to ask the user
;; questions (e.g., desktop-kill).
- (add-hook 'kill-emacs-hook 'server-force-stop t) ;Cleanup upon exit.
+ (add-hook 'kill-emacs-hook #'server-force-stop t) ;Cleanup upon exit.
(setq server-process
(apply #'make-network-process
:name server-name
@@ -761,15 +773,11 @@ by the current Emacs process, use the `server-process' variable."
;;;###autoload
(define-minor-mode server-mode
"Toggle Server mode.
-With a prefix argument ARG, enable Server mode if ARG is
-positive, and disable it otherwise. If called from Lisp, enable
-Server mode if ARG is omitted or nil.
Server mode runs a process that accepts commands from the
`emacsclient' program. See Info node `Emacs server' and
`server-start' for details."
:global t
- :group 'server
:version "22.1"
;; Fixme: Should this check for an existing server socket and do
;; nothing if there is one (for multiple Emacs sessions)?
@@ -784,7 +792,7 @@ Server mode runs a process that accepts commands from the
;; intended it to interrupt us rather than interrupt whatever Emacs
;; was doing before it started handling the process filter.
;; Hence `with-local-quit' (bug#6585).
- (let ((v (with-local-quit (eval (car (read-from-string expr))))))
+ (let ((v (with-local-quit (eval (car (read-from-string expr)) t))))
(when proc
(with-temp-buffer
(let ((standard-output (current-buffer)))
@@ -815,7 +823,7 @@ This handles splitting the command if it would be bigger than
(setq prefix "-print-nonl "))
(server-send-string proc (concat prefix qtext "\n"))))
-(defun server-create-tty-frame (tty type proc)
+(defun server-create-tty-frame (tty type proc &optional parameters)
(unless tty
(error "Invalid terminal device"))
(unless type
@@ -848,7 +856,8 @@ This handles splitting the command if it would be bigger than
;; envvars, and then to change the
;; C functions `child_setup' and
;; `getenv_internal' accordingly.
- (environment . ,(process-get proc 'env)))))))
+ (environment . ,(process-get proc 'env))
+ ,@parameters)))))
;; ttys don't use the `display' parameter, but callproc.c does to set
;; the DISPLAY environment on subprocesses.
@@ -1075,9 +1084,8 @@ The following commands are accepted by the client:
;; supported any more.
(cl-assert (eq (match-end 0) (length string)))
(let ((request (substring string 0 (match-beginning 0)))
- (coding-system (and (default-value 'enable-multibyte-characters)
- (or file-name-coding-system
- default-file-name-coding-system)))
+ (coding-system (or file-name-coding-system
+ default-file-name-coding-system))
nowait ; t if emacsclient does not want to wait for us.
frame ; Frame opened for the client (if any).
display ; Open frame on this display.
@@ -1091,24 +1099,25 @@ The following commands are accepted by the client:
tty-type ; string.
files
filepos
- args-left)
+ args-left
+ create-frame-func)
;; Remove this line from STRING.
(setq string (substring string (match-end 0)))
(setq args-left
- (mapcar 'server-unquote-arg (split-string request " " t)))
+ (mapcar #'server-unquote-arg (split-string request " " t)))
(while args-left
(pcase (pop args-left)
;; -version CLIENT-VERSION: obsolete at birth.
- (`"-version" (pop args-left))
+ ("-version" (pop args-left))
;; -nowait: Emacsclient won't wait for a result.
- (`"-nowait" (setq nowait t))
+ ("-nowait" (setq nowait t))
;; -current-frame: Don't create frames.
- (`"-current-frame" (setq use-current-frame t))
+ ("-current-frame" (setq use-current-frame t))
;; -frame-parameters: Set frame parameters
- (`"-frame-parameters"
+ ("-frame-parameters"
(let ((alist (pop args-left)))
(if coding-system
(setq alist (decode-coding-string alist coding-system)))
@@ -1116,24 +1125,24 @@ The following commands are accepted by the client:
;; -display DISPLAY:
;; Open X frames on the given display instead of the default.
- (`"-display"
+ ("-display"
(setq display (pop args-left))
(if (zerop (length display)) (setq display nil)))
;; -parent-id ID:
;; Open X frame within window ID, via XEmbed.
- (`"-parent-id"
+ ("-parent-id"
(setq parent-id (pop args-left))
(if (zerop (length parent-id)) (setq parent-id nil)))
;; -window-system: Open a new X frame.
- (`"-window-system"
+ ("-window-system"
(if (fboundp 'x-create-frame)
(setq dontkill t
tty-name 'window-system)))
;; -resume: Resume a suspended tty frame.
- (`"-resume"
+ ("-resume"
(let ((terminal (process-get proc 'terminal)))
(setq dontkill t)
(push (lambda ()
@@ -1144,7 +1153,7 @@ The following commands are accepted by the client:
;; -suspend: Suspend the client's frame. (In case we
;; get out of sync, and a C-z sends a SIGTSTP to
;; emacsclient.)
- (`"-suspend"
+ ("-suspend"
(let ((terminal (process-get proc 'terminal)))
(setq dontkill t)
(push (lambda ()
@@ -1154,13 +1163,13 @@ The following commands are accepted by the client:
;; -ignore COMMENT: Noop; useful for debugging emacsclient.
;; (The given comment appears in the server log.)
- (`"-ignore"
+ ("-ignore"
(setq dontkill t)
(pop args-left))
;; -tty DEVICE-NAME TYPE: Open a new tty frame.
;; (But if we see -window-system later, use that.)
- (`"-tty"
+ ("-tty"
(setq tty-name (pop args-left)
tty-type (pop args-left)
dontkill (or dontkill
@@ -1179,7 +1188,7 @@ The following commands are accepted by the client:
;; -position LINE[:COLUMN]: Set point to the given
;; position in the next file.
- (`"-position"
+ ("-position"
(if (not (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?"
(car args-left)))
(error "Invalid -position command in client args"))
@@ -1190,7 +1199,7 @@ The following commands are accepted by the client:
""))))))
;; -file FILENAME: Load the given file.
- (`"-file"
+ ("-file"
(let ((file (pop args-left)))
(if coding-system
(setq file (decode-coding-string file coding-system)))
@@ -1208,7 +1217,7 @@ The following commands are accepted by the client:
(setq filepos nil))
;; -eval EXPR: Evaluate a Lisp expression.
- (`"-eval"
+ ("-eval"
(if use-current-frame
(setq use-current-frame 'always))
(let ((expr (pop args-left)))
@@ -1219,14 +1228,14 @@ The following commands are accepted by the client:
(setq filepos nil)))
;; -env NAME=VALUE: An environment variable.
- (`"-env"
+ ("-env"
(let ((var (pop args-left)))
;; XXX Variables should be encoded as in getenv/setenv.
(process-put proc 'env
(cons var (process-get proc 'env)))))
;; -dir DIRNAME: The cwd of the emacsclient process.
- (`"-dir"
+ ("-dir"
(setq dir (pop args-left))
(if coding-system
(setq dir (decode-coding-string dir coding-system)))
@@ -1243,28 +1252,29 @@ The following commands are accepted by the client:
(or files commands)
(setq use-current-frame t))
- (setq frame
- (cond
- ((and use-current-frame
- (or (eq use-current-frame 'always)
- ;; We can't use the Emacs daemon's
- ;; terminal frame.
- (not (and (daemonp)
- (null (cdr (frame-list)))
- (eq (selected-frame)
- terminal-frame)))))
- (setq tty-name nil tty-type nil)
- (if display (server-select-display display)))
- ((or (and (eq system-type 'windows-nt)
- (daemonp)
- (setq display "w32"))
- (eq tty-name 'window-system))
- (server-create-window-system-frame display nowait proc
- parent-id
- frame-parameters))
- ;; When resuming on a tty, tty-name is nil.
- (tty-name
- (server-create-tty-frame tty-name tty-type proc))))
+ (setq create-frame-func
+ (lambda ()
+ (cond
+ ((and use-current-frame
+ (or (eq use-current-frame 'always)
+ ;; We can't use the Emacs daemon's
+ ;; terminal frame.
+ (not (and (daemonp)
+ (null (cdr (frame-list)))
+ (eq (selected-frame)
+ terminal-frame)))))
+ (setq tty-name nil tty-type nil)
+ (if display (server-select-display display)))
+ ((or (and (eq system-type 'windows-nt)
+ (daemonp)
+ (setq display "w32"))
+ (eq tty-name 'window-system))
+ (server-create-window-system-frame display nowait proc
+ parent-id
+ frame-parameters))
+ ;; When resuming on a tty, tty-name is nil.
+ (tty-name
+ (server-create-tty-frame tty-name tty-type proc)))))
(process-put
proc 'continuation
@@ -1276,16 +1286,16 @@ The following commands are accepted by the client:
(if (and dir (file-directory-p dir))
dir default-directory)))
(server-execute proc files nowait commands
- dontkill frame tty-name)))))
+ dontkill create-frame-func tty-name)))))
(when (or frame files)
(server-goto-toplevel proc))
(server-execute-continuation proc))))
;; condition-case
- (error (server-return-error proc err))))
+ (t (server-return-error proc err))))
-(defun server-execute (proc files nowait commands dontkill frame tty-name)
+(defun server-execute (proc files nowait commands dontkill create-frame-func tty-name)
;; This is run from timers and process-filters, i.e. "asynchronously".
;; But w.r.t the user, this is not really asynchronous since the timer
;; is run after 0s and the process-filter is run in response to the
@@ -1295,21 +1305,29 @@ The following commands are accepted by the client:
;; including code that needs to wait.
(with-local-quit
(condition-case err
- (let ((buffers (server-visit-files files proc nowait)))
- (mapc 'funcall (nreverse commands))
-
- ;; If we were told only to open a new client, obey
- ;; `initial-buffer-choice' if it specifies a file
- ;; or a function.
- (unless (or files commands)
- (let ((buf
- (cond ((stringp initial-buffer-choice)
- (find-file-noselect initial-buffer-choice))
- ((functionp initial-buffer-choice)
- (funcall initial-buffer-choice)))))
- (switch-to-buffer
- (if (buffer-live-p buf) buf (get-buffer-create "*scratch*"))
- 'norecord)))
+ (let* ((buffers (server-visit-files files proc nowait))
+ ;; If we were told only to open a new client, obey
+ ;; `initial-buffer-choice' if it specifies a file
+ ;; or a function.
+ (initial-buffer (unless (or files commands)
+ (let ((buf
+ (cond ((stringp initial-buffer-choice)
+ (find-file-noselect initial-buffer-choice))
+ ((functionp initial-buffer-choice)
+ (funcall initial-buffer-choice)))))
+ (if (buffer-live-p buf) buf (startup--get-buffer-create-scratch)))))
+ ;; Set current buffer so that newly created tty frames
+ ;; show the correct buffer initially.
+ (frame (with-current-buffer (or (car buffers)
+ initial-buffer
+ (current-buffer))
+ (prog1
+ (funcall create-frame-func)
+ ;; Switch to initial buffer in case the frame was reused.
+ (when initial-buffer
+ (switch-to-buffer initial-buffer 'norecord))))))
+
+ (mapc #'funcall (nreverse commands))
;; Delete the client if necessary.
(cond
@@ -1325,9 +1343,11 @@ The following commands are accepted by the client:
((or isearch-mode (minibufferp))
nil)
((and frame (null buffers))
+ (run-hooks 'server-after-make-frame-hook)
(message "%s" (substitute-command-keys
"When done with this frame, type \\[delete-frame]")))
((not (null buffers))
+ (run-hooks 'server-after-make-frame-hook)
(server-switch-buffer (car buffers) nil (cdr (car files)))
(run-hooks 'server-switch-hook)
(unless nowait
@@ -1407,7 +1427,7 @@ so don't mark these buffers specially, just visit them normally."
(run-hooks 'post-command-hook))
(unless nowait
;; When the buffer is killed, inform the clients.
- (add-hook 'kill-buffer-hook 'server-kill-buffer nil t)
+ (add-hook 'kill-buffer-hook #'server-kill-buffer nil t)
(push proc server-buffer-clients))
(push (current-buffer) client-record)))
(unless nowait
@@ -1518,8 +1538,8 @@ specifically for the clients and did not exist before their request for it."
"Ask before exiting Emacs if it has live clients."
(or (not (let (live-client)
(dolist (proc server-clients)
- (when (memq t (mapcar 'buffer-live-p (process-get
- proc 'buffers)))
+ (when (memq t (mapcar #'buffer-live-p
+ (process-get proc 'buffers)))
(setq live-client t)))
live-client))
(yes-or-no-p "This Emacs session has clients; exit anyway? ")))
@@ -1555,7 +1575,7 @@ starts server process and that is all. Invoked by \\[server-edit]."
(not server-process)
(memq (process-status server-process) '(signal exit)))
(server-mode 1))
- (server-clients (apply 'server-switch-buffer (server-done)))
+ (server-clients (apply #'server-switch-buffer (server-done)))
(t (message "No server editing buffers exist"))))
(defun server-switch-buffer (&optional next-buffer killed-one filepos)
@@ -1588,7 +1608,7 @@ be a cons cell (LINENUMBER . COLUMNNUMBER)."
(if (not (buffer-live-p next-buffer))
;; If NEXT-BUFFER is a dead buffer, remove the server records for it
;; and try the next surviving server buffer.
- (apply 'server-switch-buffer (server-buffer-done next-buffer))
+ (apply #'server-switch-buffer (server-buffer-done next-buffer))
;; OK, we know next-buffer is live, let's display and select it.
(if (functionp server-window)
(funcall server-window next-buffer)
@@ -1653,13 +1673,15 @@ only these files will be asked to be saved."
(save-buffers-kill-emacs arg)))
((processp proc)
(let ((buffers (process-get proc 'buffers)))
- ;; If client is bufferless, emulate a normal Emacs exit
- ;; and offer to save all buffers. Otherwise, offer to
- ;; save only the buffers belonging to the client.
(save-some-buffers
arg (if buffers
+ ;; Only files from emacsclient file list.
(lambda () (memq (current-buffer) buffers))
- t))
+ ;; No emacsclient file list: don't override
+ ;; `save-some-buffers-default-predicate' (unless
+ ;; ARG is non-nil), since we're not killing
+ ;; Emacs (unlike `save-buffers-kill-emacs').
+ (and arg t)))
(server-delete-client proc)))
(t (error "Invalid client frame")))))
@@ -1672,7 +1694,7 @@ only these files will be asked to be saved."
(save-current-buffer
(dolist (buffer (buffer-list))
(set-buffer buffer)
- (remove-hook 'kill-buffer-hook 'server-kill-buffer t)))
+ (remove-hook 'kill-buffer-hook #'server-kill-buffer t)))
;; continue standard unloading
nil)
@@ -1715,7 +1737,7 @@ returns the process ID of the Emacs instance running \"server\"."
(server-quote-arg (format "%S" form))
"\n"))
(while (memq (process-status process) '(open run))
- (accept-process-output process 0 10))
+ (accept-process-output process 0.01))
(goto-char (point-min))
;; If the result is nil, there's nothing in the buffer. If the
;; result is non-nil, it's after "-print ".