1 /** \ingroup rpmio
2  * \file rpmio/rpmsq.c
3  */
4 
5 #include "system.h"
6 
7 #include <signal.h>
8 #include <sys/signal.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <rpm/rpmsq.h>
15 #include <rpm/rpmlog.h>
16 
17 #include "debug.h"
18 
19 static int disableInterruptSafety;
20 static sigset_t rpmsqCaught;
21 static sigset_t rpmsqActive;
22 
23 typedef struct rpmsig_s * rpmsig;
24 
rpmsqIgn(int signum,siginfo_t * info,void * context)25 static void rpmsqIgn(int signum, siginfo_t *info, void *context)
26 {
27 }
28 
rpmsqTerm(int signum,siginfo_t * info,void * context)29 static void rpmsqTerm(int signum, siginfo_t *info, void *context)
30 {
31     if (info->si_pid == 0) {
32 	rpmlog(RPMLOG_DEBUG,
33 		"exiting on signal %d (killed by death, eh?)\n", signum);
34     } else {
35 	int lvl = (signum == SIGPIPE) ? RPMLOG_DEBUG : RPMLOG_WARNING;
36 	rpmlog(lvl,
37 		_("exiting on signal %d from pid %d\n"), signum, info->si_pid);
38     }
39     /* exit 128 + signum for compatibility with bash(1) */
40     exit(128 + signum);
41 }
42 
43 static struct rpmsig_s {
44     int signum;
45     rpmsqAction_t defhandler;
46     rpmsqAction_t handler;
47     siginfo_t siginfo;
48     struct sigaction oact;
49 } rpmsigTbl[] = {
50     { SIGINT,	rpmsqTerm,	NULL },
51     { SIGQUIT,	rpmsqTerm,	NULL },
52     { SIGHUP,	rpmsqTerm,	NULL },
53     { SIGTERM,	rpmsqTerm,	NULL },
54     { SIGPIPE,	rpmsqTerm,	NULL },
55     { -1,	NULL,		NULL },
56 };
57 
rpmsigGet(int signum,struct rpmsig_s ** sig)58 static int rpmsigGet(int signum, struct rpmsig_s **sig)
59 {
60     for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
61 	if (tbl->signum == signum) {
62 	    *sig = tbl;
63 	    return 1;
64 	}
65     }
66     return 0;
67 }
68 
rpmsqIsCaught(int signum)69 int rpmsqIsCaught(int signum)
70 {
71     return sigismember(&rpmsqCaught, signum);
72 }
73 
rpmsqHandler(int signum,siginfo_t * info,void * context)74 static void rpmsqHandler(int signum, siginfo_t * info, void * context)
75 {
76     int save = errno;
77 
78     if (sigismember(&rpmsqActive, signum)) {
79 	if (!sigismember(&rpmsqCaught, signum)) {
80 	    rpmsig sig = NULL;
81 	    if (rpmsigGet(signum, &sig)) {
82 		(void) sigaddset(&rpmsqCaught, signum);
83 		memcpy(&sig->siginfo, info, sizeof(*info));
84 	    }
85 	}
86     }
87 
88     errno = save;
89 }
90 
rpmsqSetAction(int signum,rpmsqAction_t handler)91 rpmsqAction_t rpmsqSetAction(int signum, rpmsqAction_t handler)
92 {
93     rpmsig sig = NULL;
94     rpmsqAction_t oh = RPMSQ_ERR;
95 
96     if (rpmsigGet(signum, &sig)) {
97 	oh = sig->handler;
98 	sig->handler = (handler == RPMSQ_IGN) ? rpmsqIgn : handler;
99     }
100     return oh;
101 }
102 
rpmsqActivate(int state)103 int rpmsqActivate(int state)
104 {
105     sigset_t newMask, oldMask;
106 
107     if (disableInterruptSafety)
108       return 0;
109 
110     (void) sigfillset(&newMask);
111     (void) pthread_sigmask(SIG_BLOCK, &newMask, &oldMask);
112 
113     if (state) {
114 	struct sigaction sa;
115 	for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
116 	    sigdelset(&rpmsqCaught, tbl->signum);
117 	    memset(&tbl->siginfo, 0, sizeof(tbl->siginfo));
118 
119 	    /* XXX Don't set a signal handler if already SIG_IGN */
120 	    sigaction(tbl->signum, NULL, &tbl->oact);
121 	    if (tbl->oact.sa_handler == SIG_IGN)
122 		continue;
123 
124 	    sigemptyset (&sa.sa_mask);
125 	    sa.sa_flags = SA_SIGINFO;
126 	    sa.sa_sigaction = rpmsqHandler;
127 	    if (sigaction(tbl->signum, &sa, &tbl->oact) == 0)
128 		sigaddset(&rpmsqActive, tbl->signum);
129 	}
130     } else {
131 	for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
132 	    if (!sigismember(&rpmsqActive, tbl->signum))
133 		continue;
134 	    if (sigaction(tbl->signum, &tbl->oact, NULL) == 0) {
135 		sigdelset(&rpmsqActive, tbl->signum);
136 		sigdelset(&rpmsqCaught, tbl->signum);
137 		memset(&tbl->siginfo, 0, sizeof(tbl->siginfo));
138 	    }
139 	}
140     }
141     pthread_sigmask(SIG_SETMASK, &oldMask, NULL);
142     return 0;
143 }
144 
rpmsqPoll(void)145 int rpmsqPoll(void)
146 {
147     sigset_t newMask, oldMask;
148     int n = 0;
149 
150     /* block all signals while processing the queue */
151     (void) sigfillset(&newMask);
152     (void) pthread_sigmask(SIG_BLOCK, &newMask, &oldMask);
153 
154     for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
155 	/* honor blocked signals in polling too */
156 	if (sigismember(&oldMask, tbl->signum))
157 	    continue;
158 	if (sigismember(&rpmsqCaught, tbl->signum)) {
159 	    rpmsqAction_t handler = (tbl->handler != NULL) ? tbl->handler :
160 							     tbl->defhandler;
161 	    /* delete signal before running handler to prevent recursing */
162 	    sigdelset(&rpmsqCaught, tbl->signum);
163 	    handler(tbl->signum, &tbl->siginfo, NULL);
164 	    memset(&tbl->siginfo, 0, sizeof(tbl->siginfo));
165 	    n++;
166 	}
167     }
168     pthread_sigmask(SIG_SETMASK, &oldMask, NULL);
169     return n;
170 }
171 
rpmsqBlock(int op)172 int rpmsqBlock(int op)
173 {
174     static sigset_t oldMask;
175     static int blocked = 0;
176     sigset_t newMask;
177     int ret = 0;
178 
179     if (op == SIG_BLOCK) {
180 	blocked++;
181 	if (blocked == 1) {
182 	    sigfillset(&newMask);
183 	    sigdelset(&newMask, SIGABRT);
184 	    sigdelset(&newMask, SIGBUS);
185 	    sigdelset(&newMask, SIGFPE);
186 	    sigdelset(&newMask, SIGILL);
187 	    sigdelset(&newMask, SIGSEGV);
188 	    sigdelset(&newMask, SIGTSTP);
189 	    ret = pthread_sigmask(SIG_BLOCK, &newMask, &oldMask);
190 	}
191     } else if (op == SIG_UNBLOCK) {
192 	blocked--;
193 	if (blocked == 0) {
194 	    ret = pthread_sigmask(SIG_SETMASK, &oldMask, NULL);
195 	    rpmsqPoll();
196 	} else if (blocked < 0) {
197 	    blocked = 0;
198 	    ret = -1;
199 	}
200     }
201 
202     return ret;
203 }
204 
205 /** \ingroup rpmio
206  *
207  * By default, librpm will trap various unix signals such as SIGINT and SIGTERM,
208  * in order to avoid process exit while locks are held or a transaction is being
209  * performed.  However, there exist tools that operate on non-running roots (traditionally
210  * build systems such as mock), as well as deployment tools such as rpm-ostree.
211  *
212  * These tools are more robust against interruption - typically they
213  * will just throw away the partially constructed root.  This function
214  * is designed for use by those tools, so an operator can happily
215  * press Control-C.
216  *
217  * It's recommended to call this once only at process startup if this
218  * behavior is desired (and to then avoid using librpm against "live"
219  * databases), because currently signal handlers will not be retroactively
220  * applied if a database is open.
221  */
rpmsqSetInterruptSafety(int on)222 void rpmsqSetInterruptSafety(int on)
223 {
224   disableInterruptSafety = !on;
225 }
226