]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // | |
3 | // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost | |
4 | // Software License, Version 1.0. (See accompanying file | |
5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | // See http://www.boost.org/libs/interprocess for documentation. | |
8 | // | |
9 | ////////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | #ifndef BOOST_INTERPROCESS_SHARED_MEMORY_OBJECT_HPP | |
12 | #define BOOST_INTERPROCESS_SHARED_MEMORY_OBJECT_HPP | |
13 | ||
14 | #ifndef BOOST_CONFIG_HPP | |
15 | # include <boost/config.hpp> | |
16 | #endif | |
17 | # | |
18 | #if defined(BOOST_HAS_PRAGMA_ONCE) | |
19 | # pragma once | |
20 | #endif | |
21 | ||
22 | #include <boost/interprocess/detail/config_begin.hpp> | |
23 | #include <boost/interprocess/detail/workaround.hpp> | |
24 | #include <boost/interprocess/creation_tags.hpp> | |
25 | #include <boost/interprocess/exceptions.hpp> | |
26 | #include <boost/move/utility_core.hpp> | |
27 | #include <boost/interprocess/interprocess_fwd.hpp> | |
28 | #include <boost/interprocess/exceptions.hpp> | |
29 | #include <boost/interprocess/detail/os_file_functions.hpp> | |
30 | #include <boost/interprocess/detail/shared_dir_helpers.hpp> | |
31 | #include <boost/interprocess/permissions.hpp> | |
32 | #include <boost/move/adl_move_swap.hpp> | |
33 | #include <cstddef> | |
34 | #include <string> | |
35 | ||
36 | #if defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS) | |
37 | # include <fcntl.h> //O_CREAT, O_*... | |
38 | # include <sys/mman.h> //shm_xxx | |
39 | # include <unistd.h> //ftruncate, close | |
40 | # include <sys/stat.h> //mode_t, S_IRWXG, S_IRWXO, S_IRWXU, | |
41 | # if defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY) | |
42 | # if defined(__FreeBSD__) | |
43 | # include <sys/sysctl.h> | |
44 | # endif | |
45 | # endif | |
46 | #else | |
47 | // | |
48 | #endif | |
49 | ||
50 | //!\file | |
51 | //!Describes a shared memory object management class. | |
52 | ||
53 | namespace boost { | |
54 | namespace interprocess { | |
55 | ||
56 | //!A class that wraps a shared memory mapping that can be used to | |
57 | //!create mapped regions from the mapped files | |
58 | class shared_memory_object | |
59 | { | |
60 | #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) | |
61 | //Non-copyable and non-assignable | |
62 | BOOST_MOVABLE_BUT_NOT_COPYABLE(shared_memory_object) | |
63 | #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED | |
64 | ||
65 | public: | |
66 | //!Default constructor. Represents an empty shared_memory_object. | |
67 | shared_memory_object(); | |
68 | ||
69 | //!Creates a shared memory object with name "name" and mode "mode", with the access mode "mode" | |
70 | //!If the file previously exists, throws an error.*/ | |
71 | shared_memory_object(create_only_t, const char *name, mode_t mode, const permissions &perm = permissions()) | |
72 | { this->priv_open_or_create(ipcdetail::DoCreate, name, mode, perm); } | |
73 | ||
74 | //!Tries to create a shared memory object with name "name" and mode "mode", with the | |
75 | //!access mode "mode". If the file previously exists, it tries to open it with mode "mode". | |
76 | //!Otherwise throws an error. | |
77 | shared_memory_object(open_or_create_t, const char *name, mode_t mode, const permissions &perm = permissions()) | |
78 | { this->priv_open_or_create(ipcdetail::DoOpenOrCreate, name, mode, perm); } | |
79 | ||
80 | //!Tries to open a shared memory object with name "name", with the access mode "mode". | |
81 | //!If the file does not previously exist, it throws an error. | |
82 | shared_memory_object(open_only_t, const char *name, mode_t mode) | |
83 | { this->priv_open_or_create(ipcdetail::DoOpen, name, mode, permissions()); } | |
84 | ||
85 | //!Moves the ownership of "moved"'s shared memory object to *this. | |
86 | //!After the call, "moved" does not represent any shared memory object. | |
87 | //!Does not throw | |
88 | shared_memory_object(BOOST_RV_REF(shared_memory_object) moved) | |
89 | : m_handle(file_handle_t(ipcdetail::invalid_file())) | |
90 | , m_mode(read_only) | |
91 | { this->swap(moved); } | |
92 | ||
93 | //!Moves the ownership of "moved"'s shared memory to *this. | |
94 | //!After the call, "moved" does not represent any shared memory. | |
95 | //!Does not throw | |
96 | shared_memory_object &operator=(BOOST_RV_REF(shared_memory_object) moved) | |
97 | { | |
98 | shared_memory_object tmp(boost::move(moved)); | |
99 | this->swap(tmp); | |
100 | return *this; | |
101 | } | |
102 | ||
103 | //!Swaps the shared_memory_objects. Does not throw | |
104 | void swap(shared_memory_object &moved); | |
105 | ||
106 | //!Erases a shared memory object from the system. | |
107 | //!Returns false on error. Never throws | |
108 | static bool remove(const char *name); | |
109 | ||
110 | //!Sets the size of the shared memory mapping | |
111 | void truncate(offset_t length); | |
112 | ||
113 | //!Destroys *this and indicates that the calling process is finished using | |
114 | //!the resource. All mapped regions are still | |
115 | //!valid after destruction. The destructor function will deallocate | |
116 | //!any system resources allocated by the system for use by this process for | |
117 | //!this resource. The resource can still be opened again calling | |
118 | //!the open constructor overload. To erase the resource from the system | |
119 | //!use remove(). | |
120 | ~shared_memory_object(); | |
121 | ||
122 | //!Returns the name of the shared memory object. | |
123 | const char *get_name() const; | |
124 | ||
125 | //!Returns true if the size of the shared memory object | |
126 | //!can be obtained and writes the size in the passed reference | |
127 | bool get_size(offset_t &size) const; | |
128 | ||
129 | //!Returns access mode | |
130 | mode_t get_mode() const; | |
131 | ||
132 | //!Returns mapping handle. Never throws. | |
133 | mapping_handle_t get_mapping_handle() const; | |
134 | ||
135 | #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) | |
136 | private: | |
137 | ||
138 | //!Closes a previously opened file mapping. Never throws. | |
139 | void priv_close(); | |
140 | ||
141 | //!Opens or creates a shared memory object. | |
142 | bool priv_open_or_create(ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm); | |
143 | ||
144 | file_handle_t m_handle; | |
145 | mode_t m_mode; | |
146 | std::string m_filename; | |
147 | #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED | |
148 | }; | |
149 | ||
150 | #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) | |
151 | ||
152 | inline shared_memory_object::shared_memory_object() | |
153 | : m_handle(file_handle_t(ipcdetail::invalid_file())) | |
154 | , m_mode(read_only) | |
155 | {} | |
156 | ||
157 | inline shared_memory_object::~shared_memory_object() | |
158 | { this->priv_close(); } | |
159 | ||
160 | ||
161 | inline const char *shared_memory_object::get_name() const | |
162 | { return m_filename.c_str(); } | |
163 | ||
164 | inline bool shared_memory_object::get_size(offset_t &size) const | |
165 | { return ipcdetail::get_file_size((file_handle_t)m_handle, size); } | |
166 | ||
167 | inline void shared_memory_object::swap(shared_memory_object &other) | |
168 | { | |
169 | boost::adl_move_swap(m_handle, other.m_handle); | |
170 | boost::adl_move_swap(m_mode, other.m_mode); | |
171 | m_filename.swap(other.m_filename); | |
172 | } | |
173 | ||
174 | inline mapping_handle_t shared_memory_object::get_mapping_handle() const | |
175 | { | |
176 | return ipcdetail::mapping_handle_from_file_handle(m_handle); | |
177 | } | |
178 | ||
179 | inline mode_t shared_memory_object::get_mode() const | |
180 | { return m_mode; } | |
181 | ||
182 | #if !defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS) | |
183 | ||
184 | inline bool shared_memory_object::priv_open_or_create | |
185 | (ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm) | |
186 | { | |
187 | m_filename = filename; | |
188 | std::string shmfile; | |
189 | ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, shmfile); | |
190 | ||
191 | //Set accesses | |
192 | if (mode != read_write && mode != read_only){ | |
193 | error_info err = other_error; | |
194 | throw interprocess_exception(err); | |
195 | } | |
196 | ||
197 | switch(type){ | |
198 | case ipcdetail::DoOpen: | |
199 | m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true); | |
200 | break; | |
201 | case ipcdetail::DoCreate: | |
202 | m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true); | |
203 | break; | |
204 | case ipcdetail::DoOpenOrCreate: | |
205 | m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true); | |
206 | break; | |
207 | default: | |
208 | { | |
209 | error_info err = other_error; | |
210 | throw interprocess_exception(err); | |
211 | } | |
212 | } | |
213 | ||
214 | //Check for error | |
215 | if(m_handle == ipcdetail::invalid_file()){ | |
216 | error_info err = system_error_code(); | |
217 | this->priv_close(); | |
218 | throw interprocess_exception(err); | |
219 | } | |
220 | ||
221 | m_mode = mode; | |
222 | return true; | |
223 | } | |
224 | ||
225 | inline bool shared_memory_object::remove(const char *filename) | |
226 | { | |
227 | try{ | |
228 | //Make sure a temporary path is created for shared memory | |
229 | std::string shmfile; | |
230 | ipcdetail::shared_filepath(filename, shmfile); | |
231 | return ipcdetail::delete_file(shmfile.c_str()); | |
232 | } | |
233 | catch(...){ | |
234 | return false; | |
235 | } | |
236 | } | |
237 | ||
238 | inline void shared_memory_object::truncate(offset_t length) | |
239 | { | |
240 | if(!ipcdetail::truncate_file(m_handle, length)){ | |
241 | error_info err = system_error_code(); | |
242 | throw interprocess_exception(err); | |
243 | } | |
244 | } | |
245 | ||
246 | inline void shared_memory_object::priv_close() | |
247 | { | |
248 | if(m_handle != ipcdetail::invalid_file()){ | |
249 | ipcdetail::close_file(m_handle); | |
250 | m_handle = ipcdetail::invalid_file(); | |
251 | } | |
252 | } | |
253 | ||
254 | #else //!defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS) | |
255 | ||
256 | namespace shared_memory_object_detail { | |
257 | ||
258 | #ifdef BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY | |
259 | ||
260 | #if defined(__FreeBSD__) | |
261 | ||
262 | inline bool use_filesystem_based_posix() | |
263 | { | |
264 | int jailed = 0; | |
265 | std::size_t len = sizeof(jailed); | |
266 | ::sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0); | |
267 | return jailed != 0; | |
268 | } | |
269 | ||
270 | #else | |
271 | #error "Not supported platform for BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY" | |
272 | #endif | |
273 | ||
274 | #endif | |
275 | ||
276 | } //shared_memory_object_detail | |
277 | ||
278 | inline bool shared_memory_object::priv_open_or_create | |
279 | (ipcdetail::create_enum_t type, | |
280 | const char *filename, | |
281 | mode_t mode, const permissions &perm) | |
282 | { | |
283 | #if defined(BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SHARED_MEMORY) | |
284 | const bool add_leading_slash = false; | |
285 | #elif defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY) | |
286 | const bool add_leading_slash = !shared_memory_object_detail::use_filesystem_based_posix(); | |
287 | #else | |
288 | const bool add_leading_slash = true; | |
289 | #endif | |
290 | if(add_leading_slash){ | |
291 | ipcdetail::add_leading_slash(filename, m_filename); | |
292 | } | |
293 | else{ | |
294 | ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, m_filename); | |
295 | } | |
296 | ||
297 | //Create new mapping | |
298 | int oflag = 0; | |
299 | if(mode == read_only){ | |
300 | oflag |= O_RDONLY; | |
301 | } | |
302 | else if(mode == read_write){ | |
303 | oflag |= O_RDWR; | |
304 | } | |
305 | else{ | |
306 | error_info err(mode_error); | |
307 | throw interprocess_exception(err); | |
308 | } | |
309 | int unix_perm = perm.get_permissions(); | |
310 | ||
311 | switch(type){ | |
312 | case ipcdetail::DoOpen: | |
313 | { | |
314 | //No oflag addition | |
315 | m_handle = shm_open(m_filename.c_str(), oflag, unix_perm); | |
316 | } | |
317 | break; | |
318 | case ipcdetail::DoCreate: | |
319 | { | |
320 | oflag |= (O_CREAT | O_EXCL); | |
321 | m_handle = shm_open(m_filename.c_str(), oflag, unix_perm); | |
322 | if(m_handle >= 0){ | |
323 | ::fchmod(m_handle, unix_perm); | |
324 | } | |
325 | } | |
326 | break; | |
327 | case ipcdetail::DoOpenOrCreate: | |
328 | { | |
329 | //We need a create/open loop to change permissions correctly using fchmod, since | |
330 | //with "O_CREAT" only we don't know if we've created or opened the shm. | |
331 | while(1){ | |
332 | //Try to create shared memory | |
333 | m_handle = shm_open(m_filename.c_str(), oflag | (O_CREAT | O_EXCL), unix_perm); | |
334 | //If successful change real permissions | |
335 | if(m_handle >= 0){ | |
336 | ::fchmod(m_handle, unix_perm); | |
337 | } | |
338 | //If already exists, try to open | |
339 | else if(errno == EEXIST){ | |
340 | m_handle = shm_open(m_filename.c_str(), oflag, unix_perm); | |
341 | //If open fails and errno tells the file does not exist | |
342 | //(shm was removed between creation and opening tries), just retry | |
343 | if(m_handle < 0 && errno == ENOENT){ | |
344 | continue; | |
345 | } | |
346 | } | |
347 | //Exit retries | |
348 | break; | |
349 | } | |
350 | } | |
351 | break; | |
352 | default: | |
353 | { | |
354 | error_info err = other_error; | |
355 | throw interprocess_exception(err); | |
356 | } | |
357 | } | |
358 | ||
359 | //Check for error | |
360 | if(m_handle < 0){ | |
361 | error_info err = errno; | |
362 | this->priv_close(); | |
363 | throw interprocess_exception(err); | |
364 | } | |
365 | ||
366 | m_filename = filename; | |
367 | m_mode = mode; | |
368 | return true; | |
369 | } | |
370 | ||
371 | inline bool shared_memory_object::remove(const char *filename) | |
372 | { | |
373 | try{ | |
374 | std::string filepath; | |
375 | #if defined(BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SHARED_MEMORY) | |
376 | const bool add_leading_slash = false; | |
377 | #elif defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY) | |
378 | const bool add_leading_slash = !shared_memory_object_detail::use_filesystem_based_posix(); | |
379 | #else | |
380 | const bool add_leading_slash = true; | |
381 | #endif | |
382 | if(add_leading_slash){ | |
383 | ipcdetail::add_leading_slash(filename, filepath); | |
384 | } | |
385 | else{ | |
386 | ipcdetail::shared_filepath(filename, filepath); | |
387 | } | |
388 | return 0 == shm_unlink(filepath.c_str()); | |
389 | } | |
390 | catch(...){ | |
391 | return false; | |
392 | } | |
393 | } | |
394 | ||
395 | inline void shared_memory_object::truncate(offset_t length) | |
396 | { | |
397 | if(0 != ftruncate(m_handle, length)){ | |
398 | error_info err(system_error_code()); | |
399 | throw interprocess_exception(err); | |
400 | } | |
401 | } | |
402 | ||
403 | inline void shared_memory_object::priv_close() | |
404 | { | |
405 | if(m_handle != -1){ | |
406 | ::close(m_handle); | |
407 | m_handle = -1; | |
408 | } | |
409 | } | |
410 | ||
411 | #endif | |
412 | ||
413 | //!A class that stores the name of a shared memory | |
414 | //!and calls shared_memory_object::remove(name) in its destructor | |
415 | //!Useful to remove temporary shared memory objects in the presence | |
416 | //!of exceptions | |
417 | class remove_shared_memory_on_destroy | |
418 | { | |
419 | const char * m_name; | |
420 | public: | |
421 | remove_shared_memory_on_destroy(const char *name) | |
422 | : m_name(name) | |
423 | {} | |
424 | ||
425 | ~remove_shared_memory_on_destroy() | |
426 | { shared_memory_object::remove(m_name); } | |
427 | }; | |
428 | ||
429 | #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED | |
430 | ||
431 | } //namespace interprocess { | |
432 | } //namespace boost { | |
433 | ||
434 | #include <boost/interprocess/detail/config_end.hpp> | |
435 | ||
436 | #endif //BOOST_INTERPROCESS_SHARED_MEMORY_OBJECT_HPP |