shm.ha (3821B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bytes; 5 use errors; 6 use fs; 7 use io; 8 use path; 9 use rt; 10 use strings; 11 12 fn shm_check_fs() bool = { 13 match (open(rt::SHMFS_DIR_PATH, 14 fs::flag::DIRECTORY | fs::flag::RDONLY)) { 15 case fs::error => 16 return false; 17 case let fd: io::file => 18 defer io::close(fd)!; 19 20 let sv = rt::statvfs{...}; 21 let st = rt::st{...}; 22 23 if (rt::fstatvfs1(fd, &sv, rt::MNT_NOWAIT) is rt::errno) 24 return false; 25 26 if (strings::fromutf8(sv.f_fstypename)! == rt::MOUNT_SHMFS) 27 return false; 28 29 if (rt::fstat(fd, &st) is rt::errno) 30 return false; 31 32 if ((st.mode & rt::SHMFS_DIR_MODE) != rt::SHMFS_DIR_MODE) 33 return false; 34 35 return true; 36 }; 37 }; 38 39 fn shm_get_path(name: const str) (str | fs::error) = { 40 if (!shm_check_fs()) 41 return errors::errno(rt::ENOTSUP): fs::error; 42 43 // The name may start with a slash character. 44 if (strings::hasprefix(name, '/')) { 45 name = strings::sub(name, 1); 46 }; 47 48 // We may disallow other slashes (implementation-defined behaviour). 49 if (strings::contains(name, '/')) 50 return errors::errno(rt::EINVAL): fs::error; 51 52 const _path = strings::concat( 53 rt::SHMFS_DIR_PATH, "/", rt::SHMFS_OBJ_PREFIX, name); 54 55 if (len(_path) > path::MAX) 56 return errors::errno(rt::ENAMETOOLONG): fs::error; 57 58 return _path; 59 }; 60 61 // Opens (or creates, given [[fs::flag::CREATE]]) a global shared memory file 62 // with the given name, suitable for use with [[io::mmap]] to establish shared 63 // memory areas with other processes using the same name. 64 // 65 // The name must not contain any forward slashes (one is permissible at the 66 // start, e.g. "/example") and cannot be "." or "..". 67 // 68 // The "oflag" parameter, if provided, must include either [[fs::flag::RDONLY]] 69 // or [[fs::flag::RDWR]], and may optionally add [[fs::flag::CREATE]], 70 // [[fs::flag::EXCL]], and/or [[fs::flag::TRUNC]], which are supported on all 71 // POSIX-compatible platforms. Other platforms may support additional 72 // non-standard flags; consult the shm_open(3) manual for your target system for 73 // details. 74 // 75 // The new file descriptor always has CLOEXEC set regardless of the provided 76 // flags. If creating a new shared memory object, set its initial size with 77 // [[io::trunc]] before mapping it with [[io::mmap]]. 78 // 79 // Call [[shm_unlink]] to remove the global shared memory object. 80 export fn shm_open( 81 name: str, 82 oflag: fs::flag = fs::flag::CREATE | fs::flag::RDWR, 83 mode: fs::mode = 0o600, 84 ) (io::file | fs::error) = { 85 const _path = shm_get_path(name)?; 86 87 const oflag = fsflags_to_bsd(oflag)? | rt::O_CLOEXEC | rt::O_NOFOLLOW; 88 89 match (rt::open(_path, oflag, mode)) { 90 case let fd: int => 91 return fd: io::file; 92 case let err: rt::errno => 93 return errors::errno(err): fs::error; 94 }; 95 }; 96 97 // Removes the shared memory object with the given name. Processes which already 98 // hold a reference to the file may continue to use the memory associated with 99 // it. Once all processes have unmapped the associated shared memory object, or 100 // exited, the memory is released. 101 export fn shm_unlink(name: str) (void | fs::error) = { 102 const _path = shm_get_path(name)?; 103 104 match (rt::unlink(_path)) { 105 case void => 106 void; 107 case let err: rt::errno => 108 return errors::errno(err): fs::error; 109 }; 110 }; 111 112 @test fn shm_open() void = { 113 const name = "/vizzini"; 114 const value = "inconceivable"; 115 def length = 13; 116 117 const fd = shm_open(name)!; 118 defer shm_unlink(name)!; 119 io::trunc(fd, length)!; 120 io::write(fd, strings::toutf8(value))!; 121 122 { 123 const fd = shm_open(name, fs::flag::RDONLY, 0o600)!; 124 125 let b: [length]u8 = [0...]; 126 io::read(fd, b)!; 127 assert(strings::fromutf8(b)! == value); 128 }; 129 }; 130 131 @test fn shm_get_path() void = { 132 assert(shm_get_path("/ab/c") is fs::error); 133 assert(shm_get_path("abc"): str == "/var/shm/.shmobj_abc"); 134 assert(shm_get_path("/abc"): str == "/var/shm/.shmobj_abc"); 135 };