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