From e27350337518352b6c00c795a22c2cb81d760669 Mon Sep 17 00:00:00 2001
From: Matt
Date: Thu, 30 Jan 2020 15:34:13 +1100
Subject: [PATCH 1/2] If the SHM export segment can't be used because it's too
small, and no processes are using it, then delete and recreate it with the
right size.
---
shmexport.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 57 insertions(+), 3 deletions(-)
diff --git a/shmexport.c b/shmexport.c
index 514ace35d..a8d8900b5 100644
--- a/shmexport.c
+++ b/shmexport.c
@@ -26,29 +26,83 @@ PERMISSIONS
#include
#include
#include
+#include
#include "gpsd.h"
#include "libgps.h" /* for SHM_PSEUDO_FD */
+/**
+ * If there's an existing segment with the given key and of at least the given
+ * size, return its associated ID.
+ * If there's an existing segment with the given key, but with size less than
+ * the given size, and there are no processes attached to the existing segment,
+ * then delete the segment, create a new segment of the given size, then
+ * return the new segment's associated ID.
+ * Otherwise, return -1, with errno set meaningfully.
+ */
+static int recreate_segment(key_t shmkey, struct gps_context_t *context, size_t desired_size, int mode)
+{
+ struct shmid_ds segment;
+ int shmid;
+ int saved_errno;
+
+ assert(NULL == context->shmexport);
+ shmid = shmget(shmkey, desired_size, mode | IPC_CREAT);
+ if (shmid != -1) {
+ return shmid; /* Segment successfully created/retrieved */
+ }
+ /* shmget failed to create/retrieve segment of given size */
+ saved_errno = errno;
+ shmid = shmget(shmkey, 0, 0);
+ if (-1 == shmid) {
+ errno = saved_errno;
+ return -1; /* No existing segment. Unhandled error. */
+ }
+ if (-1 == shmctl(shmid, IPC_STAT, &segment)) {
+ /* Failed to stat segment. Unhandled error. */
+ errno = saved_errno;
+ return -1;
+ }
+ if (segment.shm_segsz >= desired_size) {
+ /* Segment is already big enough. Unhandled error */
+ return -1;
+ }
+ /* shmget likely failed because the existing segment is too small */
+ if (segment.shm_nattch > 0) {
+ /* Other process(es) attached. Cannot resize. */
+ return -1;
+ }
+ /* No processes attached to segment. Ok to delete and recreate */
+ if (-1 == shmctl(shmid, IPC_RMID, NULL)) {
+ /* Cannot delete existing segment */
+ errno = saved_errno;
+ return -1;
+ }
+ shmid = shmget(shmkey, desired_size, mode | IPC_CREAT);
+ return shmid;
+}
+
bool shm_acquire(struct gps_context_t *context)
/* initialize the shared-memory segment to be used for export */
{
long shmkey = getenv("GPSD_SHM_KEY") ? strtol(getenv("GPSD_SHM_KEY"), NULL, 0) : GPSD_SHM_KEY;
+ int mode = 0666;
+ size_t segment_size = sizeof(struct shmexport_t);
- int shmid = shmget((key_t)shmkey, sizeof(struct shmexport_t), (int)(IPC_CREAT|0666));
+ int shmid = recreate_segment((key_t)shmkey, context, segment_size, mode);
if (shmid == -1) {
GPSD_LOG(LOG_ERROR, &context->errout,
"shmget(0x%lx, %zd, 0666) for SHM export failed: %s\n",
shmkey,
- sizeof(struct shmexport_t),
+ segment_size,
strerror(errno));
return false;
} else
GPSD_LOG(LOG_PROG, &context->errout,
"shmget(0x%lx, %zd, 0666) for SHM export succeeded\n",
shmkey,
- sizeof(struct shmexport_t));
+ segment_size);
context->shmexport = (void *)shmat(shmid, 0, 0);
if ((int)(long)context->shmexport == -1) {
--
2.25.0