Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * shm_registry.cpp - shared memory registry 00004 * 00005 * Created: Sun Mar 06 12:08:09 2011 00006 * Copyright 2011 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 00025 #include <utils/ipc/shm_registry.h> 00026 #include <core/exception.h> 00027 00028 #include <sys/mman.h> 00029 #include <sys/stat.h> 00030 #include <fcntl.h> 00031 #include <unistd.h> 00032 #include <cerrno> 00033 #include <cstring> 00034 #include <cstdlib> 00035 00036 namespace fawkes { 00037 #if 0 /* just to make Emacs auto-indent happy */ 00038 } 00039 #endif 00040 00041 /** @class SharedMemoryRegistry <utils/ipc/shm_registry.h> 00042 * Shared memory registry. 00043 * This class opens a named POSIX shared memory segment, which 00044 * contains one instance of the MemInfo struct. It is used to detect 00045 * and maintain existing SysV IPC shared memory segments in a platform 00046 * independent way. SysV IPC shared memory segments have some advanced 00047 * functionality, for example reporting how many processes have 00048 * attached to the segment. For the registry however, we are more 00049 * interested in using a symbolic name which is the same for registry 00050 * entries. Therefore, we use this here. The struct is protected by a 00051 * lock implemented as a semaphore. Whenever a shared memory segment 00052 * is created, it is registered to the registry so others can find 00053 * it. On destruction, it is unregistered from the registry. 00054 * 00055 * @author Tim Niemueller 00056 */ 00057 00058 /** Constructor. 00059 * @param master if true, the shared memory is first deleted, if it 00060 * existed, and then created fresh. It is resized to contain one 00061 * struct and initialized as an empty registry. 00062 * @param name name of the shared memory region. Must follow the rules 00063 * set by shm_open(). If NULL defaults to "/fawkes-shmem-registry". 00064 */ 00065 SharedMemoryRegistry::SharedMemoryRegistry(bool master, const char *name) 00066 { 00067 __master = false; 00068 __shm_name = name ? strdup(name) : strdup(DEFAULT_SHM_NAME); 00069 00070 __sem = sem_open(__shm_name, O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, 1); 00071 00072 if (__sem == SEM_FAILED) { 00073 free(__shm_name); 00074 throw Exception(errno, "Failed to init shared memory registry semaphore"); 00075 } 00076 00077 sem_wait(__sem); 00078 00079 __shmfd = shm_open(__shm_name, O_RDWR | O_CREAT | O_EXCL, 00080 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 00081 00082 bool created = false; 00083 00084 if ((__shmfd < 0) && (errno == EEXIST)) { 00085 __shmfd = shm_open(__shm_name, O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 00086 } else { 00087 if (ftruncate(__shmfd, sizeof(MemInfo)) != 0) { 00088 close(__shmfd); 00089 shm_unlink(__shm_name); 00090 sem_post(__sem); 00091 sem_close(__sem); 00092 sem_unlink(__shm_name); 00093 free(__shm_name); 00094 throw Exception(errno, "Failed to resize memory for shared memory registry"); 00095 } 00096 00097 created = true; 00098 } 00099 00100 if (__shmfd < 0) { 00101 sem_post(__sem); 00102 sem_close(__sem); 00103 sem_unlink(__shm_name); 00104 free(__shm_name); 00105 throw Exception(errno, "Failed to open shared memory registry"); 00106 } 00107 00108 __meminfo = (MemInfo *)mmap(NULL, sizeof(MemInfo), PROT_READ | PROT_WRITE, 00109 MAP_SHARED, __shmfd, 0); 00110 if (__meminfo == MAP_FAILED) { 00111 close(__shmfd); 00112 sem_close(__sem); 00113 free(__shm_name); 00114 throw Exception(errno, "Failed to mmap shared memory registry"); 00115 } 00116 00117 if (created) { 00118 memset(__meminfo, 0, sizeof(MemInfo)); 00119 00120 for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) { 00121 __meminfo->segments[i].shmid = -1; 00122 } 00123 } 00124 00125 sem_post(__sem); 00126 } 00127 00128 00129 /** Destructor. */ 00130 SharedMemoryRegistry::~SharedMemoryRegistry() 00131 { 00132 close(__shmfd); 00133 sem_close(__sem); 00134 00135 free(__shm_name); 00136 } 00137 00138 00139 /** Cleanup existing shared memory segments. 00140 * @param name shared memory segment name 00141 */ 00142 void 00143 SharedMemoryRegistry::cleanup(const char *name) 00144 { 00145 shm_unlink(name ? name : DEFAULT_SHM_NAME); 00146 sem_unlink(name ? name : DEFAULT_SHM_NAME); 00147 } 00148 00149 /** Get a snapshot of currently registered segments. 00150 * @return list of all currently registered segments 00151 */ 00152 std::list<SharedMemoryRegistry::SharedMemID> 00153 SharedMemoryRegistry::get_snapshot() const 00154 { 00155 std::list<SharedMemID> rv; 00156 00157 sem_wait(__sem); 00158 00159 for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) { 00160 if (__meminfo->segments[i].shmid > 0) { 00161 rv.push_back(__meminfo->segments[i]); 00162 } 00163 } 00164 00165 sem_post(__sem); 00166 00167 return rv; 00168 } 00169 00170 00171 /** Find segments with particular magic token. 00172 * @param magic_token magic token to return IDs for 00173 * @return list of segments that currently exist with the given 00174 * magic token 00175 */ 00176 std::list<SharedMemoryRegistry::SharedMemID> 00177 SharedMemoryRegistry::find_segments(const char *magic_token) const 00178 { 00179 std::list<SharedMemID> rv; 00180 00181 sem_wait(__sem); 00182 00183 for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) { 00184 if ((__meminfo->segments[i].shmid > 0) && 00185 (strncmp(magic_token, __meminfo->segments[i].magic_token, 00186 MAGIC_TOKEN_SIZE) == 0) ) 00187 { 00188 rv.push_back(__meminfo->segments[i]); 00189 } 00190 } 00191 00192 sem_post(__sem); 00193 00194 return rv; 00195 } 00196 00197 00198 /** Register a segment. 00199 * @param shmid shared memory ID of the SysV IPC segment 00200 * @param magic_token magic token for the new segment 00201 */ 00202 void 00203 SharedMemoryRegistry::add_segment(int shmid, const char *magic_token) 00204 { 00205 sem_wait(__sem); 00206 00207 bool valid = false; 00208 for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) { 00209 if (__meminfo->segments[i].shmid == shmid) { 00210 valid = true; 00211 break; 00212 } 00213 } 00214 00215 for (unsigned int i = 0; !valid && i < MAXNUM_SHM_SEGMS; ++i) { 00216 if (__meminfo->segments[i].shmid == -1) { 00217 __meminfo->segments[i].shmid = shmid; 00218 strncpy(__meminfo->segments[i].magic_token, magic_token, MAGIC_TOKEN_SIZE); 00219 valid = true; 00220 } 00221 } 00222 00223 sem_post(__sem); 00224 00225 if (! valid) { 00226 throw Exception("Maximum number of shared memory segments already registered"); 00227 } 00228 } 00229 00230 00231 /** Remove segment. 00232 * @param shmid shared memory ID of the segment to remove. 00233 */ 00234 void 00235 SharedMemoryRegistry::remove_segment(int shmid) 00236 { 00237 sem_wait(__sem); 00238 00239 for (unsigned int i = 0; i < MAXNUM_SHM_SEGMS; ++i) { 00240 if (__meminfo->segments[i].shmid == shmid) { 00241 __meminfo->segments[i].shmid = -1; 00242 } 00243 } 00244 00245 sem_post(__sem); 00246 } 00247 00248 } // end namespace fawkes