1 /*-------------------------------------------------------------------------
2 *
3 * pmsignal.c
4 * routines for signaling the postmaster from its child processes
5 *
6 *
7 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * IDENTIFICATION
11 * src/backend/storage/ipc/pmsignal.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 #include "postgres.h"
16
17 #include <signal.h>
18 #include <unistd.h>
19
20 #include "miscadmin.h"
21 #include "postmaster/postmaster.h"
22 #include "replication/walsender.h"
23 #include "storage/pmsignal.h"
24 #include "storage/shmem.h"
25
26
27 /*
28 * The postmaster is signaled by its children by sending SIGUSR1. The
29 * specific reason is communicated via flags in shared memory. We keep
30 * a boolean flag for each possible "reason", so that different reasons
31 * can be signaled by different backends at the same time. (However,
32 * if the same reason is signaled more than once simultaneously, the
33 * postmaster will observe it only once.)
34 *
35 * The flags are actually declared as "volatile sig_atomic_t" for maximum
36 * portability. This should ensure that loads and stores of the flag
37 * values are atomic, allowing us to dispense with any explicit locking.
38 *
39 * In addition to the per-reason flags, we store a set of per-child-process
40 * flags that are currently used only for detecting whether a backend has
41 * exited without performing proper shutdown. The per-child-process flags
42 * have three possible states: UNUSED, ASSIGNED, ACTIVE. An UNUSED slot is
43 * available for assignment. An ASSIGNED slot is associated with a postmaster
44 * child process, but either the process has not touched shared memory yet,
45 * or it has successfully cleaned up after itself. A ACTIVE slot means the
46 * process is actively using shared memory. The slots are assigned to
47 * child processes at random, and postmaster.c is responsible for tracking
48 * which one goes with which PID.
49 *
50 * Actually there is a fourth state, WALSENDER. This is just like ACTIVE,
51 * but carries the extra information that the child is a WAL sender.
52 * WAL senders too start in ACTIVE state, but switch to WALSENDER once they
53 * start streaming the WAL (and they never go back to ACTIVE after that).
54 */
55
56 #define PM_CHILD_UNUSED 0 /* these values must fit in sig_atomic_t */
57 #define PM_CHILD_ASSIGNED 1
58 #define PM_CHILD_ACTIVE 2
59 #define PM_CHILD_WALSENDER 3
60
61 /* "typedef struct PMSignalData PMSignalData" appears in pmsignal.h */
62 struct PMSignalData
63 {
64 /* per-reason flags */
65 sig_atomic_t PMSignalFlags[NUM_PMSIGNALS];
66 /* per-child-process flags */
67 int num_child_flags; /* # of entries in PMChildFlags[] */
68 int next_child_flag; /* next slot to try to assign */
69 sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER];
70 };
71
72 NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
73
74
75 /*
76 * PMSignalShmemSize
77 * Compute space needed for pmsignal.c's shared memory
78 */
79 Size
PMSignalShmemSize(void)80 PMSignalShmemSize(void)
81 {
82 Size size;
83
84 size = offsetof(PMSignalData, PMChildFlags);
85 size = add_size(size, mul_size(MaxLivePostmasterChildren(),
86 sizeof(sig_atomic_t)));
87
88 return size;
89 }
90
91 /*
92 * PMSignalShmemInit - initialize during shared-memory creation
93 */
94 void
PMSignalShmemInit(void)95 PMSignalShmemInit(void)
96 {
97 bool found;
98
99 PMSignalState = (PMSignalData *)
100 ShmemInitStruct("PMSignalState", PMSignalShmemSize(), &found);
101
102 if (!found)
103 {
104 MemSet(PMSignalState, 0, PMSignalShmemSize());
105 PMSignalState->num_child_flags = MaxLivePostmasterChildren();
106 }
107 }
108
109 /*
110 * SendPostmasterSignal - signal the postmaster from a child process
111 */
112 void
SendPostmasterSignal(PMSignalReason reason)113 SendPostmasterSignal(PMSignalReason reason)
114 {
115 /* If called in a standalone backend, do nothing */
116 if (!IsUnderPostmaster)
117 return;
118 /* Atomically set the proper flag */
119 PMSignalState->PMSignalFlags[reason] = true;
120 /* Send signal to postmaster */
121 kill(PostmasterPid, SIGUSR1);
122 }
123
124 /*
125 * CheckPostmasterSignal - check to see if a particular reason has been
126 * signaled, and clear the signal flag. Should be called by postmaster
127 * after receiving SIGUSR1.
128 */
129 bool
CheckPostmasterSignal(PMSignalReason reason)130 CheckPostmasterSignal(PMSignalReason reason)
131 {
132 /* Careful here --- don't clear flag if we haven't seen it set */
133 if (PMSignalState->PMSignalFlags[reason])
134 {
135 PMSignalState->PMSignalFlags[reason] = false;
136 return true;
137 }
138 return false;
139 }
140
141
142 /*
143 * AssignPostmasterChildSlot - select an unused slot for a new postmaster
144 * child process, and set its state to ASSIGNED. Returns a slot number
145 * (one to N).
146 *
147 * Only the postmaster is allowed to execute this routine, so we need no
148 * special locking.
149 */
150 int
AssignPostmasterChildSlot(void)151 AssignPostmasterChildSlot(void)
152 {
153 int slot = PMSignalState->next_child_flag;
154 int n;
155
156 /*
157 * Scan for a free slot. We track the last slot assigned so as not to
158 * waste time repeatedly rescanning low-numbered slots.
159 */
160 for (n = PMSignalState->num_child_flags; n > 0; n--)
161 {
162 if (--slot < 0)
163 slot = PMSignalState->num_child_flags - 1;
164 if (PMSignalState->PMChildFlags[slot] == PM_CHILD_UNUSED)
165 {
166 PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
167 PMSignalState->next_child_flag = slot;
168 return slot + 1;
169 }
170 }
171
172 /* Out of slots ... should never happen, else postmaster.c messed up */
173 elog(FATAL, "no free slots in PMChildFlags array");
174 return 0; /* keep compiler quiet */
175 }
176
177 /*
178 * ReleasePostmasterChildSlot - release a slot after death of a postmaster
179 * child process. This must be called in the postmaster process.
180 *
181 * Returns true if the slot had been in ASSIGNED state (the expected case),
182 * false otherwise (implying that the child failed to clean itself up).
183 */
184 bool
ReleasePostmasterChildSlot(int slot)185 ReleasePostmasterChildSlot(int slot)
186 {
187 bool result;
188
189 Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
190 slot--;
191
192 /*
193 * Note: the slot state might already be unused, because the logic in
194 * postmaster.c is such that this might get called twice when a child
195 * crashes. So we don't try to Assert anything about the state.
196 */
197 result = (PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
198 PMSignalState->PMChildFlags[slot] = PM_CHILD_UNUSED;
199 return result;
200 }
201
202 /*
203 * IsPostmasterChildWalSender - check if given slot is in use by a
204 * walsender process.
205 */
206 bool
IsPostmasterChildWalSender(int slot)207 IsPostmasterChildWalSender(int slot)
208 {
209 Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
210 slot--;
211
212 if (PMSignalState->PMChildFlags[slot] == PM_CHILD_WALSENDER)
213 return true;
214 else
215 return false;
216 }
217
218 /*
219 * MarkPostmasterChildActive - mark a postmaster child as about to begin
220 * actively using shared memory. This is called in the child process.
221 */
222 void
MarkPostmasterChildActive(void)223 MarkPostmasterChildActive(void)
224 {
225 int slot = MyPMChildSlot;
226
227 Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
228 slot--;
229 Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
230 PMSignalState->PMChildFlags[slot] = PM_CHILD_ACTIVE;
231 }
232
233 /*
234 * MarkPostmasterChildWalSender - mark a postmaster child as a WAL sender
235 * process. This is called in the child process, sometime after marking the
236 * child as active.
237 */
238 void
MarkPostmasterChildWalSender(void)239 MarkPostmasterChildWalSender(void)
240 {
241 int slot = MyPMChildSlot;
242
243 Assert(am_walsender);
244
245 Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
246 slot--;
247 Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ACTIVE);
248 PMSignalState->PMChildFlags[slot] = PM_CHILD_WALSENDER;
249 }
250
251 /*
252 * MarkPostmasterChildInactive - mark a postmaster child as done using
253 * shared memory. This is called in the child process.
254 */
255 void
MarkPostmasterChildInactive(void)256 MarkPostmasterChildInactive(void)
257 {
258 int slot = MyPMChildSlot;
259
260 Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
261 slot--;
262 Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ACTIVE ||
263 PMSignalState->PMChildFlags[slot] == PM_CHILD_WALSENDER);
264 PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
265 }
266
267
268 /*
269 * PostmasterIsAlive - check whether postmaster process is still alive
270 */
271 bool
PostmasterIsAlive(void)272 PostmasterIsAlive(void)
273 {
274 #ifndef WIN32
275 char c;
276 ssize_t rc;
277
278 rc = read(postmaster_alive_fds[POSTMASTER_FD_WATCH], &c, 1);
279 if (rc < 0)
280 {
281 if (errno == EAGAIN || errno == EWOULDBLOCK)
282 return true;
283 else
284 elog(FATAL, "read on postmaster death monitoring pipe failed: %m");
285 }
286 else if (rc > 0)
287 elog(FATAL, "unexpected data in postmaster death monitoring pipe");
288
289 return false;
290 #else /* WIN32 */
291 return (WaitForSingleObject(PostmasterHandle, 0) == WAIT_TIMEOUT);
292 #endif /* WIN32 */
293 }
294