1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * procsignal.c |
4 | * Routines for interprocess signalling |
5 | * |
6 | * |
7 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
8 | * Portions Copyright (c) 1994, Regents of the University of California |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/storage/ipc/procsignal.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | #include "postgres.h" |
16 | |
17 | #include <signal.h> |
18 | #include <unistd.h> |
19 | |
20 | #include "access/parallel.h" |
21 | #include "commands/async.h" |
22 | #include "miscadmin.h" |
23 | #include "replication/walsender.h" |
24 | #include "storage/latch.h" |
25 | #include "storage/ipc.h" |
26 | #include "storage/proc.h" |
27 | #include "storage/shmem.h" |
28 | #include "storage/sinval.h" |
29 | #include "tcop/tcopprot.h" |
30 | |
31 | |
32 | /* |
33 | * The SIGUSR1 signal is multiplexed to support signalling multiple event |
34 | * types. The specific reason is communicated via flags in shared memory. |
35 | * We keep a boolean flag for each possible "reason", so that different |
36 | * reasons can be signaled to a process concurrently. (However, if the same |
37 | * reason is signaled more than once nearly simultaneously, the process may |
38 | * observe it only once.) |
39 | * |
40 | * Each process that wants to receive signals registers its process ID |
41 | * in the ProcSignalSlots array. The array is indexed by backend ID to make |
42 | * slot allocation simple, and to avoid having to search the array when you |
43 | * know the backend ID of the process you're signalling. (We do support |
44 | * signalling without backend ID, but it's a bit less efficient.) |
45 | * |
46 | * The flags are actually declared as "volatile sig_atomic_t" for maximum |
47 | * portability. This should ensure that loads and stores of the flag |
48 | * values are atomic, allowing us to dispense with any explicit locking. |
49 | */ |
50 | typedef struct |
51 | { |
52 | pid_t pss_pid; |
53 | sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS]; |
54 | } ProcSignalSlot; |
55 | |
56 | /* |
57 | * We reserve a slot for each possible BackendId, plus one for each |
58 | * possible auxiliary process type. (This scheme assumes there is not |
59 | * more than one of any auxiliary process type at a time.) |
60 | */ |
61 | #define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES) |
62 | |
63 | static ProcSignalSlot *ProcSignalSlots = NULL; |
64 | static volatile ProcSignalSlot *MyProcSignalSlot = NULL; |
65 | |
66 | static bool CheckProcSignal(ProcSignalReason reason); |
67 | static void CleanupProcSignalState(int status, Datum arg); |
68 | |
69 | /* |
70 | * ProcSignalShmemSize |
71 | * Compute space needed for procsignal's shared memory |
72 | */ |
73 | Size |
74 | ProcSignalShmemSize(void) |
75 | { |
76 | return NumProcSignalSlots * sizeof(ProcSignalSlot); |
77 | } |
78 | |
79 | /* |
80 | * ProcSignalShmemInit |
81 | * Allocate and initialize procsignal's shared memory |
82 | */ |
83 | void |
84 | ProcSignalShmemInit(void) |
85 | { |
86 | Size size = ProcSignalShmemSize(); |
87 | bool found; |
88 | |
89 | ProcSignalSlots = (ProcSignalSlot *) |
90 | ShmemInitStruct("ProcSignalSlots" , size, &found); |
91 | |
92 | /* If we're first, set everything to zeroes */ |
93 | if (!found) |
94 | MemSet(ProcSignalSlots, 0, size); |
95 | } |
96 | |
97 | /* |
98 | * ProcSignalInit |
99 | * Register the current process in the procsignal array |
100 | * |
101 | * The passed index should be my BackendId if the process has one, |
102 | * or MaxBackends + aux process type if not. |
103 | */ |
104 | void |
105 | ProcSignalInit(int pss_idx) |
106 | { |
107 | volatile ProcSignalSlot *slot; |
108 | |
109 | Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots); |
110 | |
111 | slot = &ProcSignalSlots[pss_idx - 1]; |
112 | |
113 | /* sanity check */ |
114 | if (slot->pss_pid != 0) |
115 | elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty" , |
116 | MyProcPid, pss_idx); |
117 | |
118 | /* Clear out any leftover signal reasons */ |
119 | MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); |
120 | |
121 | /* Mark slot with my PID */ |
122 | slot->pss_pid = MyProcPid; |
123 | |
124 | /* Remember slot location for CheckProcSignal */ |
125 | MyProcSignalSlot = slot; |
126 | |
127 | /* Set up to release the slot on process exit */ |
128 | on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx)); |
129 | } |
130 | |
131 | /* |
132 | * CleanupProcSignalState |
133 | * Remove current process from ProcSignalSlots |
134 | * |
135 | * This function is called via on_shmem_exit() during backend shutdown. |
136 | */ |
137 | static void |
138 | CleanupProcSignalState(int status, Datum arg) |
139 | { |
140 | int pss_idx = DatumGetInt32(arg); |
141 | volatile ProcSignalSlot *slot; |
142 | |
143 | slot = &ProcSignalSlots[pss_idx - 1]; |
144 | Assert(slot == MyProcSignalSlot); |
145 | |
146 | /* |
147 | * Clear MyProcSignalSlot, so that a SIGUSR1 received after this point |
148 | * won't try to access it after it's no longer ours (and perhaps even |
149 | * after we've unmapped the shared memory segment). |
150 | */ |
151 | MyProcSignalSlot = NULL; |
152 | |
153 | /* sanity check */ |
154 | if (slot->pss_pid != MyProcPid) |
155 | { |
156 | /* |
157 | * don't ERROR here. We're exiting anyway, and don't want to get into |
158 | * infinite loop trying to exit |
159 | */ |
160 | elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d" , |
161 | MyProcPid, pss_idx, (int) slot->pss_pid); |
162 | return; /* XXX better to zero the slot anyway? */ |
163 | } |
164 | |
165 | slot->pss_pid = 0; |
166 | } |
167 | |
168 | /* |
169 | * SendProcSignal |
170 | * Send a signal to a Postgres process |
171 | * |
172 | * Providing backendId is optional, but it will speed up the operation. |
173 | * |
174 | * On success (a signal was sent), zero is returned. |
175 | * On error, -1 is returned, and errno is set (typically to ESRCH or EPERM). |
176 | * |
177 | * Not to be confused with ProcSendSignal |
178 | */ |
179 | int |
180 | SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId) |
181 | { |
182 | volatile ProcSignalSlot *slot; |
183 | |
184 | if (backendId != InvalidBackendId) |
185 | { |
186 | slot = &ProcSignalSlots[backendId - 1]; |
187 | |
188 | /* |
189 | * Note: Since there's no locking, it's possible that the target |
190 | * process detaches from shared memory and exits right after this |
191 | * test, before we set the flag and send signal. And the signal slot |
192 | * might even be recycled by a new process, so it's remotely possible |
193 | * that we set a flag for a wrong process. That's OK, all the signals |
194 | * are such that no harm is done if they're mistakenly fired. |
195 | */ |
196 | if (slot->pss_pid == pid) |
197 | { |
198 | /* Atomically set the proper flag */ |
199 | slot->pss_signalFlags[reason] = true; |
200 | /* Send signal */ |
201 | return kill(pid, SIGUSR1); |
202 | } |
203 | } |
204 | else |
205 | { |
206 | /* |
207 | * BackendId not provided, so search the array using pid. We search |
208 | * the array back to front so as to reduce search overhead. Passing |
209 | * InvalidBackendId means that the target is most likely an auxiliary |
210 | * process, which will have a slot near the end of the array. |
211 | */ |
212 | int i; |
213 | |
214 | for (i = NumProcSignalSlots - 1; i >= 0; i--) |
215 | { |
216 | slot = &ProcSignalSlots[i]; |
217 | |
218 | if (slot->pss_pid == pid) |
219 | { |
220 | /* the above note about race conditions applies here too */ |
221 | |
222 | /* Atomically set the proper flag */ |
223 | slot->pss_signalFlags[reason] = true; |
224 | /* Send signal */ |
225 | return kill(pid, SIGUSR1); |
226 | } |
227 | } |
228 | } |
229 | |
230 | errno = ESRCH; |
231 | return -1; |
232 | } |
233 | |
234 | /* |
235 | * CheckProcSignal - check to see if a particular reason has been |
236 | * signaled, and clear the signal flag. Should be called after receiving |
237 | * SIGUSR1. |
238 | */ |
239 | static bool |
240 | CheckProcSignal(ProcSignalReason reason) |
241 | { |
242 | volatile ProcSignalSlot *slot = MyProcSignalSlot; |
243 | |
244 | if (slot != NULL) |
245 | { |
246 | /* Careful here --- don't clear flag if we haven't seen it set */ |
247 | if (slot->pss_signalFlags[reason]) |
248 | { |
249 | slot->pss_signalFlags[reason] = false; |
250 | return true; |
251 | } |
252 | } |
253 | |
254 | return false; |
255 | } |
256 | |
257 | /* |
258 | * procsignal_sigusr1_handler - handle SIGUSR1 signal. |
259 | */ |
260 | void |
261 | procsignal_sigusr1_handler(SIGNAL_ARGS) |
262 | { |
263 | int save_errno = errno; |
264 | |
265 | if (CheckProcSignal(PROCSIG_CATCHUP_INTERRUPT)) |
266 | HandleCatchupInterrupt(); |
267 | |
268 | if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT)) |
269 | HandleNotifyInterrupt(); |
270 | |
271 | if (CheckProcSignal(PROCSIG_PARALLEL_MESSAGE)) |
272 | HandleParallelMessageInterrupt(); |
273 | |
274 | if (CheckProcSignal(PROCSIG_WALSND_INIT_STOPPING)) |
275 | HandleWalSndInitStopping(); |
276 | |
277 | if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE)) |
278 | RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE); |
279 | |
280 | if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE)) |
281 | RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE); |
282 | |
283 | if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK)) |
284 | RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK); |
285 | |
286 | if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT)) |
287 | RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT); |
288 | |
289 | if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK)) |
290 | RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); |
291 | |
292 | if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN)) |
293 | RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); |
294 | |
295 | SetLatch(MyLatch); |
296 | |
297 | latch_sigusr1_handler(); |
298 | |
299 | errno = save_errno; |
300 | } |
301 | |