csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
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
}