|
|
2ff057 |
/** \ingroup rpmio
|
|
|
2ff057 |
* \file rpmio/rpmsq.c
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "system.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
#include <signal.h>
|
|
|
2ff057 |
#include <sys/signal.h>
|
|
|
2ff057 |
#include <errno.h>
|
|
|
2ff057 |
#include <stdio.h>
|
|
|
2ff057 |
#include <stdlib.h>
|
|
|
2ff057 |
#include <string.h>
|
|
|
2ff057 |
|
|
|
2ff057 |
#include <rpm/rpmsq.h>
|
|
|
2ff057 |
#include <rpm/rpmlog.h>
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "debug.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
static __thread int disableInterruptSafety;
|
|
|
2ff057 |
static __thread sigset_t rpmsqCaught;
|
|
|
2ff057 |
static __thread sigset_t rpmsqActive;
|
|
|
2ff057 |
|
|
|
2ff057 |
typedef struct rpmsig_s * rpmsig;
|
|
|
2ff057 |
|
|
|
2ff057 |
static void rpmsqIgn(int signum, siginfo_t *info, void *context)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static void rpmsqTerm(int signum, siginfo_t *info, void *context)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
if (info->si_pid == 0) {
|
|
|
2ff057 |
rpmlog(RPMLOG_DEBUG,
|
|
|
2ff057 |
"exiting on signal %d (killed by death, eh?)\n", signum);
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
int lvl = (signum == SIGPIPE) ? RPMLOG_DEBUG : RPMLOG_WARNING;
|
|
|
2ff057 |
rpmlog(lvl,
|
|
|
2ff057 |
_("exiting on signal %d from pid %d\n"), signum, info->si_pid);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
/* exit 128 + signum for compatibility with bash(1) */
|
|
|
2ff057 |
exit(128 + signum);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static struct rpmsig_s {
|
|
|
2ff057 |
int signum;
|
|
|
2ff057 |
rpmsqAction_t defhandler;
|
|
|
2ff057 |
rpmsqAction_t handler;
|
|
|
2ff057 |
siginfo_t siginfo;
|
|
|
2ff057 |
struct sigaction oact;
|
|
|
2ff057 |
} rpmsigTbl[] = {
|
|
|
2ff057 |
{ SIGINT, rpmsqTerm, NULL },
|
|
|
2ff057 |
{ SIGQUIT, rpmsqTerm, NULL },
|
|
|
2ff057 |
{ SIGHUP, rpmsqTerm, NULL },
|
|
|
2ff057 |
{ SIGTERM, rpmsqTerm, NULL },
|
|
|
2ff057 |
{ SIGPIPE, rpmsqTerm, NULL },
|
|
|
2ff057 |
{ -1, NULL, NULL },
|
|
|
2ff057 |
};
|
|
|
2ff057 |
|
|
|
2ff057 |
static int rpmsigGet(int signum, struct rpmsig_s **sig)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
|
|
|
2ff057 |
if (tbl->signum == signum) {
|
|
|
2ff057 |
*sig = tbl;
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
int rpmsqIsCaught(int signum)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
return sigismember(&rpmsqCaught, signum);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static void rpmsqHandler(int signum, siginfo_t * info, void * context)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
int save = errno;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (sigismember(&rpmsqActive, signum)) {
|
|
|
2ff057 |
if (!sigismember(&rpmsqCaught, signum)) {
|
|
|
2ff057 |
rpmsig sig = NULL;
|
|
|
2ff057 |
if (rpmsigGet(signum, &sig)) {
|
|
|
2ff057 |
(void) sigaddset(&rpmsqCaught, signum);
|
|
|
2ff057 |
memcpy(&sig->siginfo, info, sizeof(*info));
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
errno = save;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
rpmsqAction_t rpmsqSetAction(int signum, rpmsqAction_t handler)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
rpmsig sig = NULL;
|
|
|
2ff057 |
rpmsqAction_t oh = RPMSQ_ERR;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (rpmsigGet(signum, &sig)) {
|
|
|
2ff057 |
oh = sig->handler;
|
|
|
2ff057 |
sig->handler = (handler == RPMSQ_IGN) ? rpmsqIgn : handler;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
return oh;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
int rpmsqActivate(int state)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
sigset_t newMask, oldMask;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (disableInterruptSafety)
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
(void) sigfillset(&newMask);
|
|
|
2ff057 |
(void) pthread_sigmask(SIG_BLOCK, &newMask, &oldMask);
|
|
|
2ff057 |
|
|
|
2ff057 |
if (state) {
|
|
|
2ff057 |
struct sigaction sa;
|
|
|
2ff057 |
for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
|
|
|
2ff057 |
sigdelset(&rpmsqCaught, tbl->signum);
|
|
|
2ff057 |
memset(&tbl->siginfo, 0, sizeof(tbl->siginfo));
|
|
|
2ff057 |
|
|
|
2ff057 |
/* XXX Don't set a signal handler if already SIG_IGN */
|
|
|
2ff057 |
sigaction(tbl->signum, NULL, &tbl->oact);
|
|
|
2ff057 |
if (tbl->oact.sa_handler == SIG_IGN)
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
|
|
|
2ff057 |
sigemptyset (&sa.sa_mask);
|
|
|
2ff057 |
sa.sa_flags = SA_SIGINFO;
|
|
|
2ff057 |
sa.sa_sigaction = rpmsqHandler;
|
|
|
2ff057 |
if (sigaction(tbl->signum, &sa, &tbl->oact) == 0)
|
|
|
2ff057 |
sigaddset(&rpmsqActive, tbl->signum);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
|
|
|
2ff057 |
if (!sigismember(&rpmsqActive, tbl->signum))
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
if (sigaction(tbl->signum, &tbl->oact, NULL) == 0) {
|
|
|
2ff057 |
sigdelset(&rpmsqActive, tbl->signum);
|
|
|
2ff057 |
sigdelset(&rpmsqCaught, tbl->signum);
|
|
|
2ff057 |
memset(&tbl->siginfo, 0, sizeof(tbl->siginfo));
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
pthread_sigmask(SIG_SETMASK, &oldMask, NULL);
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
int rpmsqPoll(void)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
sigset_t newMask, oldMask;
|
|
|
2ff057 |
int n = 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* block all signals while processing the queue */
|
|
|
2ff057 |
(void) sigfillset(&newMask);
|
|
|
2ff057 |
(void) pthread_sigmask(SIG_BLOCK, &newMask, &oldMask);
|
|
|
2ff057 |
|
|
|
2ff057 |
for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
|
|
|
2ff057 |
/* honor blocked signals in polling too */
|
|
|
2ff057 |
if (sigismember(&oldMask, tbl->signum))
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
if (sigismember(&rpmsqCaught, tbl->signum)) {
|
|
|
2ff057 |
rpmsqAction_t handler = (tbl->handler != NULL) ? tbl->handler :
|
|
|
2ff057 |
tbl->defhandler;
|
|
|
2ff057 |
/* delete signal before running handler to prevent recursing */
|
|
|
2ff057 |
sigdelset(&rpmsqCaught, tbl->signum);
|
|
|
2ff057 |
handler(tbl->signum, &tbl->siginfo, NULL);
|
|
|
2ff057 |
memset(&tbl->siginfo, 0, sizeof(tbl->siginfo));
|
|
|
2ff057 |
n++;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
pthread_sigmask(SIG_SETMASK, &oldMask, NULL);
|
|
|
2ff057 |
return n;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
int rpmsqBlock(int op)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
static __thread sigset_t oldMask;
|
|
|
2ff057 |
static __thread int blocked = 0;
|
|
|
2ff057 |
sigset_t newMask;
|
|
|
2ff057 |
int ret = 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (op == SIG_BLOCK) {
|
|
|
2ff057 |
blocked++;
|
|
|
2ff057 |
if (blocked == 1) {
|
|
|
2ff057 |
sigfillset(&newMask);
|
|
|
2ff057 |
sigdelset(&newMask, SIGABRT);
|
|
|
2ff057 |
sigdelset(&newMask, SIGBUS);
|
|
|
2ff057 |
sigdelset(&newMask, SIGFPE);
|
|
|
2ff057 |
sigdelset(&newMask, SIGILL);
|
|
|
2ff057 |
sigdelset(&newMask, SIGSEGV);
|
|
|
2ff057 |
sigdelset(&newMask, SIGTSTP);
|
|
|
2ff057 |
ret = pthread_sigmask(SIG_BLOCK, &newMask, &oldMask);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
} else if (op == SIG_UNBLOCK) {
|
|
|
2ff057 |
blocked--;
|
|
|
2ff057 |
if (blocked == 0) {
|
|
|
2ff057 |
ret = pthread_sigmask(SIG_SETMASK, &oldMask, NULL);
|
|
|
2ff057 |
rpmsqPoll();
|
|
|
2ff057 |
} else if (blocked < 0) {
|
|
|
2ff057 |
blocked = 0;
|
|
|
2ff057 |
ret = -1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
return ret;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/** \ingroup rpmio
|
|
|
2ff057 |
*
|
|
|
2ff057 |
* By default, librpm will trap various unix signals such as SIGINT and SIGTERM,
|
|
|
2ff057 |
* in order to avoid process exit while locks are held or a transaction is being
|
|
|
2ff057 |
* performed. However, there exist tools that operate on non-running roots (traditionally
|
|
|
2ff057 |
* build systems such as mock), as well as deployment tools such as rpm-ostree.
|
|
|
2ff057 |
*
|
|
|
2ff057 |
* These tools are more robust against interruption - typically they
|
|
|
2ff057 |
* will just throw away the partially constructed root. This function
|
|
|
2ff057 |
* is designed for use by those tools, so an operator can happily
|
|
|
2ff057 |
* press Control-C.
|
|
|
2ff057 |
*
|
|
|
2ff057 |
* It's recommended to call this once only at process startup if this
|
|
|
2ff057 |
* behavior is desired (and to then avoid using librpm against "live"
|
|
|
2ff057 |
* databases), because currently signal handlers will not be retroactively
|
|
|
2ff057 |
* applied if a database is open.
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
void rpmsqSetInterruptSafety(int on)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
disableInterruptSafety = !on;
|
|
|
2ff057 |
}
|