arrived during the time the process was stopped might be lost.
@end defun
-@deffn Command signal-process process signal
+@deffn Command signal-process process signal &optional remote
This function sends a signal to process @var{process}. The argument
@var{signal} specifies which signal to send; it should be an integer,
or a symbol whose name is a signal.
The @var{process} argument can be a system process @acronym{ID} (an
integer); that allows you to send signals to processes that are not
children of Emacs. @xref{System Processes}.
+
+If @var{process} is a process object which contains the property
+@code{remote-pid}, or @var{process} is a number and @var{remote} is a
+remote file name, @var{process} is interpreted as process on the
+respective remote host, which will be the process to signal.
@end deffn
Sometimes, it is necessary to send a signal to a non-local
asynchronous process. This is possible by writing an own
-@code{interrupt-process} implementation. This function must be added
-then to @code{interrupt-process-functions}.
+@code{interrupt-process} or @code{signal-process} implementation.
+This function must be added then to @code{interrupt-process-functions}
+or @code{signal-process-functions}, respectively.
@defvar interrupt-process-functions
This variable is a list of functions to be called for
This is the mechanism, how Tramp implements @code{interrupt-process}.
@end defvar
+@defvar signal-process-functions
+This variable is a list of functions to be called for
+@code{signal-process}. The arguments of the functions are the same as
+for @code{signal-process}. These functions are called in the order of
+the list, until one of them returns non-@code{nil}. The default
+function, which shall always be the last in this list, is
+@code{signal-default-interrupt-process}.
+
+This is the mechanism, how Tramp implements @code{signal-process}.
+@end defvar
+
@node Output from Processes
@section Receiving Output from Processes
@cindex process output
To get the previous action back, put something like the following in
your init file:
- (require 'ido)
- (keymap-set ido-file-completion-map "C-k" #'ido-delete-file-at-head)
+ (require 'ido)
+ (keymap-set ido-file-completion-map "C-k" #'ido-delete-file-at-head)
---
** New user option 'term-clear-full-screen-programs'.
To enable this behavior, customize the user option
'completion-auto-select' to t, then pressing 'TAB' will switch to the
"*Completions*" buffer when it pops up that buffer. If the value is
-'second-tab', then the first tab will display "*Completions*", and the
-second one will switch to the "*Completions*" buffer.
+'second-tab', then the first 'TAB' will display "*Completions*", and
+the second one will switch to the "*Completions*" buffer.
*** New user option 'completion-wrap-movement'.
When non-nil, the commands 'next-completion' and 'previous-completion'
+++
*** 'eudc-expansion-overwrites-query' to 'eudc-expansion-save-query-as-kill'.
'eudc-expansion-overwrites-query' is renamed to
-'eudc-expansion-save-query-as-kill' to reflect the actual behaviour of
-the customization variable.
+'eudc-expansion-save-query-as-kill' to reflect the actual behavior of
+the user option.
+++
*** New command 'eudc-expand-try-all'.
for a name that happens to match a contact in one's BBDB.
+++
-*** New behaviour and default for option 'eudc-inline-expansion-format'
+*** New behavior and default for user option 'eudc-inline-expansion-format'.
EUDC inline expansion result formatting defaulted to
- '("%s %s <%s>" firstname name email)
+ '("%s %s <%s>" firstname name email)
Since email address specifications need to comply with RFC 5322 in
order to be useful in messages, there was a risk to produce syntax
with RFC 5322. When set to nil, a default format very similar to the
old default will be produced. When set to a function, that function
is called, and the returned values are used to populate the phrase and
-comment parts (see RFC 5322 for definitions). In both cases, the
+comment parts (see RFC 5322 for definitions). In both cases, the
phrase part will be automatically quoted if necessary.
** eww/shr
modes to emulate the behavior of the historical editor Twenex Emacs.
It is believed to no longer be useful.
+---
+** proced.el supports sending signals to local processes with root permissions.
+When typing 'C-u k' or 'C-u r', sending a signal to or renicing of a
+local process will use alternative credentials. The credentials to be
+used can be customised by the user option 'proced-remote-directory',
+which defaults to "/sudo::". 'proced-signal-function' has been marked obsolete.
+
\f
* New Modes and Packages in Emacs 29.1
+++
** New package 'oclosure'.
Allows the creation of "functions with slots" or "function objects"
-via the macros `oclosure-define` and `oclosure-lambda`.
+via the macros 'oclosure-define' and 'oclosure-lambda'.
---
** New theme 'leuven-dark'.
This is useful when quoting shell arguments for a remote shell
invocation. Such shells are POSIX conform by default.
++++
+** 'signal-process' now consults the list 'signal-process-functions'.
+This is to determine which function has to be called in order to
+deliver the signal. This allows Tramp to send the signal to remote
+asynchronous processes. The hitherto existing implementation has been
+moved to 'signal-default-interrupt-process'.
+
\f
* Changes in Emacs 29.1 on Non-Free Operating Systems
(lambda ()
(remove-hook 'interrupt-process-functions #'tramp-interrupt-process)))
+(defun tramp-signal-process (process sigcode &optional remote)
+ "Send PROCESS the signal with code SIGCODE.
+PROCESS may also be a number specifying the process id of the
+process to signal; in this case, the process need not be a child of
+this Emacs.
+If PROCESS is a process object which contains the property
+`remote-pid', or PROCESS is a number and REMOTE is a remote file name,
+PROCESS is interpreted as process on the respective remote host, which
+will be the process to signal.
+SIGCODE may be an integer, or a symbol whose name is a signal name."
+ (let (pid vec)
+ (cond
+ ((processp process)
+ (setq pid (process-get process 'remote-pid)
+ vec (process-get process 'vector)))
+ ((numberp process)
+ (setq pid process
+ vec (and (stringp remote) (tramp-dissect-file-name remote))))
+ (t (signal 'wrong-type-argument (list #'processp process))))
+ (unless (or (numberp sigcode) (symbolp sigcode))
+ (signal 'wrong-type-argument (list #'numberp sigcode)))
+ ;; If it's a Tramp process, send SIGCODE remotely.
+ (when (and pid vec)
+ (tramp-message
+ vec 5 "Send signal %s to process %s with pid %s" sigcode process pid)
+ ;; This is for tramp-sh.el. Other backends do not support this (yet).
+ (if (tramp-compat-funcall
+ 'tramp-send-command-and-check
+ vec (format "\\kill -%s %d" sigcode pid))
+ 0 -1))))
+
+;; `signal-process-functions' exists since Emacs 29.1.
+(when (boundp 'signal-process-functions)
+ (add-hook 'signal-process-functions #'tramp-signal-process)
+ (add-hook
+ 'tramp-unload-hook
+ (lambda ()
+ (remove-hook 'signal-process-functions #'tramp-signal-process))))
+
(defun tramp-get-remote-null-device (vec)
"Return null device on the remote host identified by VEC.
If VEC is `tramp-null-hop', return local null device."
;;
;; To do:
;; - Interactive temporary customizability of flags in `proced-grammar-alist'
-;; - Allow "sudo kill PID", "sudo renice PID"
-;; `proced-send-signal' operates on multiple processes one by one.
-;; With "sudo" we want to execute one "kill" or "renice" command
-;; for all marked processes. Is there a `sudo-call-process'?
;;
;; Thoughts and Ideas
;; - Currently, `process-attributes' returns the list of
the external command (usually \"kill\")."
:type '(choice (function :tag "function")
(string :tag "command")))
+(make-obsolete-variable 'proced-signal-function "no longer used." "29.1")
+
+(defcustom proced-remote-directory "/sudo::"
+ "Remote directory to be used when sending a signal.
+It must point to the local host, via a `sudo' or `doas' method,
+or alike. See `proced-send-signal' and `proced-renice'."
+ :version "29.1"
+ :type '(string :tag "remote directory"))
(defcustom proced-renice-command "renice"
"Name of renice command."
Type \\[proced] to start a Proced session. In a Proced buffer
type \\<proced-mode-map>\\[proced-mark] to mark a process for later commands.
Type \\[proced-send-signal] to send signals to marked processes.
+Type \\[proced-renice] to renice marked processes.
+With a prefix argument \\[universal-argument], sending signals to and renicing of processes
+will be performed with the credentials of `proced-remote-directory'.
The initial content of a listing is defined by the variable `proced-filter'
and the variable `proced-format'.
For backward compatibility SIGNAL and PROCESS-ALIST may be nil.
Then PROCESS-ALIST contains the marked processes or the process point is on
and SIGNAL is queried interactively. This noninteractive usage is still
-supported but discouraged. It will be removed in a future version of Emacs."
+supported but discouraged. It will be removed in a future version of Emacs.
+
+With a prefix argument \\[universal-argument], send the signal with the credentials of
+`proced-remote-directory'."
(interactive
(let* ((process-alist (proced-marked-processes))
(pnum (if (= 1 (length process-alist))
proced-signal-list
nil nil nil nil "TERM"))))))
- (let (failures)
+ (let ((default-directory
+ (if (and current-prefix-arg (stringp proced-remote-directory))
+ proced-remote-directory temporary-file-directory))
+ failures)
;; Why not always use `signal-process'? See
;; https://lists.gnu.org/r/emacs-devel/2008-03/msg02955.html
(if (functionp proced-signal-function)
(dolist (process process-alist)
(condition-case err
(unless (zerop (funcall
- proced-signal-function (car process) signal))
+ proced-signal-function (car process) signal
+ (file-remote-p default-directory)))
(proced-log "%s\n" (cdr process))
(push (cdr process) failures))
(error ; catch errors from failed signals
(dolist (process process-alist)
(with-temp-buffer
(condition-case nil
- (unless (zerop (call-process
+ (unless (zerop (process-file
proced-signal-function nil t nil
signal (number-to-string (car process))))
(proced-log (current-buffer))
Interactively, PROCESS-ALIST contains the marked processes.
If no process is marked, it contains the process point is on,
After renicing all processes in PROCESS-ALIST, this command runs
-the normal hook `proced-after-send-signal-hook'."
+the normal hook `proced-after-send-signal-hook'.
+
+With a prefix argument \\[universal-argument], apply renice with the credentials of
+`proced-remote-directory'."
(interactive
(let ((process-alist (proced-marked-processes)))
(proced-with-processes-buffer process-alist
proced-mode)
(if (numberp priority)
(setq priority (number-to-string priority)))
- (let (failures)
+ (let ((default-directory
+ (if (and current-prefix-arg (stringp proced-remote-directory))
+ proced-remote-directory temporary-file-directory))
+ failures)
(dolist (process process-alist)
(with-temp-buffer
(condition-case nil
- (unless (zerop (call-process
+ (unless (zerop (process-file
proced-renice-command nil t nil
priority (number-to-string (car process))))
(proced-log (current-buffer))
return -1;
}
-DEFUN ("signal-process", Fsignal_process, Ssignal_process,
- 2, 2, "sProcess (name or number): \nnSignal code: ",
- doc: /* Send PROCESS the signal with code SIGCODE.
-PROCESS may also be a number specifying the process id of the
-process to signal; in this case, the process need not be a child of
-this Emacs.
-SIGCODE may be an integer, or a symbol whose name is a signal name. */)
- (Lisp_Object process, Lisp_Object sigcode)
+DEFUN ("internal-default-signal-process",
+ Finternal_default_signal_process,
+ Sinternal_default_signal_process, 2, 3, 0,
+ doc: /* Default function to send PROCESS the signal with code SIGCODE.
+It shall be the last element in list `signal-process-functions'.
+See function `signal-process' for more details on usage. */)
+ (Lisp_Object process, Lisp_Object sigcode, Lisp_Object remote)
{
pid_t pid;
int signo;
return make_fixnum (kill (pid, signo));
}
+DEFUN ("signal-process", Fsignal_process, Ssignal_process,
+ 2, 3, "sProcess (name or number): \nnSignal code: ",
+ doc: /* Send PROCESS the signal with code SIGCODE.
+PROCESS may also be a number specifying the process id of the
+process to signal; in this case, the process need not be a child of
+this Emacs.
+If PROCESS is a process object which contains the property
+`remote-pid', or PROCESS is a number and REMOTE is a remote file name,
+PROCESS is interpreted as process on the respective remote host, which
+will be the process to signal.
+SIGCODE may be an integer, or a symbol whose name is a signal name. */)
+ (Lisp_Object process, Lisp_Object sigcode, Lisp_Object remote)
+{
+ return CALLN (Frun_hook_with_args_until_success, Qsignal_process_functions,
+ process, sigcode, remote);
+}
+
DEFUN ("process-send-eof", Fprocess_send_eof, Sprocess_send_eof, 0, 1, 0,
doc: /* Make PROCESS see end-of-file in its input.
EOF comes after any text already sent to it.
returns non-nil. */);
Vinterrupt_process_functions = list1 (Qinternal_default_interrupt_process);
+ DEFVAR_LISP ("signal-process-functions", Vsignal_process_functions,
+ doc: /* List of functions to be called for `signal-process'.
+The arguments of the functions are the same as for `signal-process'.
+These functions are called in the order of the list, until one of them
+returns non-nil. */);
+ Vsignal_process_functions = list1 (Qinternal_default_signal_process);
+
DEFVAR_LISP ("internal--daemon-sockname", Vinternal__daemon_sockname,
doc: /* Name of external socket passed to Emacs, or nil if none. */);
Vinternal__daemon_sockname = Qnil;
"internal-default-interrupt-process");
DEFSYM (Qinterrupt_process_functions, "interrupt-process-functions");
+ DEFSYM (Qinternal_default_signal_process,
+ "internal-default-signal-process");
+ DEFSYM (Qsignal_process_functions, "signal-process-functions");
+
DEFSYM (Qnull, "null");
DEFSYM (Qpipe_process_p, "pipe-process-p");
defsubr (&Scontinue_process);
defsubr (&Sprocess_running_child_p);
defsubr (&Sprocess_send_eof);
+ defsubr (&Sinternal_default_signal_process);
defsubr (&Ssignal_process);
defsubr (&Swaiting_for_user_input_p);
defsubr (&Sprocess_type);
(ert-deftest tramp-test31-interrupt-process ()
"Check `interrupt-process'."
:tags (append '(:expensive-test :tramp-asynchronous-processes)
+ ;; The final `process-live-p' check does not run sufficiently.
(and (or (getenv "EMACS_HYDRA_CI") (getenv "EMACS_EMBA_CI"))
'(:unstable)))
(skip-unless (tramp--test-enabled))
;; Cleanup.
(ignore-errors (delete-process proc)))))
+(ert-deftest tramp-test31-signal-process ()
+ "Check `signal-process'."
+ :tags (append '(:expensive-test :tramp-asynchronous-processes)
+ ;; The final `process-live-p' check does not run sufficiently.
+ (and (or (getenv "EMACS_HYDRA_CI") (getenv "EMACS_EMBA_CI"))
+ '(:unstable)))
+ (skip-unless (tramp--test-enabled))
+ (skip-unless (tramp--test-sh-p))
+ (skip-unless (not (tramp--test-crypt-p)))
+ ;; Since Emacs 29.1.
+ (skip-unless (boundp 'signal-process-functions))
+
+ ;; We must use `file-truename' for the temporary directory, in
+ ;; order to establish the connection prior running an asynchronous
+ ;; process.
+ (let ((default-directory (file-truename tramp-test-temporary-file-directory))
+ (delete-exited-processes t)
+ kill-buffer-query-functions command proc)
+
+ (dolist (sigcode '(2 INT))
+ (unwind-protect
+ (with-temp-buffer
+ (setq command "trap 'echo boom; exit 1' 2; sleep 100"
+ proc (start-file-process-shell-command
+ (format "test1%s" sigcode) (current-buffer) command))
+ (should (processp proc))
+ (should (process-live-p proc))
+ (should (equal (process-status proc) 'run))
+ (should (numberp (process-get proc 'remote-pid)))
+ (should (equal (process-get proc 'remote-command)
+ (with-connection-local-variables
+ `(,shell-file-name ,shell-command-switch ,command))))
+ (should (zerop (signal-process proc sigcode)))
+ ;; Let the process accept the signal.
+ (with-timeout (10 (tramp--test-timeout-handler))
+ (while (accept-process-output proc 0 nil t)))
+ (should-not (process-live-p proc)))
+
+ ;; Cleanup.
+ (ignore-errors (kill-process proc))
+ (ignore-errors (delete-process proc)))
+
+ (unwind-protect
+ (with-temp-buffer
+ (setq command "trap 'echo boom; exit 1' 2; sleep 100"
+ proc (start-file-process-shell-command
+ (format "test2%s" sigcode) (current-buffer) command))
+ (should (processp proc))
+ (should (process-live-p proc))
+ (should (equal (process-status proc) 'run))
+ (should (numberp (process-get proc 'remote-pid)))
+ (should (equal (process-get proc 'remote-command)
+ (with-connection-local-variables
+ `(,shell-file-name ,shell-command-switch ,command))))
+ (should
+ (zerop
+ (signal-process
+ (process-get proc 'remote-pid) sigcode default-directory)))
+ ;; Let the process accept the signal.
+ (with-timeout (10 (tramp--test-timeout-handler))
+ (while (accept-process-output proc 0 nil t)))
+ (should-not (process-live-p proc)))
+
+ ;; Cleanup.
+ (ignore-errors (kill-process proc))
+ (ignore-errors (delete-process proc))))))
+
(defun tramp--test-async-shell-command
(command output-buffer &optional error-buffer input)
"Like `async-shell-command', reading the output.