zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: real-time signals support
@ 2024-02-24 22:01 Oliver Kiddle
  2024-02-25 22:44 ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Oliver Kiddle @ 2024-02-24 22:01 UTC (permalink / raw)
  To: Zsh workers

This adds support for POSIX real-time signals, covering kill, trap,
$signals and TRAP...() functions.

For consistency with ksh and bash, the naming is RTMIN, RTMIN+1 ...
RTMAX-1, RTMAX. This means that each signal has an alternate name
so on my system, RTMIN+31 is an alternate name for RTMAX-30. NetBSD's
sh uses RT0, RT1...

The patch also adds a -L option to kill similar to bash and ksh which
prints signals with their numbers as a table. On many systems, the
real-time signals are not numbered contiguously with the earlier
signals. Where they are (NetBSD and Solaris), zsh was already including
RTMIN.

Prior to this patch, kill -l uses the WIFSIGNALED and WTERMSIG
macros to convert a return status to the corresponding signal. But
zsh simply ORs the status with 0200 (128). So surely we need to
look for that instead? This alteration makes the behaviour more
consistent with bash. To test, try kill -l {0..260}

In the POSIX spec, SIGRTMIN/MAX are permitted to not be fixed constants, and
this is indeed the case on Linux. As far as I can tell this is because glibc
steals a couple of them for threading support. They don't appear to be
dynamically configurable. However, this does prevent the sigtrapped and
siglists arrays from having their size determined at compile-time. Where
numbers are not contiguous, I've not left a gap so some of the patch is
renaming to make the distinction between signal numbers and trap table indices
clear.

Oliver

diff --git a/Completion/Zsh/Command/_kill b/Completion/Zsh/Command/_kill
index 084cf42c8..3b5c02151 100644
--- a/Completion/Zsh/Command/_kill
+++ b/Completion/Zsh/Command/_kill
@@ -4,10 +4,11 @@ local curcontext="$curcontext" line state ret=1
 typeset -A opt_args
 
 _arguments -C \
-  '(-s -l 1)-n[specify signal number]:signal number' \
-  '(-l)-q[send the specified integer with the signal using sigqueue]:value' \
-  '(-n -l 1)-s[specify signal name]:signal:_signals -s' \
-  '(-n -s)-l[list signal names or numbers of specified signals]:*:signal:_signals' \
+  '(-s -l -L 1)-n[specify signal number]:signal number' \
+  '(-l -L)-q[send the specified integer with the signal using sigqueue]:value' \
+  '(-n -l -L 1)-s[specify signal name]:signal:_signals -s' \
+  '-l[list signal names or numbers of specified signals]:*:signal:_signals' \
+  '(- *)-L[list each signal and corresponding number]' \
   '(-n -s -l)1::signal:_signals -p -s' \
   '*:processes:->processes' && ret=0
   
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 784089594..6318053d8 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1144,7 +1144,8 @@ cindex(killing jobs)
 cindex(jobs, killing)
 xitem(tt(kill) [ tt(-s) var(signal_name) | tt(-n) var(signal_number) | \
 tt(-)var(sig) ] [ tt(-q) var(value) ] var(job) ...)
-item(tt(kill) tt(-l) [ var(sig) ... ])(
+xitem(tt(kill) tt(-l) [ var(sig) ... ])
+item(tt(kill) tt(-L))(
 Sends either tt(SIGTERM) or the specified signal to the given
 jobs or processes.
 Signals are given by number or by names, with or without the `tt(SIG)'
@@ -1158,7 +1159,8 @@ specified the signal names are listed.  Otherwise, for each
 var(sig) that is a name, the corresponding signal number is
 listed.  For each var(sig) that is a signal number or a number
 representing the exit status of a process which was terminated or
-stopped by a signal the name of the signal is printed.
+stopped by a signal the name of the signal is printed.  The final
+form with tt(-L) lists each signal name with its corresponding number.
 
 On some systems, alternative signal names are allowed for a few signals.
 Typical examples are tt(SIGCHLD) and tt(SIGCLD) or tt(SIGPOLL) and
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index a6fbe6723..8c5e67e70 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -953,7 +953,10 @@ has index 1, the signals are offset by 1 from the signal number
 used by the operating system.  For example, on typical Unix-like systems
 tt(HUP) is signal number 1, but is referred to as tt($signals[2]).  This
 is because of tt(EXIT) at position 1 in the array, which is used
-internally by zsh but is not known to the operating system.
+internally by zsh but is not known to the operating system. On many systems
+there is a block of reserved or unused signal numbers before the POSIX
+real-time signals so the array index can't be used as an accurate indicator
+of their signal number. Use, for example, tt(kill -l SIGRTMIN) instead.
 )
 vindex(TRY_BLOCK_ERROR)
 item(tt(TRY_BLOCK_ERROR) <S>)(
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index a05ea2fe4..7441c30b8 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -309,7 +309,7 @@ setfunction(char *name, char *val, int dis)
     shfunc_set_sticky(shf);
 
     if (!strncmp(name, "TRAP", 4) &&
-	(sn = getsignum(name + 4)) != -1) {
+	(sn = getsigidx(name + 4)) != -1) {
 	if (settrap(sn, NULL, ZSIG_FUNC)) {
 	    freeeprog(shf->funcdef);
 	    zfree(shf, sizeof(*shf));
diff --git a/Src/builtin.c b/Src/builtin.c
index dd352c146..7a851936c 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -3425,16 +3425,16 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    newsh->sticky = sticky_emulation_dup(shf->sticky, 0);
 	/* is newsh a signal trap? (adapted from exec.c) */
 	if (!strncmp(s, "TRAP", 4)) {
-	    int signum = getsignum(s + 4);
-	    if (signum != -1) {
-		if (settrap(signum, NULL, ZSIG_FUNC)) {
+	    int sigidx = getsigidx(s + 4);
+	    if (sigidx != -1) {
+		if (settrap(sigidx, NULL, ZSIG_FUNC)) {
 		    freeeprog(newsh->funcdef);
 		    dircache_set(&newsh->filename, NULL);
 		    zfree(newsh, sizeof(*newsh));
 		    return 1;
 		}
 		/* Remove any old node explicitly */
-		removetrapnode(signum);
+		removetrapnode(sigidx);
 	    }
 	}
 	shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node);
@@ -3713,15 +3713,15 @@ bin_functions(char *name, char **argv, Options ops, int func)
 		/* no flags, so just print */
 		printshfuncexpand(&shf->node, pflags, expand);
 	} else if (on & PM_UNDEFINED) {
-	    int signum = -1, ok = 1;
+	    int sigidx = -1, ok = 1;
 
 	    if (!strncmp(*argv, "TRAP", 4) &&
-		(signum = getsignum(*argv + 4)) != -1) {
+		(sigidx = getsigidx(*argv + 4)) != -1) {
 		/*
 		 * Because of the possibility of alternative names,
 		 * we must remove the trap explicitly.
 		 */
-		removetrapnode(signum);
+		removetrapnode(sigidx);
 	    }
 
 	    if (**argv == '/') {
@@ -3757,8 +3757,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    shfunc_set_sticky(shf);
 	    add_autoload_function(shf, *argv);
 
-	    if (signum != -1) {
-		if (settrap(signum, NULL, ZSIG_FUNC)) {
+	    if (sigidx != -1) {
+		if (settrap(sigidx, NULL, ZSIG_FUNC)) {
 		    shfunctab->removenode(shfunctab, *argv);
 		    shfunctab->freenode(&shf->node);
 		    returnval = 1;
@@ -7344,7 +7344,7 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     /* If given no arguments, list all currently-set traps */
     if (!*argv) {
 	queue_signals();
-	for (sig = 0; sig < VSIGCOUNT; sig++) {
+	for (sig = 0; sig < TRAPCOUNT; sig++) {
 	    if (sigtrapped[sig] & ZSIG_FUNC) {
 		HashNode hn;
 
@@ -7370,13 +7370,13 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 
     /* If we have a signal number, unset the specified *
      * signals.  With only -, remove all traps.        */
-    if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
+    if ((getsigidx(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
 	if (!*argv) {
-	    for (sig = 0; sig < VSIGCOUNT; sig++)
+	    for (sig = 0; sig < TRAPCOUNT; sig++)
 		unsettrap(sig);
 	} else {
 	    for (; *argv; argv++) {
-		sig = getsignum(*argv);
+		sig = getsigidx(*argv);
 		if (sig == -1) {
 		    zwarnnam(name, "undefined signal: %s", *argv);
 		    break;
@@ -7401,12 +7401,12 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	Eprog t;
 	int flags;
 
-	sig = getsignum(*argv);
+	sig = getsigidx(*argv);
 	if (sig == -1) {
 	    zwarnnam(name, "undefined signal: %s", *argv);
 	    break;
 	}
-	if (idigit(**argv) ||
+	if (idigit(**argv) || (sig >= VSIGCOUNT) ||
 	    !strcmp(sigs[sig], *argv) ||
 	    (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) {
 	    /* The signal was specified by number or by canonical name (with
diff --git a/Src/exec.c b/Src/exec.c
index 1565cb13e..f6ccbefe5 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -5427,7 +5427,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 	} else {
 	    /* is this shell function a signal trap? */
 	    if (!strncmp(s, "TRAP", 4) &&
-		(signum = getsignum(s + 4)) != -1) {
+		(signum = getsigidx(s + 4)) != -1) {
 		if (settrap(signum, NULL, ZSIG_FUNC)) {
 		    freeeprog(shf->funcdef);
 		    dircache_set(&shf->filename, NULL);
diff --git a/Src/hashtable.c b/Src/hashtable.c
index bb165505e..75b06c4ad 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -836,10 +836,10 @@ static HashNode
 removeshfuncnode(UNUSED(HashTable ht), const char *nam)
 {
     HashNode hn;
-    int signum;
+    int sigidx;
 
-    if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1)
-	hn = removetrap(signum);
+    if (!strncmp(nam, "TRAP", 4) && (sigidx = getsigidx(nam + 4)) != -1)
+	hn = removetrap(sigidx);
     else
 	hn = removehashnode(shfunctab, nam);
 
@@ -856,10 +856,10 @@ disableshfuncnode(HashNode hn, UNUSED(int flags))
 {
     hn->flags |= DISABLED;
     if (!strncmp(hn->nam, "TRAP", 4)) {
-	int signum = getsignum(hn->nam + 4);
-	if (signum != -1) {
-	    sigtrapped[signum] &= ~ZSIG_FUNC;
-	    unsettrap(signum);
+	int sigidx = getsigidx(hn->nam + 4);
+	if (sigidx != -1) {
+	    sigtrapped[sigidx] &= ~ZSIG_FUNC;
+	    unsettrap(sigidx);
 	}
     }
 }
@@ -876,9 +876,9 @@ enableshfuncnode(HashNode hn, UNUSED(int flags))
 
     shf->node.flags &= ~DISABLED;
     if (!strncmp(shf->node.nam, "TRAP", 4)) {
-	int signum = getsignum(shf->node.nam + 4);
-	if (signum != -1) {
-	    settrap(signum, NULL, ZSIG_FUNC);
+	int sigidx = getsigidx(shf->node.nam + 4);
+	if (sigidx != -1) {
+	    settrap(sigidx, NULL, ZSIG_FUNC);
 	}
     }
 }
diff --git a/Src/init.c b/Src/init.c
index 83b79d3d4..ec21521b1 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1382,6 +1382,9 @@ setupshin(char *runscript)
 void
 init_signals(void)
 {
+    sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int));
+    siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog));
+
     if (interact) {
 	int i;
 	signal_setmask(signal_mask(0));
diff --git a/Src/jobs.c b/Src/jobs.c
index 118c5e61b..49decc661 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -1073,6 +1073,21 @@ should_report_time(Job j)
     return 0;
 }
 
+/**/
+char *
+sigmsg(int sig)
+{
+    static char *unknown = "unknown signal";
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    static char rtmsg[] = "real-time event XXX";
+    if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+	sprintf(rtmsg + sizeof(rtmsg) - 4, "%u", sig - SIGRTMIN + 1);
+	return rtmsg;
+    }
+#endif
+    return sig <= SIGCOUNT ? sig_msg[sig] : unknown;
+}
+
 /* !(lng & 3) means jobs    *
  *  (lng & 1) means jobs -l *
  *  (lng & 2) means jobs -p
@@ -2694,7 +2709,7 @@ static const struct {
 int
 bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
-    int sig = SIGTERM;
+    int status, sig = SIGTERM;
     int returnval = 0;
 #ifdef HAVE_SIGQUEUE
     union sigval sigqueue_info;
@@ -2740,23 +2755,29 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
 		if (argv[1]) {
 		    while (*++argv) {
-			sig = zstrtol(*argv, &signame, 10);
+			status = zstrtol(*argv, &signame, 10);
 			if (signame == *argv) {
+			    signame = casemodify(signame, CASMOD_UPPER);
 			    if (!strncmp(signame, "SIG", 3))
 				signame += 3;
 			    for (sig = 1; sig <= SIGCOUNT; sig++)
-				if (!strcasecmp(sigs[sig], signame))
+				if (!strcmp(sigs[sig], signame))
 				    break;
 			    if (sig > SIGCOUNT) {
 				int i;
 
 				for (i = 0; alt_sigs[i].name; i++)
-				    if (!strcasecmp(alt_sigs[i].name, signame))
+				    if (!strcmp(alt_sigs[i].name, signame))
 				    {
 					sig = alt_sigs[i].num;
 					break;
 				    }
 			    }
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+			    if (sig > SIGCOUNT && (sig = rtsigno(signame))) {
+				printf("%d\n", sig);
+			    } else
+#endif
 			    if (sig > SIGCOUNT) {
 				zwarnnam(nam, "unknown signal: SIG%s",
 					 signame);
@@ -2769,14 +2790,15 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 					 signame);
 				returnval++;
 			    } else {
-				if (WIFSIGNALED(sig))
-				    sig = WTERMSIG(sig);
-				else if (WIFSTOPPED(sig))
-				    sig = WSTOPSIG(sig);
+				sig = status & ~0200;
 				if (1 <= sig && sig <= SIGCOUNT)
 				    printf("%s\n", sigs[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+				else if ((signame = rtsigname(sig, 0)))
+				    printf("%s\n", signame);
+#endif
 				else
-				    printf("%d\n", sig);
+				    printf("%d\n", status);
 			    }
 			}
 		    }
@@ -2785,10 +2807,42 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 		printf("%s", sigs[1]);
 		for (sig = 2; sig <= SIGCOUNT; sig++)
 		    printf(" %s", sigs[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+		for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+		    printf(" %s", rtsigname(sig, 0));
+#endif
 		putchar('\n');
 		return 0;
 	    }
 
+	    /* with argument "-L" list signals with their numbers in a table */
+	    if ((*argv)[1] == 'L' && (*argv)[2] == '\0') {
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+		const int width = SIGRTMAX >= 100 ? 3 : 2;
+#else
+		const int width = SIGCOUNT >= 100 ? 3 : 2;
+#endif
+		for (sig = 1; sig < SIGCOUNT
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+			+ 1
+#endif
+			; sig++)
+		{
+		    printf("%*d) %-10s%c", width, sig, sigs[sig],
+			    sig % 5 ? ' ' : '\n');
+		}
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+		for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) {
+		    printf("%*d) %-10s%c", width, sig, rtsigname(sig, 0),
+			    (sig - SIGRTMIN + SIGCOUNT + 1) % 5 ? ' ' : '\n');
+		}
+		printf("%*d) RTMAX\n", width, sig);
+#else
+		printf("%*d) %s\n", width, sig, sigs[sig]);
+#endif
+		return 0;
+	    }
+
     	    if ((*argv)[1] == 'n' && (*argv)[2] == '\0') {
 	    	char *endp;
 
@@ -2833,9 +2887,13 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 			    break;
 			}
 		}
-		if (sig > SIGCOUNT) {
+		if (sig > SIGCOUNT
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+			&& !(sig = rtsigno(signame))
+#endif
+			) {
 		    zwarnnam(nam, "unknown signal: SIG%s", signame);
-		    zwarnnam(nam, "type kill -l for a list of signals");
+		    zwarnnam(nam, "type kill -L for a list of signals");
 		    return 1;
 		}
 	    }
@@ -2916,18 +2974,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 
     return returnval < 126 ? returnval : 1;
 }
-/* Get a signal number from a string */
+
+/* Get index into table of traps from a string describing a signal */
 
 /**/
 mod_export int
-getsignum(const char *s)
+getsigidx(const char *s)
 {
     int x, i;
 
     /* check for a signal specified by number */
     x = atoi(s);
-    if (idigit(*s) && x >= 0 && x < VSIGCOUNT)
-	return x;
+    if (idigit(*s) && x >= 0)
+	return SIGIDX(x);
 
     /* search for signal by name */
     if (!strncmp(s, "SIG", 3))
@@ -2943,11 +3002,16 @@ getsignum(const char *s)
 	    return alt_sigs[i].num;
     }
 
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if ((x = rtsigno(s)))
+	return SIGIDX(x);
+#endif
+
     /* no matching signal */
     return -1;
 }
 
-/* Get the name for a signal. */
+/* Get the name for a signal given the index into the traps table. */
 
 /**/
 mod_export const char *
@@ -2961,6 +3025,11 @@ getsigname(int sig)
 		return alt_sigs[i].name;
     }
     else
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if (sig >= VSIGCOUNT)
+	return rtsigname(SIGNUM(sig), 0);
+    else
+#endif
 	return sigs[sig];
 
     /* shouldn't reach here */
@@ -2985,10 +3054,22 @@ gettrapnode(int sig, int ignoredisable)
     else
 	getptr = shfunctab->getnode;
 
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if (sig >= VSIGCOUNT)
+	sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 0));
+    else
+#endif
     sprintf(fname, "TRAP%s", sigs[sig]);
     if ((hn = getptr(shfunctab, fname)))
 	return hn;
 
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if (sig >= VSIGCOUNT) {
+	sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 1));
+	return getptr(shfunctab, fname);
+    }
+#endif
+
     for (i = 0; alt_sigs[i].name; i++) {
 	if (alt_sigs[i].num == sig) {
 	    sprintf(fname, "TRAP%s", alt_sigs[i].name);
diff --git a/Src/params.c b/Src/params.c
index 225acb8a1..7c5e9d8ff 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -946,8 +946,18 @@ createparamtable(void)
     setsparam("ZSH_ARGZERO", ztrdup(posixzero));
     setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
     setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
-    setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
-    for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
+    setaparam("signals", sigptr = zalloc((TRAPCOUNT + 1) * sizeof(char *)));
+    t = sigs;
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    while (t - sigs <= SIGCOUNT)
+	*sigptr++ = ztrdup_metafy(*t++);
+    {
+	int sig;
+	for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+	    *sigptr++ = ztrdup_metafy(rtsigname(sig, 0));
+    }
+#endif
+    while ((*sigptr++ = ztrdup_metafy(*t++))) /* empty */ ;
 
     noerrs = 0;
 }
diff --git a/Src/signals.c b/Src/signals.c
index b1a843e2c..d28853ea6 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -31,10 +31,12 @@
 #include "signals.pro"
  
 /* Array describing the state of each signal: an element contains *
- * 0 for the default action or some ZSIG_* flags ored together.   */
+ * 0 for the default action or some ZSIG_* flags ored together.   *
+ * Contains TRAPCOUNT elements but can't be allocated statically  *
+ * because that's a dynamic value on Linux                        */
 
 /**/
-mod_export int sigtrapped[VSIGCOUNT];
+mod_export int *sigtrapped;
 
 /*
  * Trap programme lists for each signal.
@@ -48,7 +50,7 @@ mod_export int sigtrapped[VSIGCOUNT];
  */
 
 /**/
-mod_export Eprog siglists[VSIGCOUNT];
+mod_export Eprog *siglists;
 
 /* Total count of trapped signals */
 
@@ -892,7 +894,7 @@ dosavetrap(int sig, int level)
  * Set a trap:  note this does not handle manipulation of
  * the function table for TRAPNAL functions.
  *
- * sig is the signal number.
+ * sig is index into the table of trapped signals.
  *
  * l is the list to be eval'd for a trap defined with the "trap"
  * builtin and should be NULL for a function trap.
@@ -931,6 +933,10 @@ settrap(int sig, Eprog l, int flags)
 #endif
             sig != SIGCHLD)
             signal_ignore(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+	else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+	    signal_ignore(SIGNUM(sig));
+#endif
     } else {
 	nsigtrapped++;
         sigtrapped[sig] = ZSIG_TRAPPED;
@@ -940,6 +946,10 @@ settrap(int sig, Eprog l, int flags)
 #endif
             sig != SIGCHLD)
             install_handler(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+	if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+	    install_handler(SIGNUM(sig));
+#endif
     }
     sigtrapped[sig] |= flags;
     /*
@@ -1019,6 +1029,11 @@ removetrap(int sig)
 #endif
              sig != SIGCHLD)
         signal_default(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+	    signal_default(SIGNUM(sig));
+#endif
+
     if (sig == SIGEXIT)
 	exit_trap_posix = 0;
 
@@ -1172,7 +1187,7 @@ endtrapscope(void)
 static int
 handletrap(int sig)
 {
-    if (!sigtrapped[sig])
+    if (!sigtrapped[SIGIDX(sig)])
 	return 0;
 
     if (trap_queueing_enabled)
@@ -1189,7 +1204,7 @@ handletrap(int sig)
 	return 1;
     }
 
-    dotrap(sig);
+    dotrap(SIGIDX(sig));
 
     if (sig == SIGALRM)
     {
@@ -1481,3 +1496,60 @@ dotrap(int sig)
 
     restore_queue_signals(q);
 }
+
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+
+/* Realtime signals, these are a contiguous block that can
+ * be separated from the other signals with an unused gap. */
+
+/**/
+int
+rtsigno(const char* signame)
+{
+    const int maxofs = SIGRTMAX - SIGRTMIN;
+    const char *end = signame + 5;
+    int offset;
+    struct rtdir { int sig; int dir; char op; } x = { 0, 0, 0 };
+    if (!strncmp(signame, "RTMIN", 5)) {
+	x = (struct rtdir) { SIGRTMIN, 1, '+' };
+    } else if (!strncmp(signame, "RTMAX", 5)) {
+	x = (struct rtdir) { SIGRTMAX, -1, '-' };
+    } else
+	return 0;
+
+    if (signame[5] == x.op) {
+	if ((offset = strtol(signame + 6, (char **) &end, 10)) > maxofs)
+	    return 0;
+	x.sig += offset * x.dir;
+    }
+    if (*end)
+	return 0;
+
+    return x.sig;
+}
+
+/**/
+char *
+rtsigname(int signo, int alt)
+{
+    char* buf = (char *) zhalloc(10);
+    int minofs = signo - SIGRTMIN;
+    int maxofs = SIGRTMAX - signo;
+    int offset;
+    int form = alt ^ (maxofs < minofs);
+
+    if (signo < SIGRTMIN || signo > SIGRTMAX)
+	return NULL;
+
+    strcpy(buf, "RT");
+    strcpy(buf+2, form ? "MAX-" : "MIN+");
+    offset = form ? maxofs : minofs;
+    if (offset) {
+	snprintf(buf + 6, 4, "%d", offset);
+    } else {
+	buf[5] = '\0';
+    }
+    return buf;
+}
+
+#endif
diff --git a/Src/signals.h b/Src/signals.h
index 41ac88cce..391f11fed 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -36,6 +36,15 @@
 #define SIGZERR   (SIGCOUNT+1)
 #define SIGDEBUG  (SIGCOUNT+2)
 #define VSIGCOUNT (SIGCOUNT+3)
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+# define TRAPCOUNT (VSIGCOUNT + SIGRTMAX - SIGRTMIN + 1)
+# define SIGNUM(x) ((x) >= VSIGCOUNT ? (x) - VSIGCOUNT + SIGRTMIN : (x))
+# define SIGIDX(x) ((x) >= SIGRTMIN && (x) <= SIGRTMAX ? (x) - SIGRTMIN + VSIGCOUNT : (x))
+#else
+# define TRAPCOUNT VSIGCOUNT
+# define SIGNUM(x) (x)
+# define SIGIDX(x) (x)
+#endif
 #define SIGEXIT    0
 
 #ifdef SV_BSDSIG
diff --git a/Src/signames2.awk b/Src/signames2.awk
index 4d1557cd8..5738030c6 100644
--- a/Src/signames2.awk
+++ b/Src/signames2.awk
@@ -15,7 +15,7 @@
     if (signam == "CHLD" && sig[signum] == "CLD")  sig[signum] = ""
     if (signam == "POLL" && sig[signum] == "IO")   sig[signum] = ""
     if (signam == "ABRT" && sig[signum] == "IOT")  sig[signum] = ""
-    if (sig[signum] == "") {
+    if (signam !~ /RTM(IN|AX)/ && sig[signum] == "") {
 	sig[signum] = signam
 	if (0 + max < 0 + signum && signum < 60)
 	    max = signum
@@ -66,10 +66,6 @@ END {
     printf "#include %czsh.mdh%c\n", 34, 34
     printf "\n"
     printf "/**/\n"
-    printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]"
-    printf " : %c%s%c)", 34, "unknown signal", 34
-    printf "\n"
-    printf "/**/\n"
     printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n"
     printf "\t%c%s%c,\n", 34, "done", 34
 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: real-time signals support
  2024-02-24 22:01 PATCH: real-time signals support Oliver Kiddle
@ 2024-02-25 22:44 ` Bart Schaefer
  2024-02-27 19:25   ` Oliver Kiddle
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2024-02-25 22:44 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: Zsh workers

On Sat, Feb 24, 2024 at 2:01 PM Oliver Kiddle <opk@zsh.org> wrote:
>
> For consistency with ksh and bash, the naming is RTMIN, RTMIN+1 ...
> RTMAX-1, RTMAX. This means that each signal has an alternate name
> so on my system, RTMIN+31 is an alternate name for RTMAX-30.
>
> The patch also adds a -L option to kill similar to bash and ksh which
> prints signals with their numbers as a table.

It looks as if "kill -L" chooses the name with the smallest "absolute value"?

Some suggestions RE -L:
* Drop the right-paren after the signal number, so this output is easily parsed.
* List in columns first rather than across rows.
* Recognize terminal width and change number of columns.
* Print one column (of number-name pairs, obviously) if output is not
a terminal.

I know the "N) NAME" format is bash-reminiscent, but bash also puts
the "SIG" prefix on every name which this already does not.  Anyway
two of those three suggestions are clearly a moderate amount of extra
effort, so just throwing out there.

> On many systems, the
> real-time signals are not numbered contiguously with the earlier
> signals.

Does this mean we should consider making $signals an associative array
like $history?  That would remove the issue that $signals[2] = HUP,
etc., but maybe that's a backward-compat problem.

> Prior to this patch, kill -l uses the WIFSIGNALED and WTERMSIG
> macros to convert a return status to the corresponding signal. But
> zsh simply ORs the status with 0200 (128). So surely we need to
> look for that instead?

Does that mean that Functions/Misc/zargs needs to change the way it
interprets exit status?  There is a presumption that 129+ is "killed
by signal", that 255 is special, and that otherwise 254 is the largest
possible exit status.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: real-time signals support
  2024-02-25 22:44 ` Bart Schaefer
@ 2024-02-27 19:25   ` Oliver Kiddle
  2024-02-27 21:12     ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Oliver Kiddle @ 2024-02-27 19:25 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh workers

Bart Schaefer wrote:
> It looks as if "kill -L" chooses the name with the smallest "absolute value"?

Yes. Following the precedent of ksh and bash.

> Some suggestions RE -L:

I'm not especially attached to any particular format here. If you or
someone else wants to change it I'm not going to object. I copied what
bash and ksh do. The patch below handles the two easy suggestions but
I'll wait for confirmation before applying. Is parsing kill -L output
really that useful?

> * Drop the right-paren after the signal number, so this output is easily parsed.
> * List in columns first rather than across rows.

I normally prefer columns first but with the RT signals, I think I
actually prefer them at the bottom where they're easier to ignore.

Ksh does this. I suspect they already have a general function for laying
out tables. Perhaps we should factor out the part of print -C which does
this so that it can be reused.

> * Recognize terminal width and change number of columns.

Ksh also does this. Not entirely to my taste on a wide terminal but I
usually like my terminal windows narrow and tall anyway.

> * Print one column (of number-name pairs, obviously) if output is not
> a terminal.

We don't adjust output for non-terminals from any other builtin. This
can be annoying when you want to send the terminal form down a pipeline.

> I know the "N) NAME" format is bash-reminiscent, but bash also puts
> the "SIG" prefix on every name which this already does not.  Anyway
> two of those three suggestions are clearly a moderate amount of extra
> effort, so just throwing out there.

I couldn't see the point of the "SIG" prefix - adds no information but
makes it harder to spot particular values.

> Does this mean we should consider making $signals an associative array
> like $history?  That would remove the issue that $signals[2] = HUP,
> etc., but maybe that's a backward-compat problem.

It's probably too late to change. For converting between the two forms,
${ kill -l $sig } is more useful and the array works well enough as a
source for completion. Incidentially, trying to use kill -l in that
manner, it becomes apparent why the `...` and $(...) forms strip
trailing newlines. Given that it aids compatibility with bash/ksh etc
and updating old zsh scripts that use $(...) I'm not too sure that it
preserving newlines is the most helpful behaviour. Parameter flags don't
appear to work with the new form so adding one of those isn't an option.

> Does that mean that Functions/Misc/zargs needs to change the way it
> interprets exit status?  There is a presumption that 129+ is "killed
> by signal", that 255 is special, and that otherwise 254 is the largest
> possible exit status.

A greater range of values appear to be open to shell functions but
otherwise, that sounds about right. There's also negative values.

Oliver

diff --git a/Src/jobs.c b/Src/jobs.c
index 49decc661..bc9987513 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -2822,23 +2822,25 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 #else
 		const int width = SIGCOUNT >= 100 ? 3 : 2;
 #endif
+		const int cols = isatty(1) ? 5 : 1;
+
 		for (sig = 1; sig < SIGCOUNT
 #if defined(SIGRTMIN) && defined(SIGRTMAX)
 			+ 1
 #endif
 			; sig++)
 		{
-		    printf("%*d) %-10s%c", width, sig, sigs[sig],
-			    sig % 5 ? ' ' : '\n');
+		    printf("%*d %-10s%c", width, sig, sigs[sig],
+			    sig % cols ? ' ' : '\n');
 		}
 #if defined(SIGRTMIN) && defined(SIGRTMAX)
 		for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) {
-		    printf("%*d) %-10s%c", width, sig, rtsigname(sig, 0),
-			    (sig - SIGRTMIN + SIGCOUNT + 1) % 5 ? ' ' : '\n');
+		    printf("%*d %-10s%c", width, sig, rtsigname(sig, 0),
+			    (sig - SIGRTMIN + SIGCOUNT + 1) % cols ? ' ' : '\n');
 		}
-		printf("%*d) RTMAX\n", width, sig);
+		printf("%*d RTMAX\n", width, sig);
 #else
-		printf("%*d) %s\n", width, sig, sigs[sig]);
+		printf("%*d %s\n", width, sig, sigs[sig]);
 #endif
 		return 0;
 	    }


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: real-time signals support
  2024-02-27 19:25   ` Oliver Kiddle
@ 2024-02-27 21:12     ` Bart Schaefer
  2024-02-27 23:51       ` Oliver Kiddle
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2024-02-27 21:12 UTC (permalink / raw)
  To: Zsh hackers list

On Tue, Feb 27, 2024 at 11:25 AM Oliver Kiddle <opk@zsh.org> wrote:
>
> Bart Schaefer wrote:
> > Some suggestions RE -L:
>
> I'm not especially attached to any particular format here. If you or
> someone else wants to change it I'm not going to object.

I'm not strongly attached either, just used to seeing numbered lists
go down the "page".

> > * Drop the right-paren after the signal number, so this output is easily parsed.
>
> Is parsing kill -L output really that useful?

I was thinking in terms of populating a hash-format of $signals given
that the array-format default is probably not going away.  With the
paren after the numbers I ended up with:

 typeset -A sigs=( ${=${ printf "%d %s\n" ${=${ printf "(%s %s\n"
${=${ kill -L }} }} }} )

Anyway all of these were ideas that popped up as I was reading and
thinking about using the output after applying the patch, keep or
discard as preferred.

> > * List in columns first rather than across rows.
>
> I normally prefer columns first but with the RT signals, I think I
> actually prefer them at the bottom where they're easier to ignore.

Fair enough.

> > * Recognize terminal width and change number of columns.
>
> Ksh also does this. Not entirely to my taste on a wide terminal but I
> usually like my terminal windows narrow and tall anyway.

The main reason is for terminals narrower than 80-ish columns; wider ones, meh.

> > * Print one column (of number-name pairs, obviously) if output is not
> > a terminal.
>
> We don't adjust output for non-terminals from any other builtin.

Also reasonable.  Fixable by asserting a COLUMNS= value if the width
is recognized.

> I couldn't see the point of the "SIG" prefix - adds no information but
> makes it harder to spot particular values.

Exactly; my point was that we're already not a perfect copy of
bash/ksh here, so going further afield is not precluded.

> A greater range of values appear to be open to shell functions but
> otherwise, that sounds about right. There's also negative values.

There would never be a useful negative exit status, would there?
Negative signals translate into the absolute value sent to the process
group leader, right?

% (exit -2)
% echo $?
254


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: real-time signals support
  2024-02-27 21:12     ` Bart Schaefer
@ 2024-02-27 23:51       ` Oliver Kiddle
  2024-02-28  2:17         ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Oliver Kiddle @ 2024-02-27 23:51 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

Bart Schaefer wrote:
> I was thinking in terms of populating a hash-format of $signals given
> that the array-format default is probably not going away.  With the
> paren after the numbers I ended up with:
>
>  typeset -A sigs=( ${=${ printf "%d %s\n" ${=${ printf "(%s %s\n"
> ${=${ kill -L }} }} }} )

I'll grant you that isn't trivial even if there may be shorter
approaches.

> The main reason is for terminals narrower than 80-ish columns; wider ones, meh.

How about limiting it to a maximum of six columns like so?

		const int cols = zterm_columns >= 30 ?
		    (zterm_columns < 90 ? zterm_columns / 15 : 6) : 1;

Without the isatty(1) test it can still be piped in column form or you
can use COLUMNS=1 to force single-column output. Or the `isatty(1) &&`
can be added back in?

> > A greater range of values appear to be open to shell functions but
> > otherwise, that sounds about right. There's also negative values.
>
> There would never be a useful negative exit status, would there?

Except for functions (and maybe builtins). Don't think I've ever used
that.

From an external programme, it seems any value is received modulo 256.

> Negative signals translate into the absolute value sent to the process
> group leader, right?

Isn't that negative pids? Or do either have that effect?

Oliver


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PATCH: real-time signals support
  2024-02-27 23:51       ` Oliver Kiddle
@ 2024-02-28  2:17         ` Bart Schaefer
  0 siblings, 0 replies; 6+ messages in thread
From: Bart Schaefer @ 2024-02-28  2:17 UTC (permalink / raw)
  To: Zsh hackers list

On Tue, Feb 27, 2024 at 3:51 PM Oliver Kiddle <opk@zsh.org> wrote:
>
> Bart Schaefer wrote:
> > The main reason is for terminals narrower than 80-ish columns; wider ones, meh.
>
> How about limiting it to a maximum of six columns like so?

Works for me.

> Without the isatty(1) test it can still be piped in column form or you
> can use COLUMNS=1 to force single-column output.

Dandy.

> > Negative signals translate into the absolute value sent to the process
> > group leader, right?
>
> Isn't that negative pids?

Oh, yes, of course.  I suspect a negative signal number would just
produce an EINVAL error.


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-02-28  2:18 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-24 22:01 PATCH: real-time signals support Oliver Kiddle
2024-02-25 22:44 ` Bart Schaefer
2024-02-27 19:25   ` Oliver Kiddle
2024-02-27 21:12     ` Bart Schaefer
2024-02-27 23:51       ` Oliver Kiddle
2024-02-28  2:17         ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).