hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };