]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
kill: add a feature decoding signal masks
authorMasatake YAMATO <yamato@redhat.com>
Sun, 30 Jun 2024 12:09:44 +0000 (21:09 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Wed, 3 Jul 2024 03:00:11 +0000 (12:00 +0900)
-l/--list option is extended to work well with the output of ps command:

   $ ps s $$
     UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    1000 1588189 0000000000000000 0000000000010000 0000000000384004 000000004b813efb S    pts/56     0:00 bash

   $ ./kill --list 0x000000004b813efb
   HUP
   INT
   ILL
   TRAP
   ABRT
   ...

If you know the pid you are interested in, you can skip running ps
comman with -d/--show-process-state option:

   $ ./kill --show-process-state $$
   Blocked: INT
   Ignored: TERM TSTP TTIN TTOU
   Caught: HUP INT PIPE ALRM CHLD WINCH

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
misc-utils/kill.1.adoc
misc-utils/kill.c

index 40ab0248d1e14cdce9c157ddc7c95ccb888ee3c2..5421fe16f5fde70633736a9902febce7b8b0942d 100644 (file)
@@ -19,8 +19,9 @@ kill - terminate a process
 
 *kill*  [**-**_signal_|*-s* _signal_|*-p*]  [*-q* _value_] [*-a*] [*--timeout* _milliseconds_ _signal_] [*--*] _pid_|_name_...
 
-*kill* *-l* [_number_] | *-L*
+*kill* *-l* [_number_|``0x``_sigmask_] | *-L*
 
+*kill* *-d* _pid_
 
 == DESCRIPTION
 
@@ -54,8 +55,20 @@ All processes invoked using this _name_ will be signaled.
 
 *-s*, *--signal* _signal_::
 The signal to send. It may be given as a name or a number.
-*-l*, *--list* [_number_]::
-Print a list of signal names, or convert the given signal number to a name. The signals can be found in _/usr/include/linux/signal.h_.
+*-l*, *--list* [_number_|``0x``_sigmask_]::
+Print a list of signal names, convert the given signal number to a name, or convert the given signal mask to names.
+The signals can be found in _/usr/include/linux/signal.h_.
++
+....
+$ ps s $$
+  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
+ 1000 1608069 0000000000000000 0000000000000002 0000000000384000 0000000008013003 Ss   pts/44     0:02 zsh
+$ kill -l 0x0000000000384000
+TERM
+TSTP
+TTIN
+TTOU
+....
 *-L*, *--table*::
 Similar to *-l*, but it will print signal names and their corresponding numbers.
 *-a*, *--all*::
@@ -81,6 +94,15 @@ As an example, the following command sends the signals *QUIT*, *TERM* and *KILL*
 kill --verbose --timeout 1000 TERM --timeout 1000 KILL \
         --signal QUIT 12345
 ....
+*-d*, *--show-process-state* _pid_::
+Decode signal related fields in /proc/_pid_/status.
++
+....
+$ kill -d $$
+Blocked: INT
+Ignored: TERM TSTP TTIN TTOU
+Caught: HUP INT PIPE ALRM CHLD WINCH
+....
 
 == EXIT STATUS
 
@@ -114,6 +136,8 @@ The original version was taken from BSD 4.4.
 *kill*(2),
 *sigqueue*(3),
 *signal*(7)
+*proc(5)*
+*proc_pid_status(5)*
 
 include::man-common/bugreports.adoc[]
 
index 2a42267aacad9efcc858228222fb5810cb447cd0..40f0c2f1888e40c56ff10c5e34f4b476bfe0f7a3 100644 (file)
@@ -46,6 +46,7 @@
 
 #include <ctype.h>             /* for isdigit() */
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -103,21 +104,84 @@ struct kill_control {
                verbose:1;
 };
 
-static void print_signal_name(int signum)
+static void print_signal_name(int signum, bool newline)
 {
        const char *name = signum_to_signame(signum);
+       const char *eol = newline? "\n": "";
 
        if (name) {
-               printf("%s\n", name);
+               printf("%s%s", name, eol);
                return;
        }
 #ifdef SIGRTMIN
        if (SIGRTMIN <= signum && signum <= SIGRTMAX) {
-               printf("RT%d\n", signum - SIGRTMIN);
+               printf("RT%d%s", signum - SIGRTMIN, eol);
                return;
        }
 #endif
-       printf("%d\n", signum);
+       printf("%d%s", signum, eol);
+}
+
+static void print_signal_mask(uint64_t sigmask, const char sep)
+{
+       for (size_t i = 0; i < sizeof(sigmask) * 8; i++) {
+               if ((((uint64_t)0x1) << i) & sigmask) {
+                       const int signum = i + 1;
+                       print_signal_name(signum, false);
+                       putchar(sep);
+               }
+       }
+}
+
+static void print_process_signal_state(pid_t pid)
+{
+       struct path_cxt *pc = NULL;
+       FILE *fp;
+       char buf[BUFSIZ];
+
+       static const struct sigfield {
+               char *key;
+               char *label;
+       } sigfields[] = {
+               { "SigPnd:\t", N_("Pending (thread)")  },
+               { "ShdPnd:\t", N_("Pending (process)") },
+               { "SigBlk:\t", N_("Blocked")           },
+               { "SigIgn:\t", N_("Ignored")           },
+               { "SigCgt:\t", N_("Caught")            },
+       };
+
+       pc = ul_new_procfs_path(pid, NULL);
+       if (!pc)
+               err(EXIT_FAILURE, _("failed to initialize procfs handler"));
+       fp = ul_path_fopen(pc, "r", "status");
+       if (!fp)
+               err(EXIT_FAILURE, _("cannot open /proc/%d/status"), pid);
+
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
+               for (size_t i = 0; i < ARRAY_SIZE(sigfields); i++) {
+                       const char *key = sigfields[i].key;
+                       size_t keylen = strlen(key);
+
+                       if (strncmp(buf, key, keylen) == 0) {
+                               char *val = buf + keylen;
+                               uint64_t sigmask;
+
+                               rtrim_whitespace((unsigned char*)val);
+                               if (ul_strtou64(val, &sigmask, 16) < 0) {
+                                       warnx( _("unexpected sigmask format: %s (%s)"), val, key);
+                                       continue;
+                               }
+                               if (sigmask != 0) {
+                                       printf("%s: ", _(sigfields[i].label));
+                                       print_signal_mask(sigmask, ' ');
+                                       putchar('\n');
+                               }
+                       }
+               }
+       }
+
+       fclose(fp);
+       ul_unref_path(pc);
 }
 
 static void pretty_print_signal(FILE *fp, size_t term_width, size_t *lpos,
@@ -211,9 +275,13 @@ static void __attribute__((__noreturn__)) usage(void)
                "                        wait up to timeout and send follow-up signal\n"), out);
 #endif
        fputs(_(" -p, --pid              print pids without signaling them\n"), out);
-       fputs(_(" -l, --list[=<signal>]  list signal names, or convert a signal number to a name\n"), out);
+       fputs(_(" -l, --list[=<signal>|=0x<sigmask>]\n"
+               "                        list signal names, convert a signal number to a name,\n"
+               "                         or convrt a signal mask to names\n"), out);
        fputs(_(" -L, --table            list signal names and numbers\n"), out);
        fputs(_(" -r, --require-handler  do not send signal if signal handler is not present\n"), out);
+       fputs(_(" -d, --show-process-state <pid>\n"
+               "                        show signal related fields in /proc/<pid>/status\n"), out);
        fputs(_("     --verbose          print pids that will be signaled\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
@@ -286,24 +354,57 @@ static char **parse_arguments(int argc, char **argv, struct kill_control *ctl)
                                errx(EXIT_FAILURE, _("too many arguments"));
                        /* argc == 2, accept "kill -l $?" */
                        arg = argv[1];
+                       if (arg[0] == '0' && arg[1] == 'x') {
+                               uint64_t sigmask;
+                               if (ul_strtou64(arg + 2, &sigmask, 16) < 0)
+                                       errx(EXIT_FAILURE, _("invalid sigmask format: %s"), arg);
+                               print_signal_mask(sigmask, '\n');
+                               exit(EXIT_SUCCESS);
+                       }
                        if ((ctl->numsig = arg_to_signum(arg, 1)) < 0)
                                errx(EXIT_FAILURE, _("unknown signal: %s"),
                                     arg);
-                       print_signal_name(ctl->numsig);
+                       print_signal_name(ctl->numsig, true);
                        exit(EXIT_SUCCESS);
                }
                /* for compatibility with procps kill(1) */
                if (!strncmp(arg, "--list=", 7) || !strncmp(arg, "-l=", 3)) {
                        char *p = strchr(arg, '=') + 1;
+                       if (p[0] == '0' && p[1] == 'x') {
+                               uint64_t sigmask;
+                               if (ul_strtou64(p + 2, &sigmask, 16) < 0)
+                                       errx(EXIT_FAILURE, _("invalid sigmask format: %s"), p);
+                               print_signal_mask(sigmask, '\n');
+                               exit(EXIT_SUCCESS);
+                       }
                        if ((ctl->numsig = arg_to_signum(p, 1)) < 0)
                                errx(EXIT_FAILURE, _("unknown signal: %s"), p);
-                       print_signal_name(ctl->numsig);
+                       print_signal_name(ctl->numsig, true);
                        exit(EXIT_SUCCESS);
                }
                if (!strcmp(arg, "-L") || !strcmp(arg, "--table")) {
                        print_all_signals(stdout, 1);
                        exit(EXIT_SUCCESS);
                }
+               if (!strcmp(arg, "-d") || !strcmp(arg, "--show-process-state")) {
+                       pid_t pid;
+                       if (argc < 2)
+                               errx(EXIT_FAILURE, _("too few arguments"));
+                       if (2 < argc)
+                               errx(EXIT_FAILURE, _("too many arguments"));
+                       arg = argv[1];
+                       pid = strtopid_or_err(arg, _("invalid pid argument"));
+                       print_process_signal_state(pid);
+                       exit(EXIT_SUCCESS);
+               }
+               if (!strncmp(arg, "-d=", 3) || !strncmp(arg, "--show-process-state=", 21)) {
+                       pid_t pid;
+                       char *p = strchr(arg, '=') + 1;
+
+                       pid = strtopid_or_err(p, _("invalid pid argument"));
+                       print_process_signal_state((pid_t)pid);
+                       exit(EXIT_SUCCESS);
+               }
                if (!strcmp(arg, "-r") || !strcmp(arg, "--require-handler")) {
                        ctl->require_handler = 1;
                        continue;