Fawkes API  Fawkes Development Version
shm_registry.cpp
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