Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
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 master if true, the shared memory is first deleted, if it
60  * existed, and then created fresh. It is resized to contain one
61  * struct and initialized as an empty registry.
62  * @param name name of the shared memory region. Must follow the rules
63  * set by shm_open(). If NULL defaults to "/fawkes-shmem-registry".
64  */
65 SharedMemoryRegistry::SharedMemoryRegistry(bool master, const char *name)
66 {
67  __master = false;
68  __shm_name = name ? strdup(name) : strdup(DEFAULT_SHM_NAME);
69 
70  __sem = sem_open(__shm_name, O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, 1);
71 
72  if (__sem == SEM_FAILED) {
73  free(__shm_name);
74  throw Exception(errno, "Failed to init shared memory registry semaphore");
75  }
76 
77  sem_wait(__sem);
78 
79  __shmfd = shm_open(__shm_name, O_RDWR | O_CREAT | O_EXCL,
80  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
81 
82  bool created = false;
83 
84  if ((__shmfd < 0) && (errno == EEXIST)) {
85  __shmfd = shm_open(__shm_name, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
86  } else {
87  if (ftruncate(__shmfd, sizeof(MemInfo)) != 0) {
88  close(__shmfd);
89  shm_unlink(__shm_name);
90  sem_post(__sem);
91  sem_close(__sem);
92  sem_unlink(__shm_name);
93  free(__shm_name);
94  throw Exception(errno, "Failed to resize memory for shared memory registry");
95  }
96 
97  created = true;
98  }
99 
100  if (__shmfd < 0) {
101  sem_post(__sem);
102  sem_close(__sem);
103  sem_unlink(__shm_name);
104  free(__shm_name);
105  throw Exception(errno, "Failed to open shared memory registry");
106  }
107 
108  __meminfo = (MemInfo *)mmap(NULL, sizeof(MemInfo), PROT_READ | PROT_WRITE,
109  MAP_SHARED, __shmfd, 0);
110  if (__meminfo == MAP_FAILED) {
111  close(__shmfd);
112  sem_close(__sem);
113  free(__shm_name);
114  throw Exception(errno, "Failed to mmap shared memory registry");
115  }
116 
117  if (created) {
118  memset(__meminfo, 0, sizeof(MemInfo));
119 
120  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
121  __meminfo->segments[i].shmid = -1;
122  }
123  }
124 
125  sem_post(__sem);
126 }
127 
128 
129 /** Destructor. */
130 SharedMemoryRegistry::~SharedMemoryRegistry()
131 {
132  close(__shmfd);
133  sem_close(__sem);
134 
135  free(__shm_name);
136 }
137 
138 
139 /** Cleanup existing shared memory segments.
140  * @param name shared memory segment name
141  */
142 void
143 SharedMemoryRegistry::cleanup(const char *name)
144 {
145  shm_unlink(name ? name : DEFAULT_SHM_NAME);
146  sem_unlink(name ? name : DEFAULT_SHM_NAME);
147 }
148 
149 /** Get a snapshot of currently registered segments.
150  * @return list of all currently registered segments
151  */
152 std::list<SharedMemoryRegistry::SharedMemID>
153 SharedMemoryRegistry::get_snapshot() const
154 {
155  std::list<SharedMemID> rv;
156 
157  sem_wait(__sem);
158 
159  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
160  if (__meminfo->segments[i].shmid > 0) {
161  rv.push_back(__meminfo->segments[i]);
162  }
163  }
164 
165  sem_post(__sem);
166 
167  return rv;
168 }
169 
170 
171 /** Find segments with particular magic token.
172  * @param magic_token magic token to return IDs for
173  * @return list of segments that currently exist with the given
174  * magic token
175  */
176 std::list<SharedMemoryRegistry::SharedMemID>
177 SharedMemoryRegistry::find_segments(const char *magic_token) const
178 {
179  std::list<SharedMemID> rv;
180 
181  sem_wait(__sem);
182 
183  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
184  if ((__meminfo->segments[i].shmid > 0) &&
185  (strncmp(magic_token, __meminfo->segments[i].magic_token,
186  MAGIC_TOKEN_SIZE) == 0) )
187  {
188  rv.push_back(__meminfo->segments[i]);
189  }
190  }
191 
192  sem_post(__sem);
193 
194  return rv;
195 }
196 
197 
198 /** Register a segment.
199  * @param shmid shared memory ID of the SysV IPC segment
200  * @param magic_token magic token for the new segment
201  */
202 void
203 SharedMemoryRegistry::add_segment(int shmid, const char *magic_token)
204 {
205  sem_wait(__sem);
206 
207  bool valid = false;
208  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
209  if (__meminfo->segments[i].shmid == shmid) {
210  valid = true;
211  break;
212  }
213  }
214 
215  for (unsigned int i = 0; !valid && i < MAXNUM_SHM_SEGMS; ++i) {
216  if (__meminfo->segments[i].shmid == -1) {
217  __meminfo->segments[i].shmid = shmid;
218  strncpy(__meminfo->segments[i].magic_token, magic_token, MAGIC_TOKEN_SIZE);
219  valid = true;
220  }
221  }
222 
223  sem_post(__sem);
224 
225  if (! valid) {
226  throw Exception("Maximum number of shared memory segments already registered");
227  }
228 }
229 
230 
231 /** Remove segment.
232  * @param shmid shared memory ID of the segment to remove.
233  */
234 void
235 SharedMemoryRegistry::remove_segment(int shmid)
236 {
237  sem_wait(__sem);
238 
239  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
240  if (__meminfo->segments[i].shmid == shmid) {
241  __meminfo->segments[i].shmid = -1;
242  }
243  }
244 
245  sem_post(__sem);
246 }
247 
248 } // end namespace fawkes