mirror of
https://gitlab.com/procps-ng/procps.git
synced 2024-11-27 03:54:06 +08:00
5b248c0675
The original idea of using signal_number_to_name() was ok when the calling function was in ps, but problematic in the library because: * There needed to be a libtool and non-libtool built of local/signals.c * The function handled RT signals, but the calling function in the library does this too. * It needed to be an array of signal,name pairs because signals.c is two-way while we only need num->name in the library. * signal_number_to_name is called by signal handlers so less library calls the better. If sigabbrev_np() is available in libc, then the library should use that. This commit provides a back-up version of that function which is only used by libc's that can't provide. Greatly inspired by Peter Hutterer's work-around with some clever stringizing and concatenation in the preprocessor for same issue in Pipewire. References: commit7f56a9aea1
731f45ed50
https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html Signed-off-by: Craig Small <csmall@dropbear.xyz>
230 lines
6.2 KiB
C
230 lines
6.2 KiB
C
/*
|
|
* signames.c - Translate signal masks
|
|
*
|
|
* Copyright © 2023-2024 Craig Small <csmall@dropbear.xyz>
|
|
* Copyright © 2020 Luis Chamberlain <mcgrof@kernel.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <dirent.h>
|
|
#include <ctype.h>
|
|
#include <pwd.h>
|
|
#include <inttypes.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include "signals.h"
|
|
#include "misc.h"
|
|
#include "procps-private.h"
|
|
|
|
/* For libraries like musl */
|
|
#ifndef __SIGRTMIN
|
|
#define __SIGRTMIN SIGRTMIN
|
|
#endif
|
|
#ifndef __SIGRTMAX
|
|
#define __SIGRTMAX SIGRTMAX
|
|
#endif
|
|
|
|
#define SIGNAME_MAX 256
|
|
/*
|
|
* The actual list of unsupported signals varies by operating system. This
|
|
* program is Linux specific as it processes /proc/ for signal information and
|
|
* there is no generic way to extract each process signal information for each
|
|
* OS. This program also relies on Linux glibc defines to figure out which
|
|
* signals are reserved for use by libc and then which ones are real time
|
|
* specific.
|
|
*/
|
|
|
|
#if !HAVE_SIGABBREV_NP
|
|
static inline const char *sigabbrev_np(int sig)
|
|
{
|
|
#define SIGABBREV(a_) case SIG##a_: return #a_
|
|
switch(sig) {
|
|
SIGABBREV(HUP);
|
|
SIGABBREV(INT);
|
|
SIGABBREV(QUIT);
|
|
SIGABBREV(ILL);
|
|
SIGABBREV(TRAP);
|
|
SIGABBREV(ABRT);
|
|
SIGABBREV(BUS);
|
|
SIGABBREV(FPE);
|
|
SIGABBREV(KILL);
|
|
SIGABBREV(USR1);
|
|
SIGABBREV(SEGV);
|
|
SIGABBREV(USR2);
|
|
SIGABBREV(PIPE);
|
|
SIGABBREV(ALRM);
|
|
SIGABBREV(TERM);
|
|
#ifdef SIGSTKFLT
|
|
SIGABBREV(STKFLT);
|
|
#endif
|
|
SIGABBREV(CHLD);
|
|
SIGABBREV(CONT);
|
|
SIGABBREV(STOP);
|
|
SIGABBREV(TSTP);
|
|
SIGABBREV(TTIN);
|
|
SIGABBREV(TTOU);
|
|
SIGABBREV(URG);
|
|
SIGABBREV(XCPU);
|
|
SIGABBREV(XFSZ);
|
|
SIGABBREV(VTALRM);
|
|
SIGABBREV(PROF);
|
|
SIGABBREV(WINCH);
|
|
SIGABBREV(POLL);
|
|
#ifdef SIGPWR // absent in kFreeBSD (Debian #832148)
|
|
SIGABBREV(PWR);
|
|
#endif
|
|
SIGABBREV(SYS);
|
|
#ifdef SIGLOST // Hurd (gitlab#93)
|
|
SIGABBREV(LOST);
|
|
#endif
|
|
#if defined __sun || defined __SUN || defined __solaris__ || defined __SOLARIS__
|
|
case 0: return "EXIT";
|
|
#endif
|
|
#if defined _AIX || defined __AIX__ || defined __aix__
|
|
case 0: return "NULL";
|
|
#endif
|
|
};
|
|
#undef SIGABBREV
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* HAVE_SIGABBREV_NP */
|
|
/*
|
|
* As per glibc:
|
|
*
|
|
* A system that defines real time signals overrides __SIGRTMAX to be something
|
|
* other than __SIGRTMIN. This also means we can count on __SIGRTMIN being the
|
|
* first real time signal, meaning what Linux programs it for your architecture
|
|
* in the kernel. SIGRTMIN then will be the application specific first real
|
|
* time signal, that is, on top of libc. The values in between
|
|
*
|
|
* __SIGRTMIN .. SIGRTMIN
|
|
*
|
|
* are used by * libc, typically for helping threading implementation.
|
|
*/
|
|
static const char *sigstat_strsignal_abbrev(int sig, char *abbrev, size_t len)
|
|
{
|
|
memset(abbrev, '\0', len);
|
|
|
|
if (sig == 0 || sig >= NSIG) {
|
|
snprintf(abbrev, len, "BOGUS_%02d", sig - _NSIG);
|
|
return abbrev;
|
|
}
|
|
|
|
/*
|
|
* The standard lower signals we can count on this being the kernel
|
|
* specific SIGRTMIN.
|
|
*/
|
|
if (sig < __SIGRTMIN) {
|
|
const char *signame = NULL;
|
|
signame = sigabbrev_np(sig);
|
|
|
|
if (signame != NULL && signame[0] != '\0') {
|
|
strncpy(abbrev, signame, len);
|
|
return abbrev;
|
|
}
|
|
}
|
|
|
|
/* This means your system should *not* have realtime signals */
|
|
if (__SIGRTMAX == __SIGRTMIN) {
|
|
snprintf(abbrev, len, "INVALID_%02d", sig);
|
|
return abbrev;
|
|
}
|
|
|
|
/*
|
|
* If we're dealing with a libc real time signal start counting
|
|
* after libc's version of SIGRTMIN
|
|
*/
|
|
if (sig >= SIGRTMIN) {
|
|
if (sig == SIGRTMIN)
|
|
snprintf(abbrev, len, "RTMIN");
|
|
else if (sig == SIGRTMAX)
|
|
snprintf(abbrev, len, "RTMAX");
|
|
else
|
|
snprintf(abbrev, len, "RTMIN+%02d", sig - SIGRTMIN);
|
|
} else
|
|
snprintf(abbrev, len, "LIBC+%02d", sig - __SIGRTMIN);
|
|
|
|
return abbrev;
|
|
}
|
|
|
|
/*
|
|
* For instance SIGTERM is 15, but its actual mask value is
|
|
* 1 << (15-1) = 0x4000
|
|
*/
|
|
static uint64_t mask_sig_val_num(int signum)
|
|
{
|
|
return ((uint64_t) 1 << (signum -1));
|
|
}
|
|
|
|
PROCPS_EXPORT int procps_sigmask_names(
|
|
char *str,
|
|
size_t size,
|
|
const char *sigmask)
|
|
{
|
|
unsigned int i;
|
|
char abbrev[SIGNAME_MAX];
|
|
unsigned int n = 0;
|
|
char *c = str;
|
|
uint64_t mask, mask_in;
|
|
uint64_t test_val = 0;
|
|
|
|
if (str == NULL || sigmask == NULL || size == 0)
|
|
return -EINVAL;
|
|
|
|
if (1 != sscanf(sigmask, "%" PRIx64, &mask_in))
|
|
return -EINVAL;
|
|
mask = mask_in;
|
|
|
|
for (i=0; i < NSIG; i++) {
|
|
test_val = mask_sig_val_num(i);
|
|
if (test_val & mask) {
|
|
n = strlen(sigstat_strsignal_abbrev(i, abbrev, SIGNAME_MAX));
|
|
if (n+1 >= size) { // +1 for the '+'
|
|
strcpy(c, "+");
|
|
size -= 1;
|
|
c += 1;
|
|
break;
|
|
} else {
|
|
n = snprintf(c, size, (c==str)?"%s":",%s",
|
|
sigstat_strsignal_abbrev(i, abbrev,
|
|
SIGNAME_MAX));
|
|
size -= n;
|
|
c+=n;
|
|
}
|
|
}
|
|
}
|
|
if (c == str) {
|
|
n = snprintf(c, size, "%c", '-');
|
|
size -= n;
|
|
c += n;
|
|
}
|
|
return (int) (c-str);
|
|
}
|