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 #include <core/exception.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <utils/ipc/shm_registry.h>
28 
29 #include <cerrno>
30 #include <cstdlib>
31 #include <cstring>
32 #include <fcntl.h>
33 #include <unistd.h>
34 
35 namespace fawkes {
36 
37 /** @class SharedMemoryRegistry <utils/ipc/shm_registry.h>
38  * Shared memory registry.
39  * This class opens a named POSIX shared memory segment, which
40  * contains one instance of the MemInfo struct. It is used to detect
41  * and maintain existing SysV IPC shared memory segments in a platform
42  * independent way. SysV IPC shared memory segments have some advanced
43  * functionality, for example reporting how many processes have
44  * attached to the segment. For the registry however, we are more
45  * interested in using a symbolic name which is the same for registry
46  * entries. Therefore, we use this here. The struct is protected by a
47  * lock implemented as a semaphore. Whenever a shared memory segment
48  * is created, it is registered to the registry so others can find
49  * it. On destruction, it is unregistered from the registry.
50  *
51  * @author Tim Niemueller
52  */
53 
54 /** Constructor.
55  * @param name name of the shared memory region. Must follow the rules
56  * set by shm_open(). If NULL defaults to "/fawkes-shmem-registry".
57  */
59 {
60  shm_name_ = name ? strdup(name) : strdup(DEFAULT_SHM_NAME);
61 
62  sem_ = sem_open(shm_name_, O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 1);
63 
64  if (sem_ == SEM_FAILED) {
65  free(shm_name_);
66  throw Exception(errno, "Failed to init shared memory registry semaphore");
67  }
68 
69  sem_wait(sem_);
70 
71  shmfd_ = shm_open(shm_name_, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
72 
73  bool created = false;
74 
75  if ((shmfd_ < 0) && (errno == EEXIST)) {
76  shmfd_ = shm_open(shm_name_, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
77  } else {
78  if (ftruncate(shmfd_, sizeof(MemInfo)) != 0) {
79  close(shmfd_);
80  shm_unlink(shm_name_);
81  sem_post(sem_);
82  sem_close(sem_);
83  sem_unlink(shm_name_);
84  free(shm_name_);
85  throw Exception(errno, "Failed to resize memory for shared memory registry");
86  }
87 
88  created = true;
89  }
90 
91  if (shmfd_ < 0) {
92  sem_post(sem_);
93  sem_close(sem_);
94  sem_unlink(shm_name_);
95  free(shm_name_);
96  throw Exception(errno, "Failed to open shared memory registry");
97  }
98 
99  meminfo_ = (MemInfo *)mmap(NULL, sizeof(MemInfo), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd_, 0);
100  if (meminfo_ == MAP_FAILED) {
101  close(shmfd_);
102  sem_close(sem_);
103  free(shm_name_);
104  throw Exception(errno, "Failed to mmap shared memory registry");
105  }
106 
107  if (created) {
108  memset(meminfo_, 0, sizeof(MemInfo));
109 
110  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
111  meminfo_->segments[i].shmid = -1;
112  }
113  }
114 
115  master_ = created;
116 
117  sem_post(sem_);
118 }
119 
120 /** Destructor. */
122 {
123  close(shmfd_);
124  sem_close(sem_);
125  if (master_) {
126  shm_unlink(shm_name_);
127  }
128 
129  free(shm_name_);
130 }
131 
132 /** Cleanup existing shared memory segments.
133  * @param name shared memory segment name
134  */
135 void
137 {
138  shm_unlink(name ? name : DEFAULT_SHM_NAME);
139  sem_unlink(name ? name : DEFAULT_SHM_NAME);
140 }
141 
142 /** Get a snapshot of currently registered segments.
143  * @return list of all currently registered segments
144  */
145 std::list<SharedMemoryRegistry::SharedMemID>
147 {
148  std::list<SharedMemID> rv;
149 
150  sem_wait(sem_);
151 
152  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
153  if (meminfo_->segments[i].shmid > 0) {
154  rv.push_back(meminfo_->segments[i]);
155  }
156  }
157 
158  sem_post(sem_);
159 
160  return rv;
161 }
162 
163 /** Find segments with particular magic token.
164  * @param magic_token magic token to return IDs for
165  * @return list of segments that currently exist with the given
166  * magic token
167  */
168 std::list<SharedMemoryRegistry::SharedMemID>
169 SharedMemoryRegistry::find_segments(const char *magic_token) const
170 {
171  std::list<SharedMemID> rv;
172 
173  sem_wait(sem_);
174 
175  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
176  if ((meminfo_->segments[i].shmid > 0)
177  && (strncmp(magic_token, meminfo_->segments[i].magic_token, MAGIC_TOKEN_SIZE) == 0)) {
178  rv.push_back(meminfo_->segments[i]);
179  }
180  }
181 
182  sem_post(sem_);
183 
184  return rv;
185 }
186 
187 /** Register a segment.
188  * @param shmid shared memory ID of the SysV IPC segment
189  * @param magic_token magic token for the new segment
190  */
191 void
192 SharedMemoryRegistry::add_segment(int shmid, const char *magic_token)
193 {
194  sem_wait(sem_);
195 
196  bool valid = false;
197  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
198  if (meminfo_->segments[i].shmid == shmid) {
199  valid = true;
200  break;
201  }
202  }
203 
204  for (unsigned int i = 0; !valid && i < MAXNUM_SHM_SEGMS; ++i) {
205  if (meminfo_->segments[i].shmid == -1) {
206  meminfo_->segments[i].shmid = shmid;
207  strncpy(meminfo_->segments[i].magic_token, magic_token, MAGIC_TOKEN_SIZE);
208  valid = true;
209  }
210  }
211 
212  sem_post(sem_);
213 
214  if (!valid) {
215  throw Exception("Maximum number of shared memory segments already registered");
216  }
217 }
218 
219 /** Remove segment.
220  * @param shmid shared memory ID of the segment to remove.
221  */
222 void
224 {
225  sem_wait(sem_);
226 
227  for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) {
228  if (meminfo_->segments[i].shmid == shmid) {
229  meminfo_->segments[i].shmid = -1;
230  }
231  }
232 
233  sem_post(sem_);
234 }
235 
236 } // 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:35
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.