Fawkes API  Fawkes Development Version
shm_registry.cpp
1 
2 /***************************************************************************
3  * shm_registry.cpp - shared memory registry
4  *
5  * Created: Sun Mar 06 12:08:09 2011
6  * Copyright 2011 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 
25 #include <utils/ipc/shm_registry.h>
26 #include <core/exception.h>
27 
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <cerrno>
33 #include <cstring>
34 #include <cstdlib>
35 
36 namespace fawkes {
37 #if 0 /* just to make Emacs auto-indent happy */
38 }
39 #endif
40 
41 /** @class SharedMemoryRegistry <utils/ipc/shm_registry.h>
42  * Shared memory registry.
43  * This class opens a named POSIX shared memory segment, which
44  * contains one instance of the MemInfo struct. It is used to detect
45  * and maintain existing SysV IPC shared memory segments in a platform
46  * independent way. SysV IPC shared memory segments have some advanced
47  * functionality, for example reporting how many processes have
48  * attached to the segment. For the registry however, we are more
49  * interested in using a symbolic name which is the same for registry
50  * entries. Therefore, we use this here. The struct is protected by a
51  * lock implemented as a semaphore. Whenever a shared memory segment
52  * is created, it is registered to the registry so others can find
53  * it. On destruction, it is unregistered from the registry.
54  *
55  * @author Tim Niemueller
56  */
57 
58 /** Constructor.
59  * @param name name of the shared memory region. Must follow the rules
60  * set by shm_open(). If NULL defaults to "/fawkes-shmem-registry".
61  */
63 {
64  __shm_name = name ? strdup(name) : strdup(DEFAULT_SHM_NAME);
65 
66  __sem = sem_open(__shm_name, O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, 1);
67 
68  if (__sem == SEM_FAILED) {
69  free(__shm_name);
70  throw Exception(errno, "Failed to init shared memory registry semaphore");
71  }
72 
73  sem_wait(__sem);
74 
75  __shmfd = shm_open(__shm_name, O_RDWR | O_CREAT | O_EXCL,
76  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
77 
78  bool created = false;
79 
80  if ((__shmfd < 0) && (errno == EEXIST)) {
81  __shmfd = shm_open(__shm_name, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
82  } else {
83  if (ftruncate(__shmfd, sizeof(MemInfo)) != 0) {
84  close(__shmfd);
85  shm_unlink(__shm_name);
86  sem_post(__sem);
87  sem_close(__sem);
88  sem_unlink(__shm_name);
89  free(__shm_name);
90  throw Exception(errno, "Failed to resize memory for shared memory registry");
91  }
92 
93  created = true;
94  }
95 
96  if (__shmfd < 0) {
97  sem_post(__sem);
98  sem_close(__sem);
99  sem_unlink(__shm_name);
100  free(__shm_name);
101  throw Exception(errno, "Failed to open shared memory registry");
102  }
103 
104  __meminfo = (MemInfo *)mmap(NULL, sizeof(MemInfo), PROT_READ | PROT_WRITE,
105  MAP_SHARED, __shmfd, 0);
106  if (__meminfo == MAP_FAILED) {
107  close(__shmfd);
108  sem_close(__sem);
109  free(__shm_name);
110  throw Exception(errno, "Failed to mmap shared memory registry");
111  }
112 
113  if (created) {
114  memset(__meminfo, 0, sizeof(MemInfo));
115 
116  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
117  __meminfo->segments[i].shmid = -1;
118  }
119  }
120 
121  __master = created;
122 
123  sem_post(__sem);
124 }
125 
126 
127 /** Destructor. */
129 {
130  close(__shmfd);
131  sem_close(__sem);
132  if (__master) {
133  shm_unlink(__shm_name);
134  }
135 
136  free(__shm_name);
137 }
138 
139 
140 /** Cleanup existing shared memory segments.
141  * @param name shared memory segment name
142  */
143 void
145 {
146  shm_unlink(name ? name : DEFAULT_SHM_NAME);
147  sem_unlink(name ? name : DEFAULT_SHM_NAME);
148 }
149 
150 /** Get a snapshot of currently registered segments.
151  * @return list of all currently registered segments
152  */
153 std::list<SharedMemoryRegistry::SharedMemID>
155 {
156  std::list<SharedMemID> rv;
157 
158  sem_wait(__sem);
159 
160  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
161  if (__meminfo->segments[i].shmid > 0) {
162  rv.push_back(__meminfo->segments[i]);
163  }
164  }
165 
166  sem_post(__sem);
167 
168  return rv;
169 }
170 
171 
172 /** Find segments with particular magic token.
173  * @param magic_token magic token to return IDs for
174  * @return list of segments that currently exist with the given
175  * magic token
176  */
177 std::list<SharedMemoryRegistry::SharedMemID>
178 SharedMemoryRegistry::find_segments(const char *magic_token) const
179 {
180  std::list<SharedMemID> rv;
181 
182  sem_wait(__sem);
183 
184  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
185  if ((__meminfo->segments[i].shmid > 0) &&
186  (strncmp(magic_token, __meminfo->segments[i].magic_token,
187  MAGIC_TOKEN_SIZE) == 0) )
188  {
189  rv.push_back(__meminfo->segments[i]);
190  }
191  }
192 
193  sem_post(__sem);
194 
195  return rv;
196 }
197 
198 
199 /** Register a segment.
200  * @param shmid shared memory ID of the SysV IPC segment
201  * @param magic_token magic token for the new segment
202  */
203 void
204 SharedMemoryRegistry::add_segment(int shmid, const char *magic_token)
205 {
206  sem_wait(__sem);
207 
208  bool valid = false;
209  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
210  if (__meminfo->segments[i].shmid == shmid) {
211  valid = true;
212  break;
213  }
214  }
215 
216  for (unsigned int i = 0; !valid && i < MAXNUM_SHM_SEGMS; ++i) {
217  if (__meminfo->segments[i].shmid == -1) {
218  __meminfo->segments[i].shmid = shmid;
219  strncpy(__meminfo->segments[i].magic_token, magic_token, MAGIC_TOKEN_SIZE);
220  valid = true;
221  }
222  }
223 
224  sem_post(__sem);
225 
226  if (! valid) {
227  throw Exception("Maximum number of shared memory segments already registered");
228  }
229 }
230 
231 
232 /** Remove segment.
233  * @param shmid shared memory ID of the segment to remove.
234  */
235 void
237 {
238  sem_wait(__sem);
239 
240  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
241  if (__meminfo->segments[i].shmid == shmid) {
242  __meminfo->segments[i].shmid = -1;
243  }
244  }
245 
246  sem_post(__sem);
247 }
248 
249 } // end namespace fawkes
void add_segment(int shmid, const char *magic_token)
Register a segment.
std::list< SharedMemoryRegistry::SharedMemID > get_snapshot() const
Get a snapshot of currently registered segments.
Fawkes library namespace.
SharedMemoryRegistry(const char *name=0)
Constructor.
Base class for exceptions in Fawkes.
Definition: exception.h:36
std::list< SharedMemoryRegistry::SharedMemID > find_segments(const char *magic_token) const
Find segments with particular magic token.
void remove_segment(int shmid)
Remove segment.
static void cleanup(const char *name=0)
Cleanup existing shared memory segments.