10
10
* Portions Copyright (c) 1994, Regents of the University of California
11
11
*
12
12
* IDENTIFICATION
13
- * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.39 2004/10/13 01:25:11 neilc Exp $
13
+ * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.40 2004/11/09 21:30:13 tgl Exp $
14
14
*
15
15
*-------------------------------------------------------------------------
16
16
*/
20
20
#include <signal.h>
21
21
#include <unistd.h>
22
22
#include <sys/file.h>
23
+ #include <sys/stat.h>
23
24
#ifdef HAVE_SYS_IPC_H
24
25
#include <sys/ipc.h>
25
26
#endif
@@ -40,6 +41,12 @@ typedef int IpcMemoryId;/* shared memory ID returned by shmget(2) */
40
41
41
42
#define IPCProtection (0600)/* access/modify by user only */
42
43
44
+ #ifdef SHM_SHARE_MMU /* use intimate shared memory on Solaris */
45
+ #define PG_SHMAT_FLAGS SHM_SHARE_MMU
46
+ #else
47
+ #define PG_SHMAT_FLAGS 0
48
+ #endif
49
+
43
50
44
51
unsigned long UsedShmemSegID = 0 ;
45
52
void * UsedShmemSegAddr = NULL ;
@@ -135,16 +142,10 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size)
135
142
on_shmem_exit (IpcMemoryDelete ,Int32GetDatum (shmid ));
136
143
137
144
/* OK, should be able to attach to the segment */
138
- #ifdef SHM_SHARE_MMU
139
- /* use intimate shared memory on Solaris */
140
- memAddress = shmat (shmid ,0 ,SHM_SHARE_MMU );
141
- #else
142
-
143
145
#ifdef EXEC_BACKEND
144
- memAddress = shmat (shmid ,UsedShmemSegAddr ,0 );
146
+ memAddress = shmat (shmid ,UsedShmemSegAddr ,PG_SHMAT_FLAGS );
145
147
#else
146
- memAddress = shmat (shmid ,NULL ,0 );
147
- #endif
148
+ memAddress = shmat (shmid ,NULL ,PG_SHMAT_FLAGS );
148
149
#endif
149
150
150
151
if (memAddress == (void * )-1 )
@@ -188,20 +189,26 @@ IpcMemoryDelete(int status, Datum shmId)
188
189
* PGSharedMemoryIsInUse
189
190
*
190
191
* Is a previously-existing shmem segment still existing and in use?
192
+ *
193
+ * The point of this exercise is to detect the case where a prior postmaster
194
+ * crashed, but it left child backends that are still running. Therefore
195
+ * we only care about shmem segments that are associated with the intended
196
+ * DataDir. This is an important consideration since accidental matches of
197
+ * shmem segment IDs are reasonably common.
191
198
*/
192
199
bool
193
200
PGSharedMemoryIsInUse (unsigned long id1 ,unsigned long id2 )
194
201
{
195
202
IpcMemoryId shmId = (IpcMemoryId )id2 ;
196
203
struct shmid_ds shmStat ;
204
+ #ifndef WIN32
205
+ struct stat statbuf ;
206
+ PGShmemHeader * hdr ;
207
+ #endif
197
208
198
209
/*
199
210
* We detect whether a shared memory segment is in use by seeing
200
211
* whether it (a) exists and (b) has any processes are attached to it.
201
- *
202
- * If we are unable to perform the stat operation for a reason other than
203
- * nonexistence of the segment (most likely, because it doesn't belong
204
- * to our userid), assume it is in use.
205
212
*/
206
213
if (shmctl (shmId ,IPC_STAT ,& shmStat )< 0 )
207
214
{
@@ -212,13 +219,58 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
212
219
*/
213
220
if (errno == EINVAL )
214
221
return false;
215
- /* Else assume segment is in use */
222
+ /*
223
+ * EACCES implies that the segment belongs to some other userid,
224
+ * which means it is not a Postgres shmem segment (or at least,
225
+ * not one that is relevant to our data directory).
226
+ */
227
+ if (errno == EACCES )
228
+ return false;
229
+ /*
230
+ * Otherwise, we had better assume that the segment is in use.
231
+ * The only likely case is EIDRM, which implies that the segment
232
+ * has been IPC_RMID'd but there are still processes attached to it.
233
+ */
216
234
return true;
217
235
}
218
- /* If it has attached processes, it's in use */
219
- if (shmStat .shm_nattch != 0 )
220
- return true;
221
- return false;
236
+
237
+ /* If it has no attached processes, it's not in use */
238
+ if (shmStat .shm_nattch == 0 )
239
+ return false;
240
+
241
+ /*
242
+ * Try to attach to the segment and see if it matches our data directory.
243
+ * This avoids shmid-conflict problems on machines that are running
244
+ * several postmasters under the same userid. On Windows, which doesn't
245
+ * have useful inode numbers, we can't do this so we punt and assume there
246
+ * is a conflict.
247
+ */
248
+ #ifndef WIN32
249
+ if (stat (DataDir ,& statbuf )< 0 )
250
+ return true;/* if can't stat, be conservative */
251
+
252
+ hdr = (PGShmemHeader * )shmat (shmId ,NULL ,PG_SHMAT_FLAGS );
253
+
254
+ if (hdr == (PGShmemHeader * )-1 )
255
+ return true;/* if can't attach, be conservative */
256
+
257
+ if (hdr -> magic != PGShmemMagic ||
258
+ hdr -> device != statbuf .st_dev ||
259
+ hdr -> inode != statbuf .st_ino )
260
+ {
261
+ /*
262
+ * It's either not a Postgres segment, or not one for my data
263
+ * directory. In either case it poses no threat.
264
+ */
265
+ shmdt ((void * )hdr );
266
+ return false;
267
+ }
268
+
269
+ /* Trouble --- looks a lot like there's still live backends */
270
+ shmdt ((void * )hdr );
271
+ #endif
272
+
273
+ return true;
222
274
}
223
275
224
276
@@ -247,6 +299,9 @@ PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
247
299
void * memAddress ;
248
300
PGShmemHeader * hdr ;
249
301
IpcMemoryId shmid ;
302
+ #ifndef WIN32
303
+ struct stat statbuf ;
304
+ #endif
250
305
251
306
#ifdef EXEC_BACKEND
252
307
/* If Exec case, just attach and return the pointer */
@@ -345,6 +400,17 @@ PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
345
400
hdr -> creatorPID = getpid ();
346
401
hdr -> magic = PGShmemMagic ;
347
402
403
+ #ifndef WIN32
404
+ /* Fill in the data directory ID info, too */
405
+ if (stat (DataDir ,& statbuf )< 0 )
406
+ ereport (FATAL ,
407
+ (errcode_for_file_access (),
408
+ errmsg ("could not stat data directory \"%s\": %m" ,
409
+ DataDir )));
410
+ hdr -> device = statbuf .st_dev ;
411
+ hdr -> inode = statbuf .st_ino ;
412
+ #endif
413
+
348
414
/*
349
415
* Initialize space allocation status for segment.
350
416
*/
@@ -397,15 +463,7 @@ PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
397
463
if ((* shmid = shmget (key ,sizeof (PGShmemHeader ),0 ))< 0 )
398
464
return NULL ;
399
465
400
- hdr = (PGShmemHeader * )shmat (* shmid ,
401
- UsedShmemSegAddr ,
402
- #ifdef SHM_SHARE_MMU
403
- /* use intimate shared memory on Solaris */
404
- SHM_SHARE_MMU
405
- #else
406
- 0
407
- #endif
408
- );
466
+ hdr = (PGShmemHeader * )shmat (* shmid ,UsedShmemSegAddr ,PG_SHMAT_FLAGS );
409
467
410
468
if (hdr == (PGShmemHeader * )-1 )
411
469
return NULL ;/* failed: must be some other app's */