]> code.delx.au - gnu-emacs/blobdiff - lisp/proced.el
(math-trig-rewrite, math-hyperbolic-trig-rewrite): New functions.
[gnu-emacs] / lisp / proced.el
index cc453b526d13696cc3101e309b1b97847c355a7a..f529ac72c2cd23c0bb0d51e69c2e67e582e6e402 100644 (file)
@@ -1,6 +1,6 @@
 ;;; proced.el --- operate on system processes like dired
 
-;; Copyright (C) 2008 Free Software Foundation, Inc.
+;; Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
 ;; Author: Roland Winkler <Roland.Winkler@physik.uni-erlangen.de>
 ;; Keywords: Processes, Unix
 ;; - allow "sudo kill PID", "renice PID"
 ;;
 ;; Thoughts and Ideas
-;; - Currently, `system-process-attributes' returns the list of
+;; - Currently, `process-attributes' returns the list of
 ;;   command-line arguments of a process as one concatenated string.
 ;;   This format is compatible with `shell-command'.  Also, under
 ;;   MS-Windows, the command-line arguments are actually stored as a
 ;;   single string, so that it is impossible to reverse-engineer it back
-;;   into separate arguments.  Alternatively, `system-process-attributes'
+;;   into separate arguments.  Alternatively, `process-attributes'
 ;;   could (try to) return a list of strings that correspond to individual
 ;;   command-line arguments.  Then one could feed such a list of
 ;;   command-line arguments into `call-process' or `start-process'.
@@ -94,7 +94,7 @@ the external command (usually \"kill\")."
 ;; It would be neat if one could temporarily override the following
 ;; predefined rules.
 (defcustom proced-grammar-alist
-  '( ;; attributes defined in `system-process-attributes'
+  '( ;; attributes defined in `process-attributes'
     (euid    "EUID"    "%d" right proced-< nil (euid pid) (nil t nil))
     (user    "USER"    nil left proced-string-lessp nil (user pid) (nil t nil))
     (egid    "EGID"    "%d" right proced-< nil (egid euid pid) (nil t nil))
@@ -102,7 +102,7 @@ the external command (usually \"kill\")."
     (comm    "COMMAND" nil left proced-string-lessp nil (comm pid) (nil t nil))
     (state   "STAT"    nil left proced-string-lessp nil (state pid) (nil t nil))
     (ppid    "PPID"    "%d" right proced-< nil (ppid pid)
-             ((lambda (ppid) (proced-filter-parents proced-process-alist ppid)) .
+             ((lambda (ppid) (proced-filter-parents proced-process-alist ppid))
               "refine to process parents"))
     (pgrp    "PGRP"    "%d" right proced-< nil (pgrp euid pid) (nil t nil))
     (sess    "SESS"    "%d" right proced-< nil (sess pid) (nil t nil))
@@ -114,8 +114,10 @@ the external command (usually \"kill\")."
     (cmajflt "CMAJFLT" "%d" right proced-< nil (cmajflt pid) (nil t t))
     (utime   "UTIME"   proced-format-time right proced-time-lessp t (utime pid) (nil t t))
     (stime   "STIME"   proced-format-time right proced-time-lessp t (stime pid) (nil t t))
+    (time    "TIME"   proced-format-time right proced-time-lessp t (time pid) (nil t t))
     (cutime  "CUTIME"  proced-format-time right proced-time-lessp t (cutime pid) (nil t t))
     (cstime  "CSTIME"  proced-format-time right proced-time-lessp t (cstime pid) (nil t t))
+    (ctime   "CTIME"  proced-format-time right proced-time-lessp t (ctime pid) (nil t t))
     (pri     "PR"      "%d" right proced-< t (pri pid) (nil t t))
     (nice    "NI"      "%3d" 3 proced-< t (nice pid) (t t nil))
     (thcount "THCOUNT" "%d" right proced-< t (thcount pid) (nil t t))
@@ -129,12 +131,8 @@ the external command (usually \"kill\")."
     ;;
     ;; attributes defined by proced (see `proced-process-attributes')
     (pid     "PID"     "%d" right proced-< nil (pid)
-             ((lambda (ppid) (proced-filter-children proced-process-alist ppid)) .
+             ((lambda (ppid) (proced-filter-children proced-process-alist ppid))
               "refine to process children"))
-    ;; time: sum of utime and stime
-    (time    "TIME"   proced-format-time right proced-time-lessp t (time pid) (nil t t))
-    ;; ctime: sum of cutime and cstime
-    (ctime   "CTIME"  proced-format-time right proced-time-lessp t (ctime pid) (nil t t))
     ;; process tree
     (tree    "TREE"   proced-format-tree left nil nil nil nil))
   "Alist of rules for handling Proced attributes.
@@ -183,7 +181,7 @@ If PREDICATE yields non-nil, the process is accepted if LESS-B is non-nil.
 If PREDICATE yields 'equal, the process is accepted if EQUAL-B is non-nil.
 If PREDICATE yields nil, the process is accepted if LARGER-B is non-nil.
 
-REFINER can also be a cons pair (FUNCTION . HELP-ECHO).
+REFINER can also be a list (FUNCTION HELP-ECHO).
 FUNCTION is called with one argument, the PID of the process at the position
 of point.  The function must return a list of PIDs that is used for the refined
 listing.  HELP-ECHO is a string that is shown when mouse is over this field.
@@ -208,19 +206,19 @@ If REFINER is nil no refinement is done."
                        (repeat :tag "Sort Scheme" (symbol :tag "Key"))
                        (choice :tag "Refiner"
                                (const :tag "None" nil)
+                               (list (function :tag "Refinement Function")
+                                     (string :tag "Help echo"))
                                (list :tag "Refine Flags"
                                      (boolean :tag "Less")
                                      (boolean :tag "Equal")
-                                     (boolean :tag "Larger"))
-                               (cons (function :tag "Refinement Function")
-                                     (string :tag "Help echo"))))))
+                                     (boolean :tag "Larger"))))))
 
 (defcustom proced-custom-attributes nil
   "List of functions defining custom attributes.
 This variable extends the functionality of `proced-process-attributes'.
 Each function is called with one argument, the list of attributes
 of a system process.  It returns a cons cell of the form (KEY . VALUE)
-like `system-process-attributes'.  This cons cell is appended to the list
+like `process-attributes'.  This cons cell is appended to the list
 returned by `proced-process-attributes'.
 If the function returns nil, the value is ignored."
   :group 'proced
@@ -319,7 +317,7 @@ of `proced-grammar-alist'."
   :group 'proced
   :type '(choice (symbol :tag "Sort Scheme")
                  (repeat :tag "Key List" (symbol :tag "Key"))))
-(make-variable-buffer-local 'proced-format)
+(make-variable-buffer-local 'proced-sort)
 
 (defcustom proced-descend t
   "Non-nil if proced listing is sorted in descending order."
@@ -346,11 +344,25 @@ Can be changed interactively via `proced-toggle-auto-update'."
 (make-variable-buffer-local 'proced-auto-update-flag)
 
 (defcustom proced-tree-flag nil
-  "Non-nil for display of Proced-buffer as process tree."
+  "Non-nil for display of Proced buffer as process tree."
   :group 'proced
   :type 'boolean)
 (make-variable-buffer-local 'proced-tree-flag)
 
+(defcustom proced-post-display-hook nil
+  "Normal hook run after displaying or updating a Proced buffer.
+May be used to adapt the window size via `fit-window-to-buffer'."
+  :type 'hook
+  :options '(fit-window-to-buffer)
+  :group 'proced)
+
+(defcustom proced-after-send-signal-hook nil
+  "Normal hook run after sending a signal to processes by `proced-send-signal'.
+May be used to revert the process listing."
+  :type 'hook
+  :options '(proced-revert)
+  :group 'proced)
+
 ;; Internal variables
 
 (defvar proced-available (not (null (list-system-processes)))
@@ -405,8 +417,8 @@ Important: the match ends just after the marker.")
 (defvar proced-process-tree nil
   "Proced process tree (internal variable).")
 
-(defvar proced-tree-indent nil
-  "Internal variable for indentation of Proced process tree.")
+(defvar proced-tree-depth nil
+  "Internal variable for depth of Proced process tree.")
 
 (defvar proced-auto-update-timer nil
   "Stores if Proced auto update timer is already installed.")
@@ -478,12 +490,11 @@ Important: the match ends just after the marker.")
     (define-key km "x" 'proced-send-signal) ; Dired compatibility
     (define-key km "k" 'proced-send-signal) ; kill processes
     ;; misc
-    (define-key km "g" 'revert-buffer)  ; Dired compatibility
     (define-key km "h" 'describe-mode)
     (define-key km "?" 'proced-help)
-    (define-key km "q" 'quit-window)
     (define-key km [remap undo] 'proced-undo)
     (define-key km [remap advertised-undo] 'proced-undo)
+    ;; Additional keybindings are inherited from `special-mode-map'
     km)
   "Keymap for Proced commands.")
 
@@ -581,8 +592,10 @@ Important: the match ends just after the marker.")
 (defun proced-header-line ()
   "Return header line for Proced buffer."
   (list (propertize " " 'display '(space :align-to 0))
-        (replace-regexp-in-string ;; preserve text properties
-         "\\(%\\)" "\\1\\1" (substring proced-header-line (window-hscroll)))))
+        (if (<= (window-hscroll) (length proced-header-line))
+            (replace-regexp-in-string ;; preserve text properties
+             "\\(%\\)" "\\1\\1"
+             (substring proced-header-line (window-hscroll))))))
 
 (defun proced-pid-at-point ()
   "Return pid of system process at point.
@@ -594,7 +607,7 @@ Return nil if point is not on a process line."
 
 ;; proced mode
 
-(define-derived-mode proced-mode nil "Proced"
+(define-derived-mode proced-mode special-mode "Proced"
   "Mode for displaying UNIX system processes and sending signals to them.
 Type \\[proced] to start a Proced session.  In a Proced buffer
 type \\<proced-mode-map>\\[proced-mark] to mark a process for later commands.
@@ -623,6 +636,9 @@ Refining an existing listing does not update the variable `proced-filter'.
 The attribute-specific rules for formatting, filtering, sorting, and refining
 are defined in `proced-grammar-alist'.
 
+After displaying or updating a Proced buffer, Proced runs the normal hook
+`proced-post-display-hook'.
+
 \\{proced-mode-map}"
   (abbrev-mode 0)
   (auto-fill-mode 0)
@@ -638,14 +654,12 @@ are defined in `proced-grammar-alist'.
             (run-at-time t proced-auto-update-interval
                          'proced-auto-update-timer))))
 
-;; Proced mode is suitable only for specially formatted data.
-(put 'proced-mode 'mode-class 'special)
-
 ;;;###autoload
 (defun proced (&optional arg)
   "Generate a listing of UNIX system processes.
 If invoked with optional ARG the window displaying the process
 information will be displayed but not selected.
+Runs the normal hook `proced-post-display-hook'.
 
 See `proced-mode' for a description of features available in Proced buffers."
   (interactive "P")
@@ -654,12 +668,21 @@ See `proced-mode' for a description of features available in Proced buffers."
   (let ((buffer (get-buffer-create "*Proced*")) new)
     (set-buffer buffer)
     (setq new (zerop (buffer-size)))
-    (if new (proced-mode))
-    (if (or new arg)
-        (proced-update t))
+    (when new
+      (proced-mode)
+      ;; `proced-update' runs `proced-post-display-hook' only if the
+      ;; Proced buffer has been selected.  Yet the following call of
+      ;; `proced-update' is for an empty Proced buffer that has not
+      ;; yet been selected.  Therefore we need to call
+      ;; `proced-post-display-hook' below.
+      (proced-update t))
     (if arg
-       (display-buffer buffer)
+        (progn
+          (display-buffer buffer)
+          (with-current-buffer buffer
+            (run-hooks 'proced-post-display-hook)))
       (pop-to-buffer buffer)
+      (run-hooks 'proced-post-display-hook)
       (message
        (substitute-command-keys
         "Type \\<proced-mode-map>\\[quit-window] to quit, \\[proced-help] for help")))))
@@ -685,6 +708,8 @@ The time interval for updates is specified via `proced-auto-update-interval'."
   (message "Proced auto update %s"
            (if proced-auto-update-flag "enabled" "disabled")))
 
+;;; Mark
+
 (defun proced-mark (&optional count)
   "Mark the current (or next COUNT) processes."
   (interactive "p")
@@ -714,6 +739,30 @@ The time interval for updates is specified via `proced-auto-update-interval'."
       (proced-insert-mark mark backward))
     (proced-move-to-goal-column)))
 
+(defun proced-toggle-marks ()
+  "Toggle marks: marked processes become unmarked, and vice versa."
+  (interactive)
+  (let ((mark-re (proced-marker-regexp))
+        buffer-read-only)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (cond ((looking-at mark-re)
+               (proced-insert-mark nil))
+              ((looking-at " ")
+               (proced-insert-mark t))
+              (t
+               (forward-line 1)))))))
+
+(defun proced-insert-mark (mark &optional backward)
+  "If MARK is non-nil, insert `proced-marker-char'.
+If BACKWARD is non-nil, move one line backwards before inserting the mark.
+Otherwise move one line forward after inserting the mark."
+  (if backward (forward-line -1))
+  (insert (if mark proced-marker-char ?\s))
+  (delete-char 1)
+  (unless backward (forward-line)))
+
 (defun proced-mark-all ()
   "Mark all processes.
 If `transient-mark-mode' is turned on and the region is active,
@@ -732,7 +781,10 @@ unmark the region."
   "Mark all processes using MARK.
 If `transient-mark-mode' is turned on and the region is active,
 mark the region."
-  (let ((count 0) end buffer-read-only)
+  (let* ((count 0)
+         (proced-marker-char (if mark proced-marker-char ?\s))
+         (marker-re (proced-marker-regexp))
+         end buffer-read-only)
     (save-excursion
       (if (use-region-p)
           ;; Operate even on those lines that are only partially a part
@@ -747,33 +799,12 @@ mark the region."
         (goto-char (point-min))
         (setq end (point-max)))
       (while (< (point) end)
-        (setq count (1+ count))
-        (proced-insert-mark mark))
-      (proced-success-message "Marked" count))))
-
-(defun proced-toggle-marks ()
-  "Toggle marks: marked processes become unmarked, and vice versa."
-  (interactive)
-  (let ((mark-re (proced-marker-regexp))
-        buffer-read-only)
-    (save-excursion
-      (goto-char (point-min))
-      (while (not (eobp))
-        (cond ((looking-at mark-re)
-               (proced-insert-mark nil))
-              ((looking-at " ")
-               (proced-insert-mark t))
-              (t
-               (forward-line 1)))))))
-
-(defun proced-insert-mark (mark &optional backward)
-  "If MARK is non-nil, insert `proced-marker-char'.
-If BACKWARD is non-nil, move one line backwards before inserting the mark.
-Otherwise move one line forward after inserting the mark."
-  (if backward (forward-line -1))
-  (insert (if mark proced-marker-char ?\s))
-  (delete-char 1)
-  (unless backward (forward-line)))
+        (unless (looking-at marker-re)
+          (setq count (1+ count))
+          (insert proced-marker-char)
+          (delete-char 1))
+        (forward-line))
+      (proced-success-message (if mark "Marked" "Unmarked") count))))
 
 (defun proced-mark-children (ppid &optional omit-ppid)
   "Mark child processes of process PPID.
@@ -1026,7 +1057,7 @@ Return the rearranged process list."
   (if proced-tree-flag
       ;; add tree attribute
       (let ((process-tree (proced-process-tree process-alist))
-            (proced-tree-indent 0)
+            (proced-tree-depth 0)
             (proced-temp-alist process-alist)
             proced-process-tree pt)
         (while (setq pt (pop process-tree))
@@ -1044,11 +1075,11 @@ Return the rearranged process list."
   "Helper function for `proced-tree'."
   (let ((pprocess (assq (car process-tree) proced-temp-alist)))
     (push (append (list (car pprocess))
-                  (list (cons 'tree proced-tree-indent))
+                  (list (cons 'tree proced-tree-depth))
                   (cdr pprocess))
           proced-process-tree)
     (if (cdr process-tree)
-        (let ((proced-tree-indent (1+ proced-tree-indent)))
+        (let ((proced-tree-depth (1+ proced-tree-depth)))
           (mapc 'proced-tree-insert (cdr process-tree))))))
 
 ;; Refining
@@ -1361,7 +1392,9 @@ Replace newline characters by \"^J\" (two characters)."
   (let ((standard-attributes
          (car (proced-process-attributes (list (emacs-pid)))))
         new-format fmi)
-    (if proced-tree-flag (push (cons 'tree 0) standard-attributes))
+    (if (and proced-tree-flag
+             (assq 'ppid standard-attributes))
+        (push (cons 'tree 0) standard-attributes))
     (dolist (fmt format)
       (if (symbolp fmt)
           (if (assq fmt standard-attributes)
@@ -1402,7 +1435,7 @@ Replace newline characters by \"^J\" (two characters)."
               (cond ((functionp (car refiner))
                      `(proced-key ,key mouse-face highlight
                                   help-echo ,(format "mouse-2, RET: %s"
-                                                     (cdr refiner))))
+                                                     (nth 1 refiner))))
                     ((consp refiner)
                      `(proced-key ,key mouse-face highlight
                                   help-echo ,(format "mouse-2, RET: refine by attribute %s %s"
@@ -1504,30 +1537,21 @@ If no attributes are known for a process (possibly because it already died)
 the process is ignored."
   ;; Should we make it customizable whether processes with empty attribute
   ;; lists are ignored?  When would such processes be of interest?
-  (let (process-alist attributes)
+  (let (process-alist attributes attr)
     (dolist (pid (or pid-list (list-system-processes)) process-alist)
-      (when (setq attributes (system-process-attributes pid))
-        (let ((utime (cdr (assq 'utime attributes)))
-              (stime (cdr (assq 'stime attributes)))
-              (cutime (cdr (assq 'cutime attributes)))
-              (cstime (cdr (assq 'cstime attributes)))
-              attr)
-          (setq attributes
-                (append (list (cons 'pid pid))
-                        (if (and utime stime)
-                            (list (cons 'time (time-add utime stime))))
-                        (if (and cutime cstime)
-                            (list (cons 'ctime (time-add cutime cstime))))
-                        attributes))
-          (dolist (fun proced-custom-attributes)
-            (if (setq attr (funcall fun attributes))
-                (push attr attributes)))
-          (push (cons pid attributes) process-alist))))))
+      (when (setq attributes (process-attributes pid))
+        (setq attributes (cons (cons 'pid pid) attributes))
+        (dolist (fun proced-custom-attributes)
+          (if (setq attr (funcall fun attributes))
+              (push attr attributes)))
+        (push (cons pid attributes) process-alist)))))
 
 (defun proced-update (&optional revert quiet)
   "Update the Proced process information.  Preserves point and marks.
 With prefix REVERT non-nil, revert listing.
-Suppress status information if QUIET is nil."
+Suppress status information if QUIET is nil.
+After updating a displayed Proced buffer run the normal hook
+`proced-post-display-hook'."
   ;; This is the main function that generates and updates the process listing.
   (interactive "P")
   (setq revert (or revert (not proced-process-alist)))
@@ -1643,6 +1667,8 @@ Suppress status information if QUIET is nil."
                                 (nth 1 grammar)))
                     "")))
     (force-mode-line-update)
+    ;; run `proced-post-display-hook' only for a displayed buffer.
+    (if (get-buffer-window) (run-hooks 'proced-post-display-hook))
     ;; done
     (or quiet (input-pending-p)
         (message (if revert "Updating process information...done."
@@ -1653,17 +1679,13 @@ Suppress status information if QUIET is nil."
 Preserves point and marks."
   (proced-update t))
 
-;; I do not want to reinvent the wheel.  Should we rename `dired-pop-to-buffer'
-;; and move it to window.el so that proced and ibuffer can easily use it, too?
-;; What about functions like `appt-disp-window' that use
-;; `shrink-window-if-larger-than-buffer'?
-(autoload 'dired-pop-to-buffer "dired")
-
 (defun proced-send-signal (&optional signal)
   "Send a SIGNAL to the marked processes.
 If no process is marked, operate on current process.
 SIGNAL may be a string (HUP, INT, TERM, etc.) or a number.
-If SIGNAL is nil display marked processes and query interactively for SIGNAL."
+If SIGNAL is nil display marked processes and query interactively for SIGNAL.
+After sending the signal, this command runs the normal hook
+`proced-after-send-signal-hook'."
   (interactive)
   (let ((regexp (proced-marker-regexp))
         process-alist)
@@ -1673,7 +1695,9 @@ If SIGNAL is nil display marked processes and query interactively for SIGNAL."
       (while (re-search-forward regexp nil t)
         (push (cons (proced-pid-at-point)
                     ;; How much info should we collect here?
-                    (substring (match-string-no-properties 0) 2))
+                    (buffer-substring-no-properties
+                     (+ 2 (line-beginning-position))
+                     (line-end-position)))
               process-alist)))
     (setq process-alist
           (if process-alist
@@ -1696,7 +1720,11 @@ If SIGNAL is nil display marked processes and query interactively for SIGNAL."
           (dolist (process process-alist)
             (insert "  " (cdr process) "\n"))
           (save-window-excursion
-            (dired-pop-to-buffer bufname) ; all we need
+            ;; Analogous to `dired-pop-to-buffer'
+            ;; Don't split window horizontally.  (Bug#1806)
+            (let (split-width-threshold)
+              (pop-to-buffer (current-buffer)))
+            (fit-window-to-buffer (get-buffer-window) nil 1)
             (let* ((completion-ignore-case t)
                    (pnum (if (= 1 (length process-alist))
                              "1 process"
@@ -1709,59 +1737,59 @@ If SIGNAL is nil display marked processes and query interactively for SIGNAL."
                                          proced-signal-list
                                          nil nil nil nil "TERM")))
               (setq signal (if (string-match "^\\(\\S-+\\)\\s-" tmp)
-                               (match-string 1 tmp) tmp))))))
-      ;; send signal
-      (let ((count 0)
-            failures)
-        ;; Why not always use `signal-process'?  See
-        ;; http://lists.gnu.org/archive/html/emacs-devel/2008-03/msg02955.html
-        (if (functionp proced-signal-function)
-            ;; use built-in `signal-process'
-            (let ((signal (if (stringp signal)
-                              (if (string-match "\\`[0-9]+\\'" signal)
-                                  (string-to-number signal)
-                                (make-symbol signal))
-                            signal)))   ; number
-              (dolist (process process-alist)
-                (condition-case err
-                    (if (zerop (funcall
-                                proced-signal-function (car process) signal))
-                        (setq count (1+ count))
-                      (proced-log "%s\n" (cdr process))
-                      (push (cdr process) failures))
-                  (error ;; catch errors from failed signals
-                   (proced-log "%s\n" err)
-                   (proced-log "%s\n" (cdr process))
-                   (push (cdr process) failures)))))
-          ;; use external system call
-          (let ((signal (concat "-" (if (numberp signal)
-                                        (number-to-string signal) signal))))
+                               (match-string 1 tmp) tmp)))))))
+    ;; send signal
+    (let ((count 0)
+          failures)
+      ;; Why not always use `signal-process'?  See
+      ;; http://lists.gnu.org/archive/html/emacs-devel/2008-03/msg02955.html
+      (if (functionp proced-signal-function)
+          ;; use built-in `signal-process'
+          (let ((signal (if (stringp signal)
+                            (if (string-match "\\`[0-9]+\\'" signal)
+                                (string-to-number signal)
+                              (make-symbol signal))
+                          signal)))   ; number
             (dolist (process process-alist)
-              (with-temp-buffer
-                (condition-case err
-                    (if (zerop (call-process
-                                proced-signal-function nil t nil
-                                signal (number-to-string (car process))))
-                        (setq count (1+ count))
-                      (proced-log (current-buffer))
-                      (proced-log "%s\n" (cdr process))
-                      (push (cdr process) failures))
-                  (error ;; catch errors from failed signals
-                   (proced-log (current-buffer))
-                   (proced-log "%s\n" (cdr process))
-                   (push (cdr process) failures)))))))
-        (if failures
-            ;; Proced error message are not always very precise.
-            ;; Can we issue a useful one-line summary in the
-            ;; message area (using FAILURES) if only one signal failed?
-            (proced-log-summary
-             signal
-             (format "%d of %d signal%s failed"
-                     (length failures) (length process-alist)
-                     (if (= 1 (length process-alist)) "" "s")))
-          (proced-success-message "Sent signal to" count)))
-      ;; final clean-up
-      (run-hooks 'proced-after-send-signal-hook))))
+              (condition-case err
+                  (if (zerop (funcall
+                              proced-signal-function (car process) signal))
+                      (setq count (1+ count))
+                    (proced-log "%s\n" (cdr process))
+                    (push (cdr process) failures))
+                (error ; catch errors from failed signals
+                 (proced-log "%s\n" err)
+                 (proced-log "%s\n" (cdr process))
+                 (push (cdr process) failures)))))
+        ;; use external system call
+        (let ((signal (concat "-" (if (numberp signal)
+                                      (number-to-string signal) signal))))
+          (dolist (process process-alist)
+            (with-temp-buffer
+              (condition-case err
+                  (if (zerop (call-process
+                              proced-signal-function nil t nil
+                              signal (number-to-string (car process))))
+                      (setq count (1+ count))
+                    (proced-log (current-buffer))
+                    (proced-log "%s\n" (cdr process))
+                    (push (cdr process) failures))
+                (error ; catch errors from failed signals
+                 (proced-log (current-buffer))
+                 (proced-log "%s\n" (cdr process))
+                 (push (cdr process) failures)))))))
+      (if failures
+          ;; Proced error message are not always very precise.
+          ;; Can we issue a useful one-line summary in the
+          ;; message area (using FAILURES) if only one signal failed?
+          (proced-log-summary
+           signal
+           (format "%d of %d signal%s failed"
+                   (length failures) (length process-alist)
+                   (if (= 1 (length process-alist)) "" "s")))
+        (proced-success-message "Sent signal to" count)))
+    ;; final clean-up
+    (run-hooks 'proced-after-send-signal-hook)))
 
 ;; similar to `dired-why'
 (defun proced-why ()