| 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 | |