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 main()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