├── CREDITS ├── README ├── VERSION ├── doc ├── examples │ ├── .gitkeep │ ├── .gitignore │ ├── Makefile │ └── shmcat.cc ├── manual │ └── .gitkeep └── doxygen │ ├── .gitignore │ └── mainpage.h ├── etc ├── aclocal │ ├── .gitkeep │ └── ax_cxx_compile_stdcxx_11.m4 ├── debian │ └── .gitkeep ├── macports │ └── .gitkeep ├── .gitignore ├── coverity │ └── model.cc └── doap.rdf ├── AUTHORS ├── autogen.sh ├── src ├── .gitignore ├── Makefile.am ├── posix++ │ ├── .gitignore │ ├── feature.cc │ ├── module.cc │ ├── version.cc │ ├── group.cc │ ├── user.cc │ ├── process_group.cc │ ├── mode.cc │ ├── mode.h │ ├── module.h │ ├── feature.h │ ├── thread.cc │ ├── thread.h │ ├── version.h.in │ ├── process_group.h │ ├── group.h │ ├── user.h │ ├── stdio.h │ ├── pathname.cc │ ├── named_pipe.h │ ├── Makefile.am │ ├── stdio.cc │ ├── named_pipe.cc │ ├── local_socket.h │ ├── process.cc │ ├── file.h │ ├── file.cc │ ├── socket.h │ ├── memory_mapping.cc │ ├── process.h │ ├── message_queue.h │ ├── pathname.h │ ├── memory_mapping.h │ ├── semaphore.h │ ├── message_queue.cc │ ├── error.cc │ ├── directory.h │ ├── error.h │ ├── mapped_file.h │ ├── sysv_segment.h │ ├── socket.cc │ ├── sysv_segment.cc │ ├── mapped_file.cc │ ├── descriptor.h │ ├── directory.cc │ ├── local_socket.cc │ └── descriptor.cc └── posix++.h ├── lib ├── .gitignore ├── Makefile.am ├── fdopendir.c ├── mkdirat.c ├── mkfifoat.c ├── symlinkat.c ├── fstatat.c ├── renameat.c ├── linkat.c ├── openat.c ├── unlinkat.c ├── readlinkat.c ├── symlinkat.h ├── mkdirat.h ├── mkfifoat.h ├── renameat.h ├── linkat.h ├── fstatat.h ├── fdopendir.h ├── libcompat.h ├── openat.h ├── unlinkat.h └── readlinkat.h ├── Makefile.am ├── TODO ├── test ├── check_file.cc ├── check_user.cc ├── check_error.cc ├── check_group.cc ├── check_feature.cc ├── check_module.cc ├── check_process.cc ├── check_socket.cc ├── check_stdio.cc ├── check_version.cc ├── check_pathname.cc ├── check_directory.cc ├── check_mapped_file.cc ├── check_descriptor.cc ├── check_named_pipe.cc ├── check_message_queue.cc ├── check_process_group.cc ├── .gitignore ├── check_sysv_segment.cc ├── check_local_socket.cc ├── check_memory_mapping.cc ├── Makefile.am └── check_semaphore.cc ├── .gitignore ├── configure.sh ├── .travis.yml ├── UNLICENSE ├── configure.ac └── README.rst /CREDITS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.rst -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.0 2 | -------------------------------------------------------------------------------- /doc/examples/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/manual/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/aclocal/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/debian/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/macports/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | * Arto Bendiken 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf --force --install 3 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | config.h 2 | config.h.in 3 | config.h.in~ 4 | -------------------------------------------------------------------------------- /doc/doxygen/.gitignore: -------------------------------------------------------------------------------- 1 | html 2 | latex 3 | man 4 | rtf 5 | xml 6 | -------------------------------------------------------------------------------- /doc/examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | shmcat 3 | stderr 4 | stdout 5 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = posix++ 2 | 3 | include_HEADERS = posix++.h 4 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | .deps/ 2 | .libs/ 3 | .dirstamp 4 | *.a 5 | *.o 6 | *.la 7 | *.lo 8 | -------------------------------------------------------------------------------- /etc/.gitignore: -------------------------------------------------------------------------------- 1 | doap.html 2 | doap.json 3 | doap.jsonld 4 | doap.nq 5 | doap.nt 6 | doap.ttl 7 | doap.xml 8 | -------------------------------------------------------------------------------- /src/posix++/.gitignore: -------------------------------------------------------------------------------- 1 | .deps/ 2 | .libs/ 3 | .dirstamp 4 | *.a 5 | *.o 6 | *.la 7 | *.lo 8 | version.h 9 | -------------------------------------------------------------------------------- /lib/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_LIBRARIES = libcompat.a 2 | 3 | libcompat_a_SOURCES = 4 | libcompat_a_LIBADD = $(LIBOBJS) $(ALLOCA) 5 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = lib src test #doc 2 | EXTRA_DIST = AUTHORS CREDITS README UNLICENSE VERSION 3 | ACLOCAL_AMFLAGS = -I etc/aclocal 4 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - posix::file#uid()/owner() 2 | - posix::file#gid()/group() 3 | - posix::file#is_readable() 4 | - posix::file#is_writable() 5 | - posix::file#is_executable() 6 | -------------------------------------------------------------------------------- /src/posix++/feature.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "feature.h" 8 | 9 | using namespace posix; 10 | -------------------------------------------------------------------------------- /src/posix++/module.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "module.h" 8 | 9 | using namespace posix; 10 | -------------------------------------------------------------------------------- /src/posix++/version.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "version.h" 8 | 9 | using namespace posix; 10 | -------------------------------------------------------------------------------- /etc/coverity/model.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | /** 4 | * @file 5 | * 6 | * Coverity Scan model for libposix++. 7 | * 8 | * @see https://scan.coverity.com/tune 9 | */ 10 | -------------------------------------------------------------------------------- /test/check_file.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::file */ 6 | 7 | TEST_CASE("test_file") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_user.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::user */ 6 | 7 | TEST_CASE("test_user") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .tmp/ 3 | pkg/ 4 | tmp/ 5 | *~ 6 | 7 | # GNU Autotools 8 | etc/aclocal/ 9 | Makefile 10 | Makefile.in 11 | aclocal.m4 12 | autom4te.cache/ 13 | config.log 14 | config.status 15 | configure 16 | libtool 17 | stamp-h1 18 | -------------------------------------------------------------------------------- /test/check_error.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::error */ 6 | 7 | TEST_CASE("test_error") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_group.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::group */ 6 | 7 | TEST_CASE("test_group") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_feature.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::feature */ 6 | 7 | TEST_CASE("test_feature") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_module.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::module */ 6 | 7 | TEST_CASE("test_module") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_process.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::process */ 6 | 7 | TEST_CASE("test_process") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_socket.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::socket */ 6 | 7 | TEST_CASE("test_socket") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_stdio.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::standard_* */ 6 | 7 | TEST_CASE("test_stdio") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_version.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::version */ 6 | 7 | TEST_CASE("test_version") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_pathname.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::pathname */ 6 | 7 | TEST_CASE("test_pathname") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_directory.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::directory */ 6 | 7 | TEST_CASE("test_directory") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_mapped_file.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::mapped_file */ 6 | 7 | TEST_CASE("test_file") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /doc/examples/Makefile: -------------------------------------------------------------------------------- 1 | CPPFLAGS = -I../../src 2 | CXXFLAGS = -std=c++11 -Wall -Wextra 3 | LDFLAGS = -L../../src/posix++/.libs -lposix++ 4 | 5 | PROGRAMS = shmcat 6 | 7 | all: $(PROGRAMS) 8 | 9 | clean: 10 | $(RM) $(PROGRAMS) *.o *~ 11 | 12 | .PHONY: all clean 13 | -------------------------------------------------------------------------------- /test/check_descriptor.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::descriptor */ 6 | 7 | TEST_CASE("test_descriptor") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_named_pipe.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::named_pipe */ 6 | 7 | TEST_CASE("test_named_pipe") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /doc/doxygen/mainpage.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @mainpage libposix++ API Documentation 3 | * 4 | * This is API documentation automatically generated from the source code 5 | * using the Doxygen documentation generation tool. 6 | * 7 | * @see https://github.com/dryproject/libposixxx 8 | */ 9 | -------------------------------------------------------------------------------- /test/check_message_queue.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::message_queue */ 6 | 7 | TEST_CASE("test_message_queue") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /test/check_process_group.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::process_group */ 6 | 7 | TEST_CASE("test_process_group") { 8 | // TODO 9 | } 10 | -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | #export CC='gcc' 3 | #export CXX='g++' 4 | export CC='clang' 5 | export CXX='clang++ --stdlib=libc++' 6 | export CPPFLAGS='' 7 | export CFLAGS='-g -Os' 8 | export CXXFLAGS='-g -Os' 9 | export LDFLAGS='' 10 | ./configure --disable-mqueue --disable-semaphore $* 11 | -------------------------------------------------------------------------------- /lib/fdopendir.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "fdopendir.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | #include /* for NULL */ 7 | 8 | DIR* 9 | fdopendir(const int fd) { 10 | (void)fd; 11 | 12 | return errno = ENOSYS, NULL; // TODO 13 | } 14 | -------------------------------------------------------------------------------- /lib/mkdirat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "mkdirat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | 7 | int 8 | mkdirat(const int dirfd, 9 | const char* const pathname, 10 | const mode_t mode) { 11 | return errno = ENOSYS, -1; // TODO 12 | } 13 | -------------------------------------------------------------------------------- /lib/mkfifoat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "mkfifoat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | 7 | int 8 | mkfifoat(const int dirfd, 9 | const char* const pathname, 10 | const mode_t mode) { 11 | return errno = ENOSYS, -1; // TODO 12 | } 13 | -------------------------------------------------------------------------------- /lib/symlinkat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "symlinkat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | 7 | int 8 | symlinkat(const char* const oldpath, 9 | const int newdirfd, 10 | const char* const newpath) { 11 | return errno = ENOSYS, -1; // TODO 12 | } 13 | -------------------------------------------------------------------------------- /lib/fstatat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "fstatat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | 7 | int 8 | fstatat(const int dirfd, 9 | const char* const pathname, 10 | struct stat* const buf, 11 | const int flags) { 12 | return errno = ENOSYS, -1; // TODO 13 | } 14 | -------------------------------------------------------------------------------- /lib/renameat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "renameat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | 7 | int 8 | renameat(const int olddirfd, 9 | const char* const oldpath, 10 | const int newdirfd, 11 | const char* const newpath) { 12 | return errno = ENOSYS, -1; // TODO 13 | } 14 | -------------------------------------------------------------------------------- /lib/linkat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "linkat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | 7 | int 8 | linkat(const int olddirfd, 9 | const char* const oldpath, 10 | const int newdirfd, 11 | const char* const newpath, 12 | const int flags) { 13 | return errno = ENOSYS, -1; // TODO 14 | } 15 | -------------------------------------------------------------------------------- /src/posix++/group.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "group.h" 8 | 9 | #include "error.h" 10 | 11 | #include /* for assert() */ 12 | #include /* for errno */ 13 | #include /* for getgrnam(), getgrgid() */ 14 | 15 | using namespace posix; 16 | -------------------------------------------------------------------------------- /src/posix++/user.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "user.h" 8 | 9 | #include "error.h" 10 | 11 | #include /* for assert() */ 12 | #include /* for errno */ 13 | #include /* for getpwnam(), getpwuid() */ 14 | 15 | using namespace posix; 16 | -------------------------------------------------------------------------------- /src/posix++/process_group.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "process_group.h" 8 | 9 | #include "error.h" 10 | 11 | #include /* for assert() */ 12 | #include /* for errno */ 13 | #include /* for std::kill() */ 14 | 15 | using namespace posix; 16 | -------------------------------------------------------------------------------- /lib/openat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "openat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | #include /* for mode_t */ 7 | 8 | int 9 | openat(const int dirfd, 10 | const char* const pathname, 11 | const int flags, ...) { 12 | (void)dirfd, (void)pathname, (void)flags; 13 | 14 | return errno = ENOSYS, -1; // TODO 15 | } 16 | -------------------------------------------------------------------------------- /lib/unlinkat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "unlinkat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | #include /* for unlink() */ 7 | 8 | int 9 | unlinkat(const int dirfd, 10 | const char* const pathname, 11 | const int flags) { 12 | (void)dirfd, (void)pathname, (void)flags; 13 | 14 | return errno = ENOSYS, -1; // TODO 15 | } 16 | -------------------------------------------------------------------------------- /src/posix++/mode.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "mode.h" 8 | 9 | #include /* for mode_t */ 10 | 11 | using namespace posix; 12 | 13 | /* 14 | * x86-64 Linux 3.x: uint32_t. 15 | * x86-64 Darwin 11.x: uint16_t. 16 | */ 17 | static_assert(sizeof(mode_t) <= sizeof(mode), "sizeof(mode_t) > sizeof(mode)"); 18 | -------------------------------------------------------------------------------- /lib/readlinkat.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "readlinkat.h" 4 | 5 | #include /* for ENOSYS, errno */ 6 | #include /* for readlink() */ 7 | 8 | int 9 | readlinkat(const int dirfd, 10 | const char* const pathname, 11 | char* const buffer, 12 | const size_t buffer_size) { 13 | (void)dirfd, (void)pathname, (void)buffer, (void)buffer_size; 14 | 15 | return errno = ENOSYS, -1; // TODO 16 | } 17 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | .deps/ 2 | .libs/ 3 | .dirstamp 4 | *.a 5 | *.o 6 | *.la 7 | *.lo 8 | *.log 9 | *.trs 10 | check_descriptor 11 | check_directory 12 | check_error 13 | check_feature 14 | check_file 15 | check_group 16 | check_local_socket 17 | check_mapped_file 18 | check_memory_mapping 19 | check_message_queue 20 | check_module 21 | check_named_pipe 22 | check_pathname 23 | check_process 24 | check_process_group 25 | check_semaphore 26 | check_stdio 27 | check_socket 28 | check_sysv_segment 29 | check_user 30 | check_version 31 | -------------------------------------------------------------------------------- /src/posix++/mode.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MODE_H 4 | #define POSIXXX_MODE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace posix { 13 | using mode = unsigned int; 14 | } 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | #endif /* POSIXXX_MODE_H */ 19 | -------------------------------------------------------------------------------- /src/posix++/module.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MODULE_H 4 | #define POSIXXX_MODULE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace posix { 13 | namespace module { 14 | } 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | #endif /* POSIXXX_MODULE_H */ 20 | -------------------------------------------------------------------------------- /src/posix++/feature.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_FEATURE_H 4 | #define POSIXXX_FEATURE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace posix { 13 | namespace feature { 14 | } 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | #endif /* POSIXXX_FEATURE_H */ 20 | -------------------------------------------------------------------------------- /test/check_sysv_segment.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::sysv_segment */ 6 | 7 | #include /* for getpagesize(), getpid() */ 8 | 9 | using namespace posix; 10 | 11 | TEST_CASE("test_sysv_segment") { 12 | sysv_segment shm = sysv_segment::create(getpid(), getpagesize(), 0600); 13 | REQUIRE(!shm.is_attached()); 14 | shm.attach(); 15 | REQUIRE(shm.is_attached()); 16 | shm.detach(); 17 | REQUIRE(!shm.is_attached()); 18 | shm.remove(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/symlinkat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_SYMLINKAT_H 4 | #define POSIXXX_SYMLINKAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 symlinkat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | int symlinkat(const char* oldpath, int newdirfd, const char* newpath); 17 | 18 | #ifdef __cplusplus 19 | } /* extern "C" */ 20 | #endif 21 | 22 | #endif /* POSIXXX_SYMLINKAT_H */ 23 | -------------------------------------------------------------------------------- /lib/mkdirat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MKDIRAT_H 4 | #define POSIXXX_MKDIRAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 mkdirat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html 10 | */ 11 | 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | int mkdirat(int dirfd, const char* pathname, mode_t mode); 19 | 20 | #ifdef __cplusplus 21 | } /* extern "C" */ 22 | #endif 23 | 24 | #endif /* POSIXXX_MKDIRAT_H */ 25 | -------------------------------------------------------------------------------- /lib/mkfifoat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MKFIFOAT_H 4 | #define POSIXXX_MKFIFOAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 mkfifoat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html 10 | */ 11 | 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | int mkfifoat(int dirfd, const char* pathname, mode_t mode); 19 | 20 | #ifdef __cplusplus 21 | } /* extern "C" */ 22 | #endif 23 | 24 | #endif /* POSIXXX_MKFIFOAT_H */ 25 | -------------------------------------------------------------------------------- /lib/renameat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_RENAMEAT_H 4 | #define POSIXXX_RENAMEAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 renameat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | int renameat( 17 | int olddirfd, const char* oldpath, 18 | int newdirfd, const char* newpath); 19 | 20 | #ifdef __cplusplus 21 | } /* extern "C" */ 22 | #endif 23 | 24 | #endif /* POSIXXX_RENAMEAT_H */ 25 | -------------------------------------------------------------------------------- /src/posix++/thread.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "thread.h" 8 | 9 | #ifdef __linux__ 10 | #include /* for SYS_gettid */ 11 | #include /* for syscall() */ 12 | #endif 13 | 14 | using namespace posix; 15 | 16 | pid_t 17 | posix::gettid() noexcept { 18 | #ifdef __linux__ 19 | static __thread pid_t tid = 0; 20 | if (!tid) { 21 | tid = syscall(SYS_gettid); 22 | } 23 | return tid; 24 | #else 25 | return 0; /* not supported */ 26 | #endif 27 | } 28 | -------------------------------------------------------------------------------- /lib/linkat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_LINKAT_H 4 | #define POSIXXX_LINKAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 linkat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | int linkat( 17 | int olddirfd, const char* oldpath, 18 | int newdirfd, const char* newpath, 19 | int flags); 20 | 21 | #ifdef __cplusplus 22 | } /* extern "C" */ 23 | #endif 24 | 25 | #endif /* POSIXXX_LINKAT_H */ 26 | -------------------------------------------------------------------------------- /lib/fstatat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_FSTATAT_H 4 | #define POSIXXX_FSTATAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 fstatat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html 10 | */ 11 | 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #define AT_SYMLINK_NOFOLLOW 0x100 19 | 20 | int fstatat(int dirfd, const char* pathname, struct stat* buf, int flags); 21 | 22 | #ifdef __cplusplus 23 | } /* extern "C" */ 24 | #endif 25 | 26 | #endif /* POSIXXX_FSTATAT_H */ 27 | -------------------------------------------------------------------------------- /src/posix++/thread.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_THREAD_H 4 | #define POSIXXX_THREAD_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for pid_t */ 13 | 14 | namespace posix { 15 | /** 16 | * @note This is Linux-specific and should not be used in portable programs. 17 | */ 18 | pid_t gettid() noexcept; 19 | } 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | #endif /* POSIXXX_THREAD_H */ 24 | -------------------------------------------------------------------------------- /src/posix++/version.h.in: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_VERSION_H 4 | #define POSIXXX_VERSION_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for std::string */ 13 | 14 | namespace posix { 15 | namespace version { 16 | const std::string string = "@PACKAGE_VERSION@"; 17 | const short major = @PACKAGE_VERSION_MAJOR@; 18 | const short minor = @PACKAGE_VERSION_MINOR@; 19 | const short patch = @PACKAGE_VERSION_PATCH@; 20 | }; 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #endif /* POSIXXX_VERSION_H */ 26 | -------------------------------------------------------------------------------- /lib/fdopendir.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_FDOPENDIR_H 4 | #define POSIXXX_FDOPENDIR_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 fdopendir() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/opendir.html 10 | */ 11 | 12 | #include /* for DIR */ 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | * Opens the directory associated with a file descriptor. 20 | * 21 | * @param fd 22 | * @return a pointer to the directory stream, or `NULL` on error 23 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/opendir.html 24 | */ 25 | DIR* fdopendir(int fd); 26 | 27 | #ifdef __cplusplus 28 | } /* extern "C" */ 29 | #endif 30 | 31 | #endif /* POSIXXX_FDOPENDIR_H */ 32 | -------------------------------------------------------------------------------- /doc/examples/shmcat.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include 4 | #include 5 | 6 | #include /* for std::size_t */ 7 | #include /* for std::fwrite() */ 8 | #include /* for std::atoi() */ 9 | #include /* for sysconf() */ 10 | 11 | static const std::size_t pagesize = sysconf(_SC_PAGESIZE); 12 | 13 | int 14 | main(int argc, char* argv[]) { 15 | /* For each command-line argument: */ 16 | for (int i = 1; i < argc; i++) { 17 | /* Convert the argument to an integer: */ 18 | const int arg = std::atoi(argv[i]); 19 | 20 | /* Attach to the SysV shared-memory segment: */ 21 | posix::sysv_segment shm(arg); 22 | shm.attach(); 23 | 24 | /* Write the first page to standard output: */ 25 | std::fwrite(shm.data(), pagesize, 1, stdout); 26 | } 27 | return EXIT_SUCCESS; 28 | } 29 | -------------------------------------------------------------------------------- /test/check_local_socket.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::local_socket */ 6 | #include /* for posix::pathname */ 7 | 8 | #include /* for AF_LOCAL, SOCK_STREAM */ 9 | 10 | using namespace posix; 11 | 12 | TEST_CASE("test_pair") { 13 | auto sp = local_socket::pair(); 14 | auto s1 = sp.first; 15 | auto s2 = sp.second; 16 | 17 | REQUIRE(s1.valid()); 18 | REQUIRE(s2.valid()); 19 | 20 | #ifdef __linux__ 21 | // FIXME: this fails on Mac OS X 10.7. 22 | REQUIRE(s1.domain() == AF_LOCAL); 23 | #endif 24 | REQUIRE(s1.type() == SOCK_STREAM); 25 | REQUIRE(s1.protocol() == 0); 26 | 27 | s1.send("Hello, world!"); 28 | s1.close_write(); 29 | 30 | REQUIRE(s2.recv_string() == "Hello, world!"); 31 | } 32 | 33 | TEST_CASE("test_connect") { 34 | // TODO 35 | } 36 | -------------------------------------------------------------------------------- /lib/libcompat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef LIBCOMPAT_H 4 | #define LIBCOMPAT_H 5 | 6 | #ifdef __APPLE__ 7 | #include /* for PATH_MAX */ 8 | #endif 9 | 10 | #ifndef HAVE_FDOPENDIR 11 | #include "fdopendir.h" 12 | #endif 13 | 14 | #ifndef HAVE_FSTATAT 15 | #include "fstatat.h" 16 | #endif 17 | 18 | #ifndef HAVE_LINKAT 19 | #include "linkat.h" 20 | #endif 21 | 22 | #ifndef HAVE_MKDIRAT 23 | #include "mkdirat.h" 24 | #endif 25 | 26 | #ifndef HAVE_MKFIFOAT 27 | #include "mkfifoat.h" 28 | #endif 29 | 30 | #ifndef HAVE_OPENAT 31 | #include "openat.h" 32 | #endif 33 | 34 | #ifndef HAVE_READLINKAT 35 | #include "readlinkat.h" 36 | #endif 37 | 38 | #ifndef HAVE_RENAMEAT 39 | #include "renameat.h" 40 | #endif 41 | 42 | #ifndef HAVE_SYMLINKAT 43 | #include "symlinkat.h" 44 | #endif 45 | 46 | #ifndef HAVE_UNLINKAT 47 | #include "unlinkat.h" 48 | #endif 49 | 50 | #endif /* LIBCOMPAT_H */ 51 | -------------------------------------------------------------------------------- /test/check_memory_mapping.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::memory_mapping */ 6 | 7 | using namespace posix; 8 | 9 | static std::uint8_t buffer[0x1000] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 10 | 11 | TEST_CASE("test_size") { 12 | REQUIRE(memory_mapping(buffer, sizeof(buffer)).size() == sizeof(buffer)); 13 | } 14 | 15 | TEST_CASE("test_data") { 16 | REQUIRE(memory_mapping(buffer, sizeof(buffer)).data() == buffer); 17 | REQUIRE(memory_mapping(buffer, sizeof(buffer)).data() == buffer); 18 | REQUIRE(memory_mapping(buffer, sizeof(buffer)).data(1) == buffer + 1); 19 | } 20 | 21 | TEST_CASE("test_operator_bool") { 22 | #ifdef NDEBUG 23 | REQUIRE(!memory_mapping(nullptr, 0)); /* asserts */ 24 | #endif 25 | REQUIRE(memory_mapping(buffer, sizeof(buffer))); 26 | } 27 | 28 | TEST_CASE("test_operator_at") { 29 | REQUIRE(memory_mapping(buffer, sizeof(buffer))[1] == buffer[1]); 30 | } 31 | -------------------------------------------------------------------------------- /lib/openat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_OPENAT_H 4 | #define POSIXXX_OPENAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 openat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * A special `dirfd` value used to indicate that `openat()` should operate 18 | * relative to the current working directory. 19 | */ 20 | #ifndef AT_FDCWD 21 | #define AT_FDCWD -100 22 | #endif 23 | 24 | /** 25 | * Opens a file relative to a directory file descriptor. 26 | * 27 | * @param dirfd 28 | * @param pathname 29 | * @param flags 30 | * @param mode 31 | * @return a new file descriptor, or -1 on error 32 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html 33 | */ 34 | int openat(int dirfd, const char* pathname, int flags, ...); 35 | 36 | #ifdef __cplusplus 37 | } /* extern "C" */ 38 | #endif 39 | 40 | #endif /* POSIXXX_OPENAT_H */ 41 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS += $(TEST_CPPFLAGS) 2 | AM_CXXFLAGS += $(TEST_CXXFLAGS) 3 | AM_LDFLAGS += $(TEST_LDFLAGS) 4 | 5 | LDADD = $(top_srcdir)/src/posix++/libposix++.la 6 | 7 | check_PROGRAMS = \ 8 | check_descriptor \ 9 | check_directory \ 10 | check_error \ 11 | check_feature \ 12 | check_file \ 13 | check_group \ 14 | check_mapped_file \ 15 | check_memory_mapping \ 16 | check_module \ 17 | check_named_pipe \ 18 | check_pathname \ 19 | check_process \ 20 | check_process_group \ 21 | check_user \ 22 | check_version 23 | 24 | if !DISABLE_MQUEUE 25 | check_PROGRAMS += check_message_queue 26 | endif 27 | 28 | if !DISABLE_SEMAPHORE 29 | check_PROGRAMS += check_semaphore 30 | endif 31 | 32 | if !DISABLE_STDIO 33 | check_PROGRAMS += check_stdio 34 | endif 35 | 36 | if !DISABLE_SOCKET 37 | check_PROGRAMS += check_socket check_local_socket 38 | endif 39 | 40 | if !DISABLE_SYSV 41 | check_PROGRAMS += check_sysv_segment 42 | endif 43 | 44 | TESTS = $(check_PROGRAMS) 45 | 46 | AM_DEFAULT_SOURCE_EXT = .cc 47 | -------------------------------------------------------------------------------- /test/check_semaphore.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #include "catch.hpp" 4 | 5 | #include /* for posix::semaphore */ 6 | 7 | using namespace posix; 8 | 9 | TEST_CASE("constructor") { 10 | REQUIRE_NOTHROW(unnamed_semaphore{}); 11 | REQUIRE_NOTHROW(unnamed_semaphore{0}); 12 | REQUIRE_NOTHROW(unnamed_semaphore{1}); 13 | } 14 | 15 | TEST_CASE("#value()") { 16 | REQUIRE(unnamed_semaphore{}.value() == 0); 17 | REQUIRE(unnamed_semaphore{0}.value() == 0); 18 | REQUIRE(unnamed_semaphore{1}.value() == 1); 19 | } 20 | 21 | TEST_CASE("#destroy()") { 22 | /* It's currently unsafe to call #destroy() explicitly, as the destructor 23 | * will also call it and invoking it twice leads to undefined behavior. */ 24 | } 25 | 26 | TEST_CASE("#wait()") { 27 | REQUIRE_NOTHROW(unnamed_semaphore{1}.wait()); /* won't block */ 28 | } 29 | 30 | TEST_CASE("#try_wait()") { 31 | REQUIRE(unnamed_semaphore{0}.try_wait() == false); 32 | REQUIRE(unnamed_semaphore{1}.try_wait() == true); 33 | } 34 | 35 | TEST_CASE("#notify()") { 36 | REQUIRE_NOTHROW(unnamed_semaphore{}.notify()); 37 | } 38 | -------------------------------------------------------------------------------- /lib/unlinkat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_UNLINKAT_H 4 | #define POSIXXX_UNLINKAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 unlinkat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * A special `dirfd` value used to indicate that `unlinkat()` should operate 18 | * relative to the current working directory. 19 | */ 20 | #ifndef AT_FDCWD 21 | #define AT_FDCWD -100 22 | #endif 23 | 24 | #ifndef AT_REMOVEDIR 25 | #define AT_REMOVEDIR 0x200 26 | #endif 27 | 28 | /** 29 | * Removes a file or subdirectory relative to a directory file descriptor. 30 | * 31 | * @param dirfd 32 | * @param pathname 33 | * @param flags 34 | * @return zero on success, or -1 on error 35 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html 36 | */ 37 | int unlinkat(int dirfd, const char* pathname, int flags); 38 | 39 | #ifdef __cplusplus 40 | } /* extern "C" */ 41 | #endif 42 | 43 | #endif /* POSIXXX_UNLINKAT_H */ 44 | -------------------------------------------------------------------------------- /lib/readlinkat.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_READLINKAT_H 4 | #define POSIXXX_READLINKAT_H 5 | 6 | /** 7 | * A public domain implementation of the POSIX.1-2008 readlinkat() function. 8 | * 9 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html 10 | */ 11 | 12 | #include /* for size_t */ 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | * A special `dirfd` value used to indicate that `readlinkat()` should operate 20 | * relative to the current working directory. 21 | */ 22 | #ifndef AT_FDCWD 23 | #define AT_FDCWD -100 24 | #endif 25 | 26 | /** 27 | * Reads a symlink relative to a directory file descriptor. 28 | * 29 | * @param dirfd 30 | * @param pathname 31 | * @param buffer 32 | * @param buffer_size 33 | * @return the number of bytes placed in `buffer`, or -1 on error 34 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html 35 | */ 36 | int readlinkat(int dirfd, const char* pathname, char* buffer, size_t buffer_size); 37 | 38 | #ifdef __cplusplus 39 | } /* extern "C" */ 40 | #endif 41 | 42 | #endif /* POSIXXX_READLINKAT_H */ 43 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # https://travis-ci.org/dryproject/libposix 2 | language: cpp 3 | compiler: 4 | - clang 5 | - gcc 6 | before_install: 7 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 8 | - sudo apt-get update -qq 9 | - if [ "$CXX" = "clang++" ]; then sudo apt-get install -qq libstdc++-4.8-dev; fi 10 | - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi 11 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi 12 | before_script: 13 | - ./autogen.sh 14 | script: 15 | - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ./configure --disable-mqueue && make && make check; fi 16 | branches: 17 | only: 18 | - master 19 | - coverity 20 | 21 | # https://scan.coverity.com/projects/3229 22 | env: 23 | global: 24 | - secure: "AMy6elgwntjtn57FBtbZqSKcG5sDdPOsWgFrorU7xn1N4FZzv8jumcL7hAUpKv3bN9cKv4upNlh2una1bNXj3DNUoGguewo4Oln0SE20aBMXqvUMB7XuON1SMwTiY9MHqvUiJE9TLH3qj6+unRM3pxglN66pWtJKpwGmzsT5dDE=" # COVERITY_SCAN_TOKEN 25 | addons: 26 | coverity_scan: 27 | project: 28 | name: dryproject/libposix 29 | notification_email: arto@bendiken.net 30 | build_command_prepend: cov-configure --comptype gcc --compiler $CC && ./configure 31 | build_command: make 32 | branch_pattern: coverity 33 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /src/posix++/process_group.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_PROCESS_GROUP_H 4 | #define POSIXXX_PROCESS_GROUP_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace posix { 13 | using process_group_id = unsigned int; 14 | class process_group; 15 | } 16 | 17 | /** 18 | * Represents a POSIX process group. 19 | * 20 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_290 21 | */ 22 | class posix::process_group { 23 | public: 24 | /** 25 | * Constructor. 26 | */ 27 | process_group(const process_group_id id) : _id(id) {} 28 | 29 | /** 30 | * Returns the integer identifier for this process group. 31 | */ 32 | inline process_group_id id() const { 33 | return _id; 34 | } 35 | 36 | protected: 37 | /** 38 | * The unique positive integer identifier representing a process group 39 | * during its lifetime. 40 | * 41 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_291 42 | */ 43 | process_group_id _id = 0; 44 | }; 45 | 46 | #endif /* POSIXXX_PROCESS_GROUP_H */ 47 | -------------------------------------------------------------------------------- /src/posix++.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_H 4 | #define POSIXXX_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | /** 13 | * libposix++ 14 | */ 15 | 16 | namespace posix {} 17 | 18 | #include "posix++/descriptor.h" 19 | #include "posix++/directory.h" 20 | #include "posix++/error.h" 21 | #include "posix++/feature.h" 22 | #include "posix++/file.h" 23 | #include "posix++/group.h" 24 | #include "posix++/local_socket.h" 25 | #include "posix++/mapped_file.h" 26 | #include "posix++/memory_mapping.h" 27 | #include "posix++/message_queue.h" 28 | #include "posix++/mode.h" 29 | #include "posix++/module.h" 30 | #include "posix++/named_pipe.h" 31 | #include "posix++/pathname.h" 32 | #include "posix++/process.h" 33 | #include "posix++/process_group.h" 34 | #include "posix++/semaphore.h" 35 | #include "posix++/socket.h" 36 | #include "posix++/stdio.h" 37 | #include "posix++/sysv_segment.h" 38 | #include "posix++/thread.h" 39 | #include "posix++/user.h" 40 | #include "posix++/version.h" 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | 44 | #endif /* POSIXXX_H */ 45 | -------------------------------------------------------------------------------- /src/posix++/group.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_GROUP_H 4 | #define POSIXXX_GROUP_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace posix { 13 | class group; 14 | using group_id = unsigned int; 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | /** 20 | * Represents a POSIX group. 21 | * 22 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_187 23 | */ 24 | class posix::group { 25 | public: 26 | /** 27 | * Constructor. 28 | */ 29 | group(const group_id id) noexcept 30 | : _id(id) {} 31 | 32 | /** 33 | * Returns the integer identifier for this group. 34 | * 35 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_189 36 | */ 37 | inline group_id id() const noexcept { 38 | return _id; 39 | } 40 | 41 | protected: 42 | /** 43 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_188 44 | */ 45 | group_id _id = 0; 46 | }; 47 | 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | #endif /* POSIXXX_GROUP_H */ 52 | -------------------------------------------------------------------------------- /src/posix++/user.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_USER_H 4 | #define POSIXXX_USER_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace posix { 13 | class user; 14 | using user_id = unsigned int; 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | /** 20 | * Represents a POSIX user. 21 | * 22 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_424 23 | */ 24 | class posix::user { 25 | public: 26 | /** 27 | * Constructor. 28 | */ 29 | user(const user_id id) noexcept 30 | : _id(id) {} 31 | 32 | /** 33 | * Returns the integer identifier for this user. 34 | * 35 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_430 36 | */ 37 | inline user_id id() const noexcept { 38 | return _id; 39 | } 40 | 41 | /** 42 | * Determines whether this is the superuser (aka `root`). 43 | */ 44 | inline bool is_root() const noexcept { 45 | return _id == 0; 46 | } 47 | 48 | protected: 49 | /** 50 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_430 51 | */ 52 | user_id _id = 0; 53 | }; 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | 57 | #endif /* POSIXXX_USER_H */ 58 | -------------------------------------------------------------------------------- /src/posix++/stdio.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_STDIO_H 4 | #define POSIXXX_STDIO_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for std::size_t */ 13 | #include /* for std::string */ 14 | 15 | namespace posix { 16 | struct descriptor; // @see 17 | 18 | /** 19 | * Returns a reference to the descriptor for the standard input stream. 20 | */ 21 | const descriptor& standard_input() noexcept; 22 | 23 | /** 24 | * Returns a reference to the descriptor for the standard output stream. 25 | */ 26 | const descriptor& standard_output() noexcept; 27 | 28 | /** 29 | * Returns a reference to the descriptor for the standard error stream. 30 | */ 31 | const descriptor& standard_error() noexcept; 32 | 33 | /** 34 | * Writes some data to the given descriptor, handling `EINTR`/`EAGAIN`. 35 | * 36 | * @throws posix::error on failure 37 | */ 38 | void write(int fd, const void* buffer, std::size_t count); 39 | 40 | /** 41 | * Writes a string to the given descriptor, handling `EINTR`/`EAGAIN`. 42 | * 43 | * @throws posix::error on failure 44 | */ 45 | inline void write(const int fd, const std::string& string) { 46 | write(fd, string.data(), string.size()); 47 | } 48 | } 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | 52 | #endif /* POSIXXX_STDIO_H */ 53 | -------------------------------------------------------------------------------- /src/posix++/pathname.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "error.h" 8 | #include "pathname.h" 9 | 10 | #include /* for assert() */ 11 | #include /* for std::strcpy(), GNU basename() */ 12 | #include /* for basename(), dirname() */ 13 | #include /* for lstat() */ 14 | #include /* for lstat() */ 15 | #include /* for lstat(), unlink() */ 16 | 17 | #undef basename /* get rid of the glibc macro */ 18 | 19 | using namespace posix; 20 | 21 | bool 22 | pathname::is_portable() const noexcept { 23 | return true; // TODO 24 | } 25 | 26 | pathname 27 | pathname::dirname() const { 28 | char buffer[_string.size() + 1]; 29 | std::strcpy(buffer, _string.c_str()); 30 | return pathname{::dirname(buffer)}; 31 | } 32 | 33 | pathname 34 | pathname::basename() const { 35 | char buffer[_string.size() + 1]; 36 | std::strcpy(buffer, _string.c_str()); 37 | return pathname{::basename(buffer)}; 38 | } 39 | 40 | bool 41 | pathname::exists() const { 42 | struct stat buffer; 43 | if (lstat(c_str(), &buffer) == -1) { 44 | assert(errno != EFAULT); 45 | switch (errno) { 46 | case ENOENT: /* No such file or directory */ 47 | return false; 48 | default: 49 | throw_error("lstat", "\"%s\", %s", c_str(), "buffer"); 50 | } 51 | } 52 | return true; 53 | } 54 | 55 | void 56 | pathname::unlink() const { 57 | if (::unlink(c_str()) == -1) { 58 | throw_error("unlink", "\"%s\"", c_str()); 59 | } 60 | } 61 | 62 | pathname& 63 | pathname::append(const char* const path) { 64 | assert(path); 65 | _string.append(path); 66 | return *this; 67 | } 68 | -------------------------------------------------------------------------------- /etc/doap.rdf: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 2013-04-01 12 | 13 | ... 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | libposix++ 22 | C++ 23 | ... 24 | 25 | 26 | 27 | 28 | 29 | a033f652c84a4d73b8c26d318c2395699dd2bdfb 30 | Arto Bendiken 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/posix++/named_pipe.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_NAMED_PIPE_H 4 | #define POSIXXX_NAMED_PIPE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "descriptor.h" 13 | #include "mode.h" 14 | 15 | #include /* for std::move(), std::pair */ 16 | 17 | namespace posix { 18 | class directory; 19 | class named_pipe; 20 | class pathname; 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | /** 26 | * Represents a POSIX named pipe (aka FIFO). 27 | * 28 | * @see http://en.wikipedia.org/wiki/Named_pipe 29 | */ 30 | class posix::named_pipe : public posix::descriptor { 31 | public: 32 | static void create(const pathname& pathname, mode mode); 33 | 34 | static void create(const directory& directory, const pathname& pathname, mode mode); 35 | 36 | static named_pipe open(const pathname& pathname, int flags); 37 | 38 | static named_pipe open(const directory& directory, const pathname& pathname, int flags); 39 | 40 | /** 41 | * Default constructor. 42 | */ 43 | named_pipe() = delete; 44 | 45 | /** 46 | * Constructor. 47 | */ 48 | named_pipe(const int fd) noexcept 49 | : descriptor(fd) {} 50 | 51 | /** 52 | * Copy constructor. 53 | */ 54 | named_pipe(const named_pipe& other) = default; /* may throw */ 55 | 56 | /** 57 | * Move constructor. 58 | */ 59 | named_pipe(named_pipe&& other) noexcept = default; 60 | 61 | /** 62 | * Destructor. 63 | */ 64 | ~named_pipe() noexcept = default; 65 | 66 | protected: 67 | static void create(int dirfd, const char* pathname, mode mode = 0); 68 | static named_pipe open(int dirfd, const char* pathname, int flags); 69 | }; 70 | 71 | //////////////////////////////////////////////////////////////////////////////// 72 | 73 | #endif /* POSIXXX_NAMED_PIPE_H */ 74 | -------------------------------------------------------------------------------- /src/posix++/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/src -iquote $(srcdir) 2 | AM_CXXFLAGS = -Wall -Wextra -pipe 3 | AM_LDFLAGS = 4 | LDADD = libposix++.la $(top_srcdir)/lib/libcompat.a 5 | lib_LTLIBRARIES = libposix++.la 6 | 7 | libposix___la_SOURCES = \ 8 | descriptor.cc \ 9 | directory.cc \ 10 | error.cc \ 11 | feature.cc \ 12 | file.cc \ 13 | group.cc \ 14 | mapped_file.cc \ 15 | memory_mapping.cc \ 16 | mode.cc \ 17 | module.cc \ 18 | named_pipe.cc \ 19 | pathname.cc \ 20 | process.cc \ 21 | process_group.cc \ 22 | thread.cc \ 23 | user.cc \ 24 | version.cc 25 | 26 | base_pkgincludedir = $(includedir)/posix++ 27 | 28 | base_pkginclude_HEADERS = \ 29 | descriptor.h \ 30 | directory.h \ 31 | error.h \ 32 | feature.h \ 33 | file.h \ 34 | group.h \ 35 | mapped_file.h \ 36 | memory_mapping.h \ 37 | mode.h \ 38 | module.h \ 39 | named_pipe.h \ 40 | pathname.h \ 41 | process.h \ 42 | process_group.h \ 43 | thread.h \ 44 | user.h \ 45 | version.h 46 | 47 | if !DISABLE_MQUEUE 48 | libposix___la_SOURCES += message_queue.cc 49 | base_pkginclude_HEADERS += message_queue.h 50 | endif 51 | 52 | if !DISABLE_SEMAPHORE 53 | base_pkginclude_HEADERS += semaphore.h 54 | endif 55 | 56 | if !DISABLE_STDIO 57 | libposix___la_SOURCES += stdio.cc 58 | base_pkginclude_HEADERS += stdio.h 59 | endif 60 | 61 | if !DISABLE_SOCKET 62 | libposix___la_SOURCES += socket.cc local_socket.cc 63 | base_pkginclude_HEADERS += socket.h local_socket.h 64 | endif 65 | 66 | if !DISABLE_SYSV 67 | libposix___la_SOURCES += sysv_segment.cc 68 | base_pkginclude_HEADERS += sysv_segment.h 69 | endif 70 | -------------------------------------------------------------------------------- /src/posix++/stdio.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "descriptor.h" 8 | #include "error.h" 9 | #include "stdio.h" 10 | 11 | #include /* for std::array */ 12 | #include /* for assert() */ 13 | #include /* for E*, errno */ 14 | #include /* for std::uint8_t */ 15 | #include /* for STD{IN,OUT,ERR}_FILENO, write() */ 16 | 17 | using namespace posix; 18 | 19 | static struct { 20 | std::array standard_input; 21 | std::array standard_output; 22 | std::array standard_error; 23 | } static_data; 24 | 25 | const descriptor& 26 | posix::standard_input() noexcept { 27 | static const descriptor* const stdin = 28 | new(static_data.standard_input.data()) descriptor(STDIN_FILENO); 29 | return *stdin; 30 | } 31 | 32 | const descriptor& 33 | posix::standard_output() noexcept { 34 | static const descriptor* const stdout = 35 | new(static_data.standard_output.data()) descriptor(STDOUT_FILENO); 36 | return *stdout; 37 | } 38 | 39 | const descriptor& 40 | posix::standard_error() noexcept { 41 | static const descriptor* const stderr = 42 | new(static_data.standard_error.data()) descriptor(STDERR_FILENO); 43 | return *stderr; 44 | } 45 | 46 | void 47 | posix::write(const int fd, const void* const buffer, const std::size_t count) { 48 | assert(fd > 0); 49 | assert(buffer != nullptr); 50 | 51 | std::size_t position = 0; 52 | 53 | while (position < count) { 54 | const ssize_t rc = ::write(fd, reinterpret_cast(buffer) + position, count - position); 55 | if (rc == -1) { 56 | switch (errno) { 57 | case EINTR: 58 | case EAGAIN: 59 | continue; /* try again */ 60 | default: 61 | throw_error("write", "%d, %s, %zu", fd, "chunk", count - position); 62 | } 63 | } 64 | position += static_cast(rc); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/posix++/named_pipe.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "directory.h" 8 | #include "error.h" 9 | #include "named_pipe.h" 10 | #include "pathname.h" 11 | 12 | #include /* for assert() */ 13 | #include /* for errno */ 14 | #include /* for AT_FDCWD, O_CLOEXEC */ 15 | #include /* for mkfifoat() */ 16 | #include /* for struct stat */ 17 | 18 | using namespace posix; 19 | 20 | void 21 | named_pipe::create(const int dirfd, 22 | const char* const pathname, 23 | const mode mode) { 24 | assert(dirfd > 0 || dirfd == AT_FDCWD); 25 | assert(pathname); 26 | assert(pathname[0] != '\0'); 27 | 28 | if (mkfifoat(dirfd, pathname, mode) == -1) { 29 | throw_error("mkfifoat"); 30 | } 31 | } 32 | 33 | named_pipe 34 | named_pipe::open(const int dirfd, 35 | const char* const pathname, 36 | int flags) { 37 | assert(dirfd > 0 || dirfd == AT_FDCWD); 38 | assert(pathname); 39 | assert(pathname[0] != '\0'); 40 | 41 | #ifdef O_CLOEXEC 42 | flags |= O_CLOEXEC; /* POSIX.1-2008 (Linux, FreeBSD) */ 43 | #endif 44 | 45 | int fd; 46 | if ((fd = openat(dirfd, pathname, flags)) == -1) { 47 | throw_error("openat"); 48 | } 49 | 50 | return named_pipe(fd); 51 | } 52 | 53 | void 54 | named_pipe::create(const pathname& pathname, 55 | const mode mode) { 56 | return create(AT_FDCWD, pathname.c_str(), mode); 57 | } 58 | 59 | void 60 | named_pipe::create(const directory& directory, 61 | const pathname& pathname, 62 | const mode mode) { 63 | return create(directory.fd(), pathname.c_str(), mode); 64 | } 65 | 66 | named_pipe 67 | named_pipe::open(const pathname& pathname, 68 | const int flags) { 69 | return open(AT_FDCWD, pathname.c_str(), flags); 70 | } 71 | 72 | named_pipe 73 | named_pipe::open(const directory& directory, 74 | const pathname& pathname, 75 | const int flags) { 76 | return open(directory.fd(), pathname.c_str(), flags); 77 | } 78 | -------------------------------------------------------------------------------- /src/posix++/local_socket.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_LOCAL_SOCKET_H 4 | #define POSIXXX_LOCAL_SOCKET_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "descriptor.h" 13 | #include "socket.h" 14 | 15 | #include /* for std::move(), std::pair */ 16 | 17 | namespace posix { 18 | class local_socket; 19 | class pathname; 20 | } 21 | 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | /** 26 | * Represents a POSIX local socket (aka Unix domain socket). 27 | * 28 | * @see http://en.wikipedia.org/wiki/Unix_domain_socket 29 | */ 30 | class posix::local_socket : public posix::socket { 31 | public: 32 | static std::pair pair(); 33 | 34 | /** 35 | * @throws std::length_error if `pathname` exceeds the maximum socket pathname length 36 | */ 37 | static local_socket bind(const pathname& pathname); 38 | 39 | /** 40 | * @throws std::length_error if `pathname` exceeds the maximum socket pathname length 41 | */ 42 | static local_socket connect(const pathname& pathname); 43 | 44 | /** 45 | * Constructor. 46 | */ 47 | local_socket(const int fd) noexcept 48 | : socket(fd) {} 49 | 50 | /** 51 | * Copy constructor. 52 | */ 53 | local_socket(const local_socket& other) /* may throw */ 54 | : socket(other) {} 55 | 56 | /** 57 | * Move constructor. 58 | */ 59 | local_socket(local_socket&& other) noexcept 60 | : socket(std::move(other)) {} 61 | 62 | /** 63 | * Destructor. 64 | */ 65 | ~local_socket() noexcept {} 66 | 67 | /** 68 | * Accepts a connection on this socket. 69 | */ 70 | local_socket accept(); 71 | 72 | /** 73 | * Sends a descriptor to the peer. 74 | */ 75 | void send_descriptor(const descriptor& descriptor); 76 | 77 | /** 78 | * Receives a descriptor from the peer. 79 | */ 80 | descriptor recv_descriptor(); 81 | 82 | protected: 83 | /** 84 | * Default constructor. 85 | */ 86 | local_socket(); 87 | }; 88 | 89 | 90 | //////////////////////////////////////////////////////////////////////////////// 91 | 92 | #endif /* POSIXXX_LOCAL_SOCKET_H */ 93 | -------------------------------------------------------------------------------- /src/posix++/process.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "error.h" 8 | #include "group.h" 9 | #include "process.h" 10 | #include "user.h" 11 | 12 | #include /* for assert() */ 13 | #include /* for errno */ 14 | #include /* for SIG*, kill() */ 15 | #include /* for gid_t, pid_t, uid_t */ 16 | #include /* for waitpid() */ 17 | #include /* for get*gid(), get*pid(), get*uid() */ 18 | 19 | using namespace posix; 20 | 21 | process 22 | current_process() noexcept { 23 | return process(getpid()); 24 | } 25 | 26 | process 27 | parent_process() noexcept { 28 | return process(getppid()); 29 | } 30 | 31 | user 32 | process::uid() const noexcept { 33 | return user(getuid()); 34 | } 35 | 36 | user 37 | process::euid() const noexcept { 38 | return user(geteuid()); 39 | } 40 | 41 | group 42 | process::gid() const noexcept { 43 | return group(getgid()); 44 | } 45 | 46 | group 47 | process::egid() const noexcept { 48 | return group(getegid()); 49 | } 50 | 51 | bool 52 | process::alive() noexcept { 53 | int status; 54 | try { 55 | return _id ? !wait(status, WNOHANG) : false; 56 | } 57 | catch (const posix::runtime_error&) { 58 | return false; /* ECHILD */ 59 | } 60 | } 61 | 62 | int 63 | process::wait() { 64 | assert(_id > 0); 65 | 66 | int status = 0; 67 | pid_t rc; 68 | do { 69 | rc = waitpid(_id, &status, 0); 70 | } while (!rc || (rc == -1 && errno == EINTR)); 71 | return status; 72 | } 73 | 74 | bool 75 | process::wait(int& status, const int options) { 76 | assert(_id > 0); 77 | 78 | for (;;) { 79 | const pid_t rc = waitpid(_id, &status, options); 80 | 81 | switch (rc) { 82 | case -1: /* an error condition occurred */ 83 | switch (errno) { 84 | case EINTR: /* Interrupted system call */ 85 | continue; /* try again */ 86 | default: 87 | throw_error("waitpid", "%u, %s, 0x%x", 88 | _id, "status", static_cast(options)); 89 | } 90 | 91 | case 0: /* the process hasn't changed state */ 92 | return false; 93 | 94 | default: /* the process has changed state */ 95 | return true; 96 | } 97 | } 98 | } 99 | 100 | void 101 | process::signal(const int signum) { 102 | assert(_id > 0); 103 | assert(signum >= 0); 104 | 105 | if (kill(_id, signum) == -1) { 106 | throw_error("kill", "%u, %d", _id, signum); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/posix++/file.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_FILE_H 4 | #define POSIXXX_FILE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "descriptor.h" 13 | #include "mode.h" 14 | 15 | #include /* for std::size_t */ 16 | #include /* for SEEK_*, off_t */ 17 | #include /* for std::move() */ 18 | 19 | namespace posix { 20 | class directory; 21 | class file; 22 | class pathname; 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | /** 28 | * Represents a POSIX file. 29 | * 30 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_163 31 | */ 32 | class posix::file : public posix::descriptor { 33 | public: 34 | static file create(const pathname& pathname, mode mode); 35 | 36 | static file create(const directory& directory, const pathname& pathname, mode mode); 37 | 38 | static file open(const pathname& pathname, int flags, mode mode = 0); 39 | 40 | static file open(const directory& directory, const pathname& pathname, int flags, mode mode = 0); 41 | 42 | /** 43 | * Default constructor. 44 | */ 45 | file() noexcept 46 | : descriptor{} {} 47 | 48 | /** 49 | * Constructor. 50 | */ 51 | file(int dirfd, const char* pathname, int flags, mode mode = 0); 52 | 53 | /** 54 | * Constructor. 55 | */ 56 | file(const int fd) noexcept 57 | : descriptor{fd} {} 58 | 59 | /** 60 | * Copy constructor. 61 | */ 62 | file(const file& other) /* may throw */ 63 | : descriptor{other} {} 64 | 65 | /** 66 | * Move constructor. 67 | */ 68 | file(file&& other) noexcept 69 | : descriptor{std::move(other)} {} 70 | 71 | /** 72 | * Destructor. 73 | */ 74 | ~file() noexcept = default; 75 | 76 | /** 77 | * Checks whether this file's size is zero. 78 | */ 79 | bool empty() const { 80 | return size() == 0; 81 | } 82 | 83 | /** 84 | * Returns the current file size in bytes. 85 | */ 86 | std::size_t size() const; 87 | 88 | /** 89 | * Returns the current file offset. 90 | */ 91 | std::size_t offset() const { 92 | return seek(0, SEEK_CUR); 93 | } 94 | 95 | /** 96 | * Sets the file offset to the beginning of the file. 97 | */ 98 | void rewind() const { 99 | seek(0, SEEK_SET); 100 | } 101 | 102 | /** 103 | * Returns or changes the current file offset. 104 | */ 105 | std::size_t seek(off_t offset, int whence = SEEK_SET) const; 106 | 107 | /** 108 | * ... 109 | */ 110 | void allocate(off_t offset, off_t length) const; 111 | 112 | /** 113 | * ... 114 | */ 115 | void truncate(off_t length = 0) const; 116 | }; 117 | 118 | //////////////////////////////////////////////////////////////////////////////// 119 | 120 | #endif /* POSIXXX_FILE_H */ 121 | -------------------------------------------------------------------------------- /src/posix++/file.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "file.h" 8 | 9 | #include "directory.h" 10 | #include "error.h" 11 | #include "pathname.h" 12 | 13 | #include /* for assert() */ 14 | #include /* for errno */ 15 | #include /* for AT_FDCWD, O_CLOEXEC, posix_fallocate() */ 16 | #include /* for fstat() */ 17 | #include /* for struct stat */ 18 | #include /* for ftruncate(), lseek() */ 19 | 20 | using namespace posix; 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | 24 | file 25 | file::create(const pathname& pathname, 26 | const mode mode) { 27 | 28 | const int flags = O_CREAT | O_WRONLY | O_TRUNC; 29 | return file{AT_FDCWD, pathname.c_str(), flags, mode}; 30 | } 31 | 32 | file 33 | file::create(const directory& directory, 34 | const pathname& pathname, 35 | const mode mode) { 36 | 37 | const int flags = O_CREAT | O_WRONLY | O_TRUNC; 38 | return file{directory.fd(), pathname.c_str(), flags, mode}; 39 | } 40 | 41 | file 42 | file::open(const pathname& pathname, 43 | const int flags, 44 | const mode mode) { 45 | 46 | return file{AT_FDCWD, pathname.c_str(), flags, mode}; 47 | } 48 | 49 | file 50 | file::open(const directory& directory, 51 | const pathname& pathname, 52 | const int flags, 53 | const mode mode) { 54 | 55 | return file{directory.fd(), pathname.c_str(), flags, mode}; 56 | } 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | 60 | file::file(const int dirfd, 61 | const char* const pathname, 62 | int flags, 63 | const mode mode) 64 | : descriptor{} { 65 | 66 | assert(dirfd > 0 || dirfd == AT_FDCWD); 67 | assert(pathname != nullptr); 68 | assert(pathname[0] != '\0'); 69 | 70 | #ifdef O_CLOEXEC 71 | flags |= O_CLOEXEC; /* POSIX.1-2008 (Linux, FreeBSD) */ 72 | #endif 73 | 74 | if ((_fd = openat(dirfd, pathname, flags, mode)) == -1) { 75 | throw_error("openat", "%d, \"%s\", 0x%x, 0%o", dirfd, pathname, 76 | flags, static_cast(mode)); 77 | } 78 | } 79 | 80 | //////////////////////////////////////////////////////////////////////////////// 81 | 82 | std::size_t 83 | file::size() const { 84 | struct stat st; 85 | 86 | if (fstat(fd(), &st) == -1) { 87 | assert(errno != EFAULT); 88 | throw_error("fstat", "%d, %s", fd(), "buffer"); 89 | } 90 | 91 | return static_cast(st.st_size); 92 | } 93 | 94 | std::size_t 95 | file::seek(const off_t offset, 96 | const int whence) const { 97 | off_t result; 98 | 99 | if ((result = lseek(fd(), offset, whence)) == static_cast(-1)) { 100 | throw_error("lseek", "%d, 0x%lx, %d", 101 | fd(), static_cast(offset), whence); 102 | } 103 | 104 | assert(result >= 0); 105 | return static_cast(result); 106 | } 107 | 108 | void 109 | file::allocate(const off_t offset, 110 | const off_t length) const { 111 | int err; 112 | if ((err = posix_fallocate(fd(), offset, length)) != 0) { 113 | throw_error(err, "posix_fallocate", "%d, 0x%08lx, 0x%08lx", fd(), 114 | static_cast(offset), 115 | static_cast(length)); 116 | } 117 | } 118 | 119 | void 120 | file::truncate(const off_t length) const { 121 | if (ftruncate(fd(), length) == -1) { 122 | throw_error("ftruncate", "%d, 0x%08lx", fd(), 123 | static_cast(length)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/posix++/socket.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_SOCKET_H 4 | #define POSIXXX_SOCKET_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "descriptor.h" 13 | 14 | #include /* for std::size_t */ 15 | #include /* for std::function */ 16 | #include /* for std::string */ 17 | #include /* for std::move() */ 18 | 19 | namespace posix { 20 | class socket; 21 | } 22 | 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | /** 27 | * Represents a POSIX socket. 28 | * 29 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html 30 | * @see https://en.wikipedia.org/wiki/Berkeley_sockets 31 | */ 32 | class posix::socket : public posix::descriptor { 33 | public: 34 | /** 35 | * Returns the socket domain. 36 | */ 37 | int domain() const; 38 | 39 | /** 40 | * Returns the socket type. 41 | */ 42 | int type() const; 43 | 44 | /** 45 | * Returns the socket protocol. 46 | */ 47 | int protocol() const; 48 | 49 | /** 50 | * Returns and clears the pending socket error. 51 | */ 52 | int error(); 53 | 54 | /** 55 | * Returns socket options. 56 | */ 57 | void getsockopt(int level, int optname, void* optval, int* optlen) const; 58 | 59 | /** 60 | * Listens for connections on this socket. 61 | */ 62 | void listen(unsigned int backlog = 128); 63 | 64 | /** 65 | * Sends a string to the peer. 66 | */ 67 | void send(const std::string& string); 68 | 69 | /** 70 | * Sends data to the peer. 71 | */ 72 | void send(const char* data); 73 | 74 | /** 75 | * Sends data to the peer. 76 | */ 77 | void send(const void* data, std::size_t size); 78 | 79 | /** 80 | * Receives a text chunk from the peer. 81 | */ 82 | std::string recv_chunk(); 83 | 84 | /** 85 | * Receives a string from the peer. 86 | */ 87 | std::string recv_string(); 88 | 89 | /** 90 | * Receives data from the peer, into the given string buffer. 91 | */ 92 | std::size_t recv(std::string& buffer); 93 | 94 | /** 95 | * Receives data from the peer, chunk by chunk. 96 | */ 97 | std::size_t recv(std::function callback); 98 | 99 | /** 100 | * Receives data from the peer, into the given raw buffer. 101 | */ 102 | std::size_t recv(void* buffer, std::size_t buffer_size, int flags = 0); 103 | 104 | /** 105 | * Closes this socket for writing. 106 | */ 107 | void close_write(); 108 | 109 | /** 110 | * Closes this socket for reading. 111 | */ 112 | void close_read(); 113 | 114 | /** 115 | * Shuts down part of a full-duplex connection. 116 | */ 117 | void shutdown(int how); 118 | 119 | protected: 120 | /** 121 | * Default constructor. 122 | */ 123 | socket() noexcept 124 | : descriptor() {} 125 | 126 | /** 127 | * Constructor. 128 | */ 129 | socket(const int fd) noexcept 130 | : descriptor(fd) {} 131 | 132 | /** 133 | * Copy constructor. 134 | */ 135 | socket(const socket& other) /* may throw */ 136 | : descriptor(other) {} 137 | 138 | /** 139 | * Move constructor. 140 | */ 141 | socket(socket&& other) noexcept 142 | : descriptor(std::move(other)) {} 143 | 144 | /** 145 | * Destructor. 146 | */ 147 | ~socket() noexcept {} 148 | }; 149 | 150 | //////////////////////////////////////////////////////////////////////////////// 151 | 152 | #endif /* POSIXXX_SOCKET_H */ 153 | -------------------------------------------------------------------------------- /src/posix++/memory_mapping.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "error.h" 8 | #include "descriptor.h" 9 | #include "memory_mapping.h" 10 | 11 | #include /* for assert() */ 12 | #include /* for errno */ 13 | #include /* for mmap(), mremap(), munmap() */ 14 | #include /* for fstat() */ 15 | #include /* for struct stat */ 16 | 17 | using namespace posix; 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | std::uint8_t* 22 | memory_mapping::map(const int fd, 23 | const std::size_t size, 24 | const std::size_t offset) { 25 | assert(fd >= 0); 26 | assert(size > 0); 27 | 28 | const int prot = PROT_READ; // FIXME 29 | 30 | const int flags = MAP_SHARED; 31 | 32 | if (size == static_cast(-1)) { 33 | struct stat st; 34 | if (fstat(fd, &st) == -1) { 35 | throw_error("fstat", "%d, %s", fd, "buffer"); 36 | } 37 | _size = static_cast(st.st_size) - offset; 38 | } 39 | else { 40 | _size = size; 41 | } 42 | 43 | void* const addr = ::mmap(nullptr, _size, prot, flags, fd, offset); 44 | if (addr == MAP_FAILED) { 45 | throw_error("mmap", "%p, %zu, 0x%x, 0x%x, %d, %zu", 46 | nullptr, _size, static_cast(prot), 47 | static_cast(flags), fd, offset); 48 | } 49 | 50 | return reinterpret_cast(addr); 51 | } 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | 55 | memory_mapping::memory_mapping(const descriptor& descriptor) 56 | : memory_mapping{descriptor.fd()} {} 57 | 58 | memory_mapping::memory_mapping(const descriptor& descriptor, 59 | const std::size_t size, 60 | const std::size_t offset) 61 | : memory_mapping{descriptor.fd(), size, offset} {} 62 | 63 | memory_mapping::memory_mapping(const int fd) 64 | : _size{0}, 65 | _data{map(fd, static_cast(-1), 0)} {} 66 | 67 | memory_mapping::memory_mapping(const int fd, 68 | const std::size_t size, 69 | const std::size_t offset) 70 | : _size{size}, 71 | _data{map(fd, size, offset)} {} 72 | 73 | memory_mapping::memory_mapping(void* const data, 74 | const std::size_t size) noexcept 75 | : _size{size}, 76 | _data{reinterpret_cast(data)} {} 77 | 78 | memory_mapping::~memory_mapping() noexcept { 79 | if (_data) { 80 | if (::munmap(reinterpret_cast(_data), _size) == -1) { 81 | /* Ignore any errors from munmap() here. */ 82 | } 83 | _data = nullptr; 84 | _size = 0; 85 | } 86 | } 87 | 88 | //////////////////////////////////////////////////////////////////////////////// 89 | 90 | void 91 | memory_mapping::remap(const std::size_t new_size, 92 | const int flags) { 93 | #ifdef __linux__ 94 | void* const new_addr = ::mremap(_data, _size, new_size, flags); 95 | if (new_addr == MAP_FAILED) { 96 | throw_error("mremap", "%p, %zu, %zu, 0x%", 97 | _data, _size, new_size, static_cast(flags)); 98 | } 99 | _data = reinterpret_cast(new_addr); 100 | _size = new_size; 101 | #else 102 | throw_error(ENOSYS); /* Function not implemented */ 103 | #endif /* __linux__ */ 104 | } 105 | 106 | bool 107 | memory_mapping::readable() const noexcept { 108 | return _data != nullptr; // TODO 109 | } 110 | 111 | bool 112 | memory_mapping::writable() const noexcept { 113 | return _data != nullptr; // TODO 114 | } 115 | 116 | bool 117 | memory_mapping::executable() const noexcept { 118 | return _data != nullptr; // TODO 119 | } 120 | -------------------------------------------------------------------------------- /etc/aclocal/ax_cxx_compile_stdcxx_11.m4: -------------------------------------------------------------------------------- 1 | # ============================================================================ 2 | # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html 3 | # ============================================================================ 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CXX_COMPILE_STDCXX_11([ext|noext]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check for baseline language coverage in the compiler for the C++11 12 | # standard; if necessary, add switches to CXXFLAGS to enable support. 13 | # Errors out if no mode that supports C++11 baseline syntax can be found. 14 | # The argument, if specified, indicates whether you insist on an extended 15 | # mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. -std=c++11). 16 | # If neither is specified, you get whatever works, with preference for an 17 | # extended mode. 18 | # 19 | # LICENSE 20 | # 21 | # Copyright (c) 2008 Benjamin Kosnik 22 | # Copyright (c) 2012 Zack Weinberg 23 | # 24 | # Copying and distribution of this file, with or without modification, are 25 | # permitted in any medium without royalty provided the copyright notice 26 | # and this notice are preserved. This file is offered as-is, without any 27 | # warranty. 28 | 29 | #serial 1 30 | 31 | m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [ 32 | template 33 | struct check 34 | { 35 | static_assert(sizeof(int) <= sizeof(T), "not big enough"); 36 | }; 37 | 38 | typedef check> right_angle_brackets; 39 | 40 | int a; 41 | decltype(a) b; 42 | 43 | typedef check check_type; 44 | check_type c; 45 | check_type&& cr = static_cast(c); 46 | ]) 47 | 48 | AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl 49 | m4_if([$1], [], [], 50 | [$1], [ext], [], 51 | [$1], [noext], [], 52 | [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl 53 | AC_LANG_ASSERT([C++])dnl 54 | ac_success=no 55 | AC_CACHE_CHECK(whether $CXX supports C++11 features by default, 56 | ax_cv_cxx_compile_cxx11, 57 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], 58 | [ax_cv_cxx_compile_cxx11=yes], 59 | [ax_cv_cxx_compile_cxx11=no])]) 60 | if test x$ax_cv_cxx_compile_cxx11 = xyes; then 61 | ac_success=yes 62 | fi 63 | 64 | m4_if([$1], [noext], [], [dnl 65 | if test x$ac_success = xno; then 66 | for switch in -std=gnu++11 -std=gnu++0x; do 67 | cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) 68 | AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, 69 | $cachevar, 70 | [ac_save_CXXFLAGS="$CXXFLAGS" 71 | CXXFLAGS="$CXXFLAGS $switch" 72 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], 73 | [eval $cachevar=yes], 74 | [eval $cachevar=no]) 75 | CXXFLAGS="$ac_save_CXXFLAGS"]) 76 | if eval test x\$$cachevar = xyes; then 77 | CXXFLAGS="$CXXFLAGS $switch" 78 | ac_success=yes 79 | break 80 | fi 81 | done 82 | fi]) 83 | 84 | m4_if([$1], [ext], [], [dnl 85 | if test x$ac_success = xno; then 86 | for switch in -std=c++11 -std=c++0x; do 87 | cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) 88 | AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, 89 | $cachevar, 90 | [ac_save_CXXFLAGS="$CXXFLAGS" 91 | CXXFLAGS="$CXXFLAGS $switch" 92 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], 93 | [eval $cachevar=yes], 94 | [eval $cachevar=no]) 95 | CXXFLAGS="$ac_save_CXXFLAGS"]) 96 | if eval test x\$$cachevar = xyes; then 97 | CXXFLAGS="$CXXFLAGS $switch" 98 | ac_success=yes 99 | break 100 | fi 101 | done 102 | fi]) 103 | 104 | if test x$ac_success = xno; then 105 | AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) 106 | fi 107 | ]) 108 | -------------------------------------------------------------------------------- /src/posix++/process.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_PROCESS_H 4 | #define POSIXXX_PROCESS_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "user.h" 13 | 14 | namespace posix { 15 | class group; 16 | using process_id = unsigned int; 17 | class process; 18 | class user; 19 | 20 | /** 21 | * Returns the current process ID. 22 | * 23 | * @note This function always succeeds. 24 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_296 25 | */ 26 | process current_process() noexcept; 27 | 28 | /** 29 | * Returns the parent process ID of the current process. 30 | * 31 | * @note This function always succeeds. 32 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_266 33 | */ 34 | process parent_process() noexcept; 35 | } 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | 39 | /** 40 | * Represents a POSIX process. 41 | * 42 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_289 43 | */ 44 | class posix::process { 45 | public: 46 | /** 47 | * Constructor. 48 | */ 49 | process(const process_id id) noexcept 50 | : _id(id) {} 51 | 52 | /** 53 | * Assigns a new identifier value to this process. 54 | */ 55 | process& assign(const process_id id) { 56 | _id = id; 57 | return *this; 58 | } 59 | 60 | /** 61 | * Returns the integer identifier for this process. 62 | */ 63 | inline process_id id() const noexcept { 64 | return _id; 65 | } 66 | 67 | /** 68 | * Returns the real user ID of this process. 69 | * 70 | * @note This method always succeeds. 71 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_311 72 | */ 73 | user uid() const noexcept; 74 | 75 | /** 76 | * Returns the effective user ID of this process. 77 | * 78 | * @note This method always succeeds. 79 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_142 80 | */ 81 | user euid() const noexcept; 82 | 83 | /** 84 | * Returns the real group ID of this process. 85 | * 86 | * @note This method always succeeds. 87 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_308 88 | */ 89 | group gid() const noexcept; 90 | 91 | /** 92 | * Returns the effective group ID of this process. 93 | * 94 | * @note This method always succeeds. 95 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_141 96 | */ 97 | group egid() const noexcept; 98 | 99 | /** 100 | * Determines whether this is a privileged process. 101 | * 102 | * @note This method always succeeds. 103 | */ 104 | bool is_privileged() const noexcept { 105 | return euid().is_root(); 106 | } 107 | 108 | /** 109 | * Checks whether this process is still alive. 110 | */ 111 | bool alive() noexcept; 112 | 113 | /** 114 | * Waits for this process to terminate. 115 | */ 116 | int wait(); 117 | 118 | /** 119 | * Waits for this process to change state. 120 | * 121 | * @retval true if the process has changed state 122 | * @retval false if the process has not changed state 123 | */ 124 | bool wait(int& status, int options); 125 | 126 | /** 127 | * Sends a signal to this process. 128 | */ 129 | void signal(int signum); 130 | 131 | protected: 132 | /** 133 | * The unique positive integer identifier representing a process during 134 | * its lifetime. 135 | * 136 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_294 137 | */ 138 | process_id _id = 0; 139 | }; 140 | 141 | //////////////////////////////////////////////////////////////////////////////// 142 | 143 | #endif /* POSIXXX_PROCESS_H */ 144 | -------------------------------------------------------------------------------- /src/posix++/message_queue.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MESSAGE_QUEUE_H 4 | #define POSIXXX_MESSAGE_QUEUE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "descriptor.h" 13 | #include "mode.h" 14 | 15 | #include /* for std::size_t */ 16 | #include /* for std::int64_t */ 17 | #include /* for std::string */ 18 | #include /* for std::move() */ 19 | 20 | namespace posix { 21 | class message_queue; 22 | struct message_queue_attr; 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | /** 28 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/mqueue.h.html 29 | */ 30 | struct posix::message_queue_attr { 31 | long flags; 32 | long maxmsg; 33 | long msgsize; 34 | }; 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | /** 39 | * Represents a POSIX message queue. 40 | * 41 | * @note Assumes that message queue descriptors are actually file descriptors. 42 | * This is the case on at least Linux and FreeBSD. 43 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_224 44 | */ 45 | class posix::message_queue : public posix::descriptor { 46 | const std::size_t _message_size = 8192; // FIXME 47 | 48 | public: 49 | static message_queue open(const std::string& queue_name, 50 | int flags, mode mode = 0); 51 | 52 | static message_queue open(const std::string& queue_name, 53 | int flags, mode mode, const message_queue_attr& attributes); 54 | 55 | /** 56 | * Removes a message queue. 57 | * 58 | * @param queue_name the name of the queue to remove 59 | * @pre `queue_name` must not be `nullptr`. 60 | */ 61 | static void unlink(const char* queue_name); 62 | 63 | /** 64 | * Default constructor. 65 | */ 66 | message_queue() = delete; 67 | 68 | /** 69 | * Constructor. 70 | */ 71 | message_queue(const int fd) noexcept 72 | : descriptor(fd) {} 73 | 74 | /** 75 | * Copy constructor. 76 | */ 77 | message_queue(const message_queue& other) /* may throw */ 78 | : descriptor(other) {} 79 | 80 | /** 81 | * Move constructor. 82 | */ 83 | message_queue(message_queue&& other) noexcept 84 | : descriptor(std::move(other)) {} 85 | 86 | /** 87 | * Destructor. 88 | */ 89 | ~message_queue() noexcept; 90 | 91 | /** 92 | * Closes this message queue. 93 | * 94 | * @note this method is idempotent 95 | */ 96 | void close() noexcept; 97 | 98 | /** 99 | * Receives a message from this message queue. 100 | * 101 | * @param message_data the message data 102 | * @param message_size the message size in bytes 103 | * @param message_priority the message priority (optionally) 104 | * @param recv_timeout the receive timeout in milliseconds, or -1 for a blocking receive 105 | * @pre `message_data` must not be `nullptr`. 106 | */ 107 | std::size_t recv(void* message_data, 108 | std::size_t message_size, 109 | unsigned int* message_priority = nullptr, 110 | std::int64_t recv_timeout = -1); 111 | 112 | /** 113 | * Sends a message to this message queue. 114 | * 115 | * @param message_data the message data 116 | * @param message_size the message size in bytes 117 | * @param message_priority the message priority (defaults to zero) 118 | * @param send_timeout the send timeout in milliseconds, or -1 for a blocking send 119 | * @pre `message_data` must not be `nullptr`. 120 | */ 121 | void send(const void* message_data, 122 | std::size_t message_size, 123 | unsigned int message_priority = 0, 124 | std::int64_t send_timeout = -1); 125 | 126 | protected: 127 | static message_queue open(const char* name, 128 | int flags, mode mode, const message_queue_attr* attributes); 129 | }; 130 | 131 | //////////////////////////////////////////////////////////////////////////////// 132 | 133 | #endif /* POSIXXX_MESSAGE_QUEUE_H */ 134 | -------------------------------------------------------------------------------- /src/posix++/pathname.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_PATHNAME_H 4 | #define POSIXXX_PATHNAME_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for std::invalid_argument */ 13 | #include /* for std::string */ 14 | 15 | namespace posix { 16 | class pathname; 17 | } 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | /** 22 | * Represents a POSIX pathname. 23 | * 24 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266 25 | */ 26 | class posix::pathname { 27 | std::string _string; 28 | 29 | public: 30 | /** 31 | * Default constructor. 32 | */ 33 | pathname() = default; 34 | 35 | /** 36 | * Constructor. 37 | */ 38 | pathname(const std::string& path) 39 | : _string{path} {} 40 | 41 | /** 42 | * Constructor. 43 | */ 44 | pathname(const char* const path) 45 | : _string{path ? path : ""} { 46 | if (!path) { 47 | throw std::invalid_argument("path cannot be nullptr"); 48 | } 49 | } 50 | 51 | /** 52 | * Copy constructor. 53 | */ 54 | pathname(const pathname& other) = default; 55 | 56 | /** 57 | * Move constructor. 58 | */ 59 | pathname(pathname&& other) noexcept = default; 60 | 61 | /** 62 | * Destructor. 63 | */ 64 | ~pathname() noexcept = default; 65 | 66 | /** 67 | * Copy assignment operator. 68 | */ 69 | pathname& operator=(const pathname& other) = default; 70 | 71 | /** 72 | * Move assignment operator. 73 | */ 74 | pathname& operator=(pathname&& other) /*noexcept*/ = default; 75 | 76 | /** 77 | * Determines whether this pathname is the empty string. 78 | */ 79 | bool empty() const noexcept { 80 | return _string.empty(); 81 | } 82 | 83 | /** 84 | * Returns the length of this pathname. 85 | */ 86 | std::size_t size() const noexcept { 87 | return _string.size(); 88 | } 89 | 90 | /** 91 | * Returns this pathname as a string. 92 | */ 93 | const std::string& string() const noexcept { 94 | return _string; 95 | } 96 | 97 | /** 98 | * Returns this pathname as a C string. 99 | */ 100 | const char* c_str() const noexcept { 101 | return _string.c_str(); 102 | } 103 | 104 | /** 105 | * Determines whether this is an absolute pathname. 106 | * 107 | * @pre the pathname must not be empty. 108 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_02 109 | */ 110 | bool is_absolute() const noexcept { 111 | return _string[0] == '/'; 112 | } 113 | 114 | /** 115 | * Determines whether this is a relative pathname. 116 | * 117 | * @pre the pathname must not be empty. 118 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_320 119 | */ 120 | bool is_relative() const noexcept { 121 | return !is_absolute(); 122 | } 123 | 124 | /** 125 | * Determines whether this is a portable pathname. 126 | * 127 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_277 128 | */ 129 | bool is_portable() const noexcept; 130 | 131 | /** 132 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_129 133 | */ 134 | posix::pathname dirname() const; 135 | 136 | /** 137 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_40 138 | */ 139 | posix::pathname basename() const; 140 | 141 | /** 142 | * Determines whether the file designated by this pathname exists. 143 | * 144 | * @throws posix::error on failure 145 | */ 146 | bool exists() const; 147 | 148 | /** 149 | * Removes the file designated by this pathname. 150 | * 151 | * @throws posix::error on failure 152 | */ 153 | void unlink() const; 154 | 155 | /** 156 | * Appends the given string to this pathname. 157 | * 158 | * @return `*this` 159 | */ 160 | pathname& append(const char* path); 161 | }; 162 | 163 | //////////////////////////////////////////////////////////////////////////////// 164 | 165 | #endif /* POSIXXX_PATHNAME_H */ 166 | -------------------------------------------------------------------------------- /src/posix++/memory_mapping.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MEMORY_MAPPING_H 4 | #define POSIXXX_MEMORY_MAPPING_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for std::size_t */ 13 | #include /* for std::uint8_t */ 14 | 15 | namespace posix { 16 | struct descriptor; 17 | class memory_mapping; 18 | } 19 | 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | /** 24 | * Represents a POSIX memory mapping. 25 | * 26 | * @see http://en.wikipedia.org/wiki/Memory-mapped_file 27 | */ 28 | class posix::memory_mapping { 29 | protected: 30 | std::size_t _size; 31 | std::uint8_t* _data; 32 | 33 | protected: 34 | std::uint8_t* map(int fd, std::size_t size, std::size_t offset); 35 | 36 | public: 37 | /** 38 | * Default constructor. 39 | */ 40 | memory_mapping() noexcept = delete; 41 | 42 | /** 43 | * Constructor. 44 | * 45 | * @throws posix::error on failure 46 | */ 47 | memory_mapping(const descriptor& descriptor); 48 | 49 | /** 50 | * Constructor. 51 | * 52 | * @throws posix::error on failure 53 | */ 54 | memory_mapping(const descriptor& descriptor, std::size_t size, std::size_t offset = 0); 55 | 56 | /** 57 | * Constructor. 58 | * 59 | * @throws posix::error on failure 60 | */ 61 | memory_mapping(int fd); 62 | 63 | /** 64 | * Constructor. 65 | * 66 | * @throws posix::error on failure 67 | */ 68 | memory_mapping(int fd, std::size_t size, std::size_t offset = 0); 69 | 70 | /** 71 | * Constructor. 72 | * 73 | * @throws posix::error on failure 74 | */ 75 | memory_mapping(void* data, std::size_t size) noexcept; 76 | 77 | /** 78 | * Copy constructor. 79 | */ 80 | memory_mapping(const memory_mapping& other) noexcept = delete; 81 | 82 | /** 83 | * Move constructor. 84 | */ 85 | memory_mapping(memory_mapping&& other) noexcept = default; 86 | 87 | /** 88 | * Copy assignment operator. 89 | */ 90 | memory_mapping& operator=(const memory_mapping& other) noexcept = delete; 91 | 92 | /** 93 | * Move assignment operator. 94 | */ 95 | memory_mapping& operator=(memory_mapping&& other) noexcept = default; 96 | 97 | /** 98 | * Destructor. 99 | */ 100 | ~memory_mapping() noexcept; 101 | 102 | /** 103 | * Expands or shrinks this mapping. 104 | * 105 | * @note Not available on all platforms. 106 | */ 107 | void remap(std::size_t new_size, int flags = 0); 108 | 109 | /** 110 | * Returns the byte size of the mapping. 111 | */ 112 | std::size_t size() const noexcept { 113 | return _size; 114 | } 115 | 116 | /** 117 | * Returns a pointer to the mapped memory. 118 | */ 119 | template 120 | T* data(const std::size_t offset = 0) noexcept { 121 | return reinterpret_cast(_data + offset); 122 | } 123 | 124 | /** 125 | * Returns a pointer to the mapped memory. 126 | */ 127 | template 128 | const T* data(const std::size_t offset = 0) const noexcept { 129 | return reinterpret_cast(_data + offset); 130 | } 131 | 132 | /** 133 | * Returns a pointer to the mapped memory. 134 | */ 135 | std::uint8_t* data(const std::size_t offset = 0) noexcept { 136 | return _data + offset; 137 | } 138 | 139 | /** 140 | * Returns a pointer to the mapped memory. 141 | */ 142 | const std::uint8_t* data(const std::size_t offset = 0) const noexcept { 143 | return _data + offset; 144 | } 145 | 146 | /** 147 | * Returns the byte at the given offset. 148 | */ 149 | std::uint8_t operator[](const std::size_t offset) const noexcept { 150 | return _data[offset]; 151 | } 152 | 153 | /** 154 | * ... 155 | */ 156 | explicit operator bool() const noexcept { 157 | return _data != nullptr; 158 | } 159 | 160 | /** 161 | * Checks whether this memory mapping is readable. 162 | */ 163 | bool readable() const noexcept; 164 | 165 | /** 166 | * Checks whether this memory mapping is writable. 167 | */ 168 | bool writable() const noexcept; 169 | 170 | /** 171 | * Checks whether this memory mapping is writable. 172 | */ 173 | bool executable() const noexcept; 174 | }; 175 | 176 | //////////////////////////////////////////////////////////////////////////////// 177 | 178 | #endif /* POSIXXX_MEMORY_MAPPING_H */ 179 | -------------------------------------------------------------------------------- /src/posix++/semaphore.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_SEMAPHORE_H 4 | #define POSIXXX_SEMAPHORE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | /** 13 | * @file 14 | * 15 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16 16 | */ 17 | 18 | #include "error.h" 19 | 20 | #include /* for sem_t, sem_*() */ 21 | 22 | namespace posix { 23 | class unnamed_semaphore; 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | /** 29 | * An unnamed POSIX semaphore. 30 | * 31 | * @note Instances of this class are neither movable nor copyable. 32 | * 33 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/semaphore.h.html 34 | */ 35 | class posix::unnamed_semaphore final { 36 | sem_t _state; 37 | 38 | public: 39 | /** 40 | * Default constructor. 41 | */ 42 | unnamed_semaphore() : unnamed_semaphore{0} {} 43 | 44 | /** 45 | * Constructor. 46 | * 47 | * @param value the initial state of this semaphore (0 or 1) 48 | * @param is_shared whether this semaphore is shared between processes 49 | * 50 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html 51 | */ 52 | unnamed_semaphore(unsigned int value, 53 | bool is_shared = false) { 54 | if (sem_init(&_state, is_shared ? 1 : 0, value) == -1) { 55 | throw_error("sem_init"); 56 | } 57 | } 58 | 59 | /** 60 | * Copy constructor. 61 | */ 62 | unnamed_semaphore(const unnamed_semaphore& other) noexcept = delete; 63 | 64 | /** 65 | * Move constructor. 66 | */ 67 | unnamed_semaphore(unnamed_semaphore&& other) noexcept = delete; 68 | 69 | /** 70 | * Destructor. 71 | */ 72 | ~unnamed_semaphore() noexcept { 73 | try { destroy(); } catch (const posix::error&) {} 74 | } 75 | 76 | /** 77 | * Copy assignment operator. 78 | */ 79 | unnamed_semaphore& operator=(const unnamed_semaphore& other) noexcept = delete; 80 | 81 | /** 82 | * Move assignment operator. 83 | */ 84 | unnamed_semaphore& operator=(unnamed_semaphore&& other) noexcept = delete; 85 | 86 | /** 87 | * Returns the current value of this semaphore. 88 | * 89 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html 90 | */ 91 | int value() const { 92 | int sval = 0; 93 | if (sem_getvalue(const_cast(&_state), &sval) == -1) { 94 | throw_error("sem_getvalue"); 95 | } 96 | return sval; 97 | } 98 | 99 | /** 100 | * Destroys this semaphore. 101 | * 102 | * @note This method is *not* idempotent. 103 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html 104 | */ 105 | void destroy() { 106 | if (sem_destroy(&_state) == -1) { 107 | throw_error("sem_destroy"); 108 | } 109 | } 110 | 111 | /** 112 | * Locks this semaphore. 113 | * 114 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_wait.html 115 | */ 116 | void wait() { 117 | for (;;) { 118 | if (sem_wait(&_state) == -1) { 119 | if (errno != EINTR) { 120 | throw_error("sem_wait"); 121 | } 122 | } 123 | else { 124 | break; 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * Attempts to lock this semaphore, without blocking. 131 | * 132 | * @retval true if the function succeeded in locking this semaphore 133 | * @retval false otherwise 134 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html 135 | */ 136 | bool try_wait() { 137 | for (;;) { 138 | if (sem_trywait(&_state) == -1) { 139 | if (errno == EAGAIN) { 140 | return false; 141 | } 142 | if (errno != EINTR) { 143 | throw_error("sem_trywait"); 144 | } 145 | } 146 | else { 147 | return true; 148 | } 149 | } 150 | } 151 | 152 | /** 153 | * Unlocks this semaphore. 154 | * 155 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html 156 | */ 157 | void notify() { 158 | if (sem_post(&_state) == -1) { 159 | throw_error("sem_post"); 160 | } 161 | } 162 | }; 163 | 164 | //////////////////////////////////////////////////////////////////////////////// 165 | 166 | #endif /* POSIXXX_SEMAPHORE_H */ 167 | -------------------------------------------------------------------------------- /src/posix++/message_queue.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "error.h" 8 | #include "message_queue.h" 9 | 10 | #include /* for std::min() */ 11 | #include /* for assert() */ 12 | #include /* for errno */ 13 | #include /* for std::memcpy() */ 14 | #include /* for std::time(), mq_timed*() */ 15 | #include /* for O_* */ 16 | #include /* for mq_*() */ 17 | 18 | using namespace posix; 19 | 20 | static_assert(sizeof(mqd_t) <= sizeof(int), 21 | "sizeof(mqd_t) > sizeof(int)"); 22 | 23 | message_queue 24 | message_queue::open(const std::string& name, 25 | const int flags, 26 | const mode mode) { 27 | assert(!name.empty()); 28 | 29 | return open(name.c_str(), flags, mode, nullptr); 30 | } 31 | 32 | message_queue 33 | message_queue::open(const std::string& name, 34 | const int flags, 35 | const mode mode, 36 | const message_queue_attr& attributes) { 37 | assert(!name.empty()); 38 | 39 | return open(name.c_str(), flags, mode, &attributes); 40 | } 41 | 42 | /** 43 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html 44 | */ 45 | message_queue 46 | message_queue::open(const char* const name, 47 | const int flags, 48 | const mode mode, 49 | const message_queue_attr* const attributes) { 50 | assert(name); 51 | 52 | mq_attr attr; 53 | if (attributes) { 54 | attr.mq_flags = attributes->flags; 55 | attr.mq_maxmsg = attributes->maxmsg; 56 | attr.mq_msgsize = attributes->msgsize; 57 | } 58 | 59 | mqd_t mqd; 60 | if ((mqd = mq_open(name, flags, mode, attributes ? &attr : nullptr)) == -1) { 61 | throw_error("mq_open", "\"%s\", 0x%x, 0%o, %s", 62 | name, static_cast(flags), static_cast(mode), "attr"); 63 | } 64 | 65 | return message_queue(mqd); 66 | } 67 | 68 | /** 69 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html 70 | */ 71 | void 72 | message_queue::unlink(const char* const queue_name) { 73 | assert(queue_name); 74 | 75 | if (mq_unlink(queue_name) == -1) { 76 | throw_error("mq_unlink", "\"%s\"", queue_name); 77 | } 78 | } 79 | 80 | message_queue::~message_queue() noexcept { 81 | close(); 82 | } 83 | 84 | /** 85 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html 86 | */ 87 | void 88 | message_queue::close() noexcept { 89 | if (valid()) { 90 | if (mq_close(static_cast(_fd)) == -1) { 91 | /* Ignore any errors from mq_close(). */ 92 | } 93 | _fd = -1; 94 | } 95 | } 96 | 97 | /** 98 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html 99 | */ 100 | std::size_t 101 | message_queue::recv(void* message_data, 102 | const std::size_t message_size, 103 | unsigned int* message_priority, 104 | const std::int64_t recv_timeout) { 105 | assert(message_data); 106 | assert(recv_timeout >= -1); 107 | 108 | char buffer[_message_size]; 109 | 110 | ssize_t rc; 111 | if (recv_timeout == -1) { 112 | rc = mq_receive(fd(), buffer, _message_size, message_priority); 113 | } 114 | else { 115 | const struct timespec timeout = { 116 | .tv_sec = std::time(nullptr) + (recv_timeout / 1000), 117 | .tv_nsec = (recv_timeout % 1000) * 1000000, 118 | }; 119 | rc = mq_timedreceive(fd(), buffer, _message_size, message_priority, &timeout); 120 | } 121 | 122 | if (rc == -1) { 123 | throw_error((recv_timeout == -1) ? "mq_receive" : "mq_timedreceive"); // TODO: annotate with more information 124 | } 125 | 126 | std::memcpy(message_data, buffer, std::min(message_size, _message_size)); 127 | 128 | return static_cast(rc); 129 | } 130 | 131 | /** 132 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html 133 | */ 134 | void 135 | message_queue::send(const void* const message_data, 136 | const std::size_t message_size, 137 | const unsigned int message_priority, 138 | const std::int64_t send_timeout) { 139 | assert(message_data); 140 | assert(send_timeout >= -1); 141 | 142 | int rc; 143 | if (send_timeout == -1) { 144 | rc = mq_send(fd(), reinterpret_cast(message_data), message_size, message_priority); 145 | } 146 | else { 147 | const struct timespec timeout = { 148 | .tv_sec = std::time(nullptr) + (send_timeout / 1000), 149 | .tv_nsec = (send_timeout % 1000) * 1000000, 150 | }; 151 | rc = mq_timedsend(fd(), reinterpret_cast(message_data), message_size, message_priority, &timeout); 152 | } 153 | 154 | if (rc == -1) { 155 | throw_error((send_timeout == -1) ? "mq_send" : "mq_timedsend"); // TODO: annotate with more information 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/posix++/error.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "error.h" 8 | 9 | #include /* for std::array */ 10 | #include /* for assert() */ 11 | #include /* for E*, errno */ 12 | #include /* for va_*() */ 13 | #include /* for std::snprintf() */ 14 | #include /* for stpncpy() */ 15 | 16 | using namespace posix; 17 | 18 | error::error() noexcept 19 | : std::system_error{errno, std::system_category()} {} 20 | 21 | bad_descriptor::bad_descriptor() noexcept 22 | : logic_error{EBADF} {} 23 | 24 | bad_descriptor::bad_descriptor(const char* const origin) noexcept 25 | : logic_error{EBADF, std::generic_category(), origin} {} 26 | 27 | bad_address::bad_address() noexcept 28 | : logic_error{EFAULT} {} 29 | 30 | bad_address::bad_address(const char* const origin) noexcept 31 | : logic_error{EFAULT, std::generic_category(), origin} {} 32 | 33 | invalid_argument::invalid_argument() noexcept 34 | : logic_error{EINVAL} {} 35 | 36 | invalid_argument::invalid_argument(const char* const origin) noexcept 37 | : logic_error{EINVAL, std::generic_category(), origin} {} 38 | 39 | connection_refused::connection_refused() noexcept 40 | : runtime_error{ECONNREFUSED} {} 41 | 42 | connection_refused::connection_refused(const char* const origin) noexcept 43 | : runtime_error{ECONNREFUSED, std::generic_category(), origin} {} 44 | 45 | void 46 | posix::throw_error() { 47 | throw_error(errno, nullptr, nullptr); 48 | } 49 | 50 | void 51 | posix::throw_error(const char* const origin, 52 | const char* const format, 53 | ...) { 54 | va_list args; 55 | va_start(args, format); 56 | throw_error(errno, origin, format, args); 57 | va_end(args); /* never reached */ 58 | } 59 | 60 | void 61 | posix::throw_error(const int code) { 62 | throw_error(code, nullptr, nullptr); 63 | } 64 | 65 | void 66 | posix::throw_error(const int code, 67 | const char* const origin, 68 | const char* const format, 69 | ...) { 70 | va_list args; 71 | va_start(args, format); 72 | throw_error(code, origin, format, args); 73 | va_end(args); /* never reached */ 74 | } 75 | 76 | void 77 | posix::throw_error(const int code, 78 | const char* const origin, 79 | const char* const format, 80 | va_list args) { 81 | static thread_local std::array buffer; 82 | 83 | const char* what = nullptr; 84 | if (!origin) { 85 | what = nullptr; 86 | buffer.data()[0] = '\0'; 87 | } 88 | else { 89 | what = buffer.data(); 90 | 91 | auto buffer_offset = std::snprintf(buffer.data(), buffer.size(), "%s(", origin); 92 | assert(buffer_offset < buffer.size()); 93 | 94 | if (format) { // FIXME: better buffer overflow handling. 95 | buffer_offset += std::vsnprintf(buffer.data() + buffer_offset, buffer.size() - buffer_offset, format, args); 96 | assert(buffer_offset < buffer.size()); 97 | } 98 | 99 | buffer.data()[buffer_offset++] = ')'; 100 | assert(buffer_offset < buffer.size()); 101 | 102 | buffer.data()[buffer_offset++] = '\0'; 103 | } 104 | 105 | switch (code) { 106 | case EBADF: /* Bad file descriptor */ 107 | throw !what ? bad_descriptor{} : bad_descriptor{what}; 108 | case ECONNREFUSED: /* Connection refused */ 109 | throw !what ? connection_refused{} : connection_refused{what}; 110 | case EFAULT: /* Bad address */ 111 | throw !what ? bad_address{} : bad_address{what}; 112 | case EINVAL: /* Invalid argument */ 113 | throw !what ? invalid_argument{} : invalid_argument{what}; 114 | case EMFILE: /* Too many open files */ 115 | throw !what ? fatal_error{code} : fatal_error{code, std::generic_category(), what}; 116 | case EMSGSIZE: /* Message too long */ 117 | throw !what ? logic_error{code} : logic_error{code, std::generic_category(), what}; 118 | case ENAMETOOLONG: /* File name too long */ 119 | throw !what ? logic_error{code} : logic_error{code, std::generic_category(), what}; 120 | case ENFILE: /* Too many open files in system */ 121 | throw !what ? fatal_error{code} : fatal_error{code, std::generic_category(), what}; 122 | case ENOBUFS: /* No buffer space available in kernel */ 123 | throw !what ? fatal_error{code} : fatal_error{code, std::generic_category(), what}; 124 | case ENOMEM: /* Cannot allocate memory in kernel */ 125 | throw !what ? fatal_error{code} : fatal_error{code, std::generic_category(), what}; 126 | case ENOSPC: /* No space left on device */ 127 | throw !what ? fatal_error{code} : fatal_error{code, std::generic_category(), what}; 128 | case ENOSYS: /* Function not implemented */ 129 | throw !what ? logic_error{code} : logic_error{code, std::generic_category(), what}; 130 | case ENOTDIR: /* Not a directory */ 131 | throw !what ? logic_error{code} : logic_error{code, std::generic_category(), what}; 132 | case EACCES: /* Permission denied */ 133 | case ELOOP: /* Too many levels of symbolic links */ 134 | case ENOENT: /* No such file or directory */ 135 | case ENOPROTOOPT: /* Protocol not available */ 136 | case ENOTCONN: /* Transport endpoint is not connected */ 137 | case ENOTSOCK: /* Socket operation on non-socket */ 138 | default: 139 | throw !what ? runtime_error{code} : runtime_error{code, std::generic_category(), what}; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/posix++/directory.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_DIRECTORY_H 4 | #define POSIXXX_DIRECTORY_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for AT_* */ 13 | #include /* for struct stat */ 14 | 15 | #include "descriptor.h" 16 | #include "mode.h" 17 | 18 | #include /* for std::uint8_t */ 19 | #include /* for std::function */ 20 | #include /* for std::string */ 21 | #include /* for std::move() */ 22 | 23 | namespace posix { 24 | class directory; 25 | class pathname; 26 | } 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | 30 | /** 31 | * Represents a POSIX directory. 32 | * 33 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_128 34 | */ 35 | class posix::directory : public posix::descriptor { 36 | public: 37 | class iterator; 38 | 39 | /** 40 | * Directory entry. 41 | */ 42 | struct entry { 43 | std::uint8_t type; 44 | ino_t inode; 45 | std::string name; 46 | }; 47 | 48 | /** 49 | * Opens the current working directory. 50 | */ 51 | static directory current() { 52 | return open("."); 53 | } 54 | 55 | /** 56 | * Returns a temporary directory. 57 | */ 58 | static directory temporary(const char* basename = nullptr); 59 | 60 | /** 61 | * Opens an existing directory. 62 | */ 63 | static directory open(const pathname& pathname); 64 | 65 | /** 66 | * Opens an existing directory. 67 | */ 68 | static directory open(const char* pathname); 69 | 70 | /** 71 | * Opens an existing directory. 72 | */ 73 | static directory open(const directory& directory, const pathname& pathname); 74 | 75 | /** 76 | * Opens an existing directory. 77 | */ 78 | static directory open(const directory& directory, const char* pathname); 79 | 80 | /** 81 | * Constructor. 82 | */ 83 | directory(const int fd) noexcept 84 | : descriptor(fd) {} 85 | 86 | /** 87 | * Copy constructor. 88 | */ 89 | directory(const directory& other) /* may throw */ 90 | : descriptor(other) {} 91 | 92 | /** 93 | * Move constructor. 94 | */ 95 | directory(directory&& other) noexcept 96 | : descriptor(std::move(other)) {} 97 | 98 | /** 99 | * Destructor. 100 | */ 101 | ~directory() noexcept = default; 102 | 103 | // TODO: copy/move assignment operators. 104 | 105 | /** 106 | * Checks whether this directory is empty. 107 | * 108 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_144 109 | */ 110 | bool empty() const; 111 | 112 | /** 113 | * Checks whether a given directory entry exists. 114 | */ 115 | std::size_t count(const char* pathname) const; 116 | 117 | /** 118 | * Returns information about a given directory entry. 119 | */ 120 | bool stat(const char* pathname, struct stat& result, int flags = 0) const; 121 | 122 | /** 123 | * Creates a hard link relative to this directory. 124 | */ 125 | void link(const char* old_pathname, const char* new_pathname) const; 126 | 127 | /** 128 | * Creates a symbolic link relative to this directory. 129 | */ 130 | void symlink(const char* old_pathname, const char* new_pathname) const; 131 | 132 | /** 133 | * Creates a subdirectory. 134 | */ 135 | void mkdir(const char* pathname, mode mode) const; 136 | 137 | /** 138 | * Removes the specified directory entry, which must be an empty subdirectory. 139 | */ 140 | void rmdir(const char* pathname) const; 141 | 142 | /** 143 | * Removes the specified directory entry. 144 | */ 145 | void unlink(const char* pathname) const; 146 | 147 | /** 148 | * Renames the specified directory entry to a new pathname. 149 | */ 150 | void rename(const char* old_pathname, const char* new_pathname) const; 151 | 152 | /** 153 | * Renames the specified directory entry to a new pathname. 154 | */ 155 | void rename(const char* old_pathname, const directory& new_directory, const char* new_pathname) const; 156 | 157 | /** 158 | * Reads the target path of a symbolic link. 159 | */ 160 | pathname readlink(const char* pathname) const; 161 | 162 | void for_each(std::function callback) const; 163 | 164 | iterator begin() const; 165 | 166 | iterator end() const; 167 | 168 | const iterator cbegin() const; 169 | 170 | const iterator cend() const; 171 | 172 | protected: 173 | static directory open(int dirfd, const char* pathname); 174 | 175 | void unlink(const char* pathname, int flags) const; 176 | }; 177 | 178 | //////////////////////////////////////////////////////////////////////////////// 179 | 180 | /** 181 | * POSIX directory iterator. 182 | */ 183 | class posix::directory::iterator { 184 | public: 185 | /** 186 | * Default constructor. 187 | */ 188 | iterator() noexcept = default; 189 | 190 | /** 191 | * Constructor. 192 | */ 193 | iterator(const directory& dir); 194 | 195 | /** 196 | * Destructor. 197 | */ 198 | ~iterator() noexcept; 199 | 200 | bool operator==(const iterator& other) { 201 | return !operator!=(other); 202 | } 203 | 204 | bool operator!=(const iterator& other); 205 | 206 | iterator& operator++(); 207 | 208 | std::string operator*(); 209 | 210 | protected: 211 | void* _dirp = nullptr; 212 | }; 213 | 214 | //////////////////////////////////////////////////////////////////////////////// 215 | 216 | #endif /* POSIXXX_DIRECTORY_H */ 217 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl configure.ac -*- Autoconf -*- 2 | dnl 3 | dnl Process this file with `autoconf` to produce a configure script. 4 | dnl 5 | dnl This is free and unencumbered software released into the public domain. 6 | AC_PREREQ([2.68]) 7 | 8 | dnl Define version information: 9 | m4_define([VERSION_MAJOR], 10 | m4_esyscmd([cut -d'.' -f1 VERSION | tr -d '\n'])) 11 | m4_define([VERSION_MINOR], 12 | m4_esyscmd([cut -d'.' -f2 VERSION | tr -d '\n'])) 13 | m4_define([VERSION_PATCH], 14 | m4_esyscmd([cut -d'.' -f3 VERSION | tr -d '\n'])) 15 | m4_define([VERSION_STRING], 16 | m4_esyscmd([git describe --dirty --always | tr -d '\n'])) 17 | 18 | dnl Define package information: 19 | AC_INIT([libposix++], [VERSION_STRING], 20 | [arto@bendiken.net], [libposix++], 21 | [https://github.com/dryproject/libposixxx]) 22 | 23 | dnl Configure Autoconf: 24 | AC_CONFIG_HEADERS([src/config.h]) 25 | AC_CONFIG_SRCDIR([src/posix++.h]) 26 | AC_CONFIG_AUX_DIR([etc/aclocal]) 27 | AC_CONFIG_MACRO_DIR([etc/aclocal]) 28 | AC_CONFIG_LIBOBJ_DIR([lib]) 29 | 30 | dnl Configure Automake: 31 | AM_INIT_AUTOMAKE([foreign -Wall -Werror dist-bzip2 subdir-objects nostdinc]) 32 | AM_SILENT_RULES([yes]) 33 | 34 | dnl Check for programs: 35 | AC_PROG_CC(clang gcc cc) 36 | AC_PROG_CC_C99 37 | AM_PROG_CC_C_O 38 | AC_PROG_CPP 39 | AC_PROG_CXX(clang++ g++ c++) 40 | AC_PROG_CXXCPP 41 | AC_USE_SYSTEM_EXTENSIONS 42 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 43 | LT_INIT 44 | AC_LANG([C++]) 45 | AX_CXX_COMPILE_STDCXX_11([noext]) 46 | 47 | dnl Check for configuration options: 48 | # --with-stdlib=libstdc++|libc++ 49 | AC_ARG_WITH([stdlib], 50 | [AS_HELP_STRING([--with-stdlib=LIB], [specify the C++ standard library to use [default=system]])], 51 | [], [with_stdlib=system]) 52 | AS_IF([test "x$with_stdlib" != "xsystem"], 53 | [CXXFLAGS="$CXXFLAGS -stdlib=$with_stdlib" 54 | LDFLAGS="$LDFLAGS -stdlib=$with_stdlib"]) 55 | # --enable-debug/--disable-debug 56 | AC_ARG_ENABLE([debug], 57 | [AS_HELP_STRING([--enable-debug], [build with debugging support [default=no]])], 58 | [], [enable_debug=no]) 59 | AS_IF([test "x$enable_debug" != "xno"], 60 | [AC_DEFINE([DEBUG], [1], [Enable debugging support.])], 61 | [AC_DEFINE([NDEBUG], [1], [Disable assertions.])]) 62 | AM_CONDITIONAL([ENABLE_DEBUG], [test "x$enable_debug" != "xno"]) 63 | # --enable-unicode/--disable-unicode 64 | AC_ARG_ENABLE([unicode], 65 | [AS_HELP_STRING([--disable-unicode], [omit support for Unicode strings])]) 66 | AS_IF([test "x$enable_unicode" == "xno"], 67 | [AC_DEFINE([DISABLE_UNICODE], 1, [Define to disable Unicode string support.])]) 68 | # --enable-mqueue/--disable-mqueue 69 | AC_ARG_ENABLE([mqueue], 70 | [AS_HELP_STRING([--disable-mqueue], [omit support for POSIX message queues])]) 71 | AS_IF([test "x$enable_mqueue" == "xno"], [ 72 | AC_DEFINE([DISABLE_MQUEUE], 1, [Define to disable POSIX message queue support.])]) 73 | AM_CONDITIONAL([DISABLE_MQUEUE], [test "x$enable_mqueue" == "xno"]) 74 | # --enable-semaphore/--disable-semaphore 75 | AC_ARG_ENABLE([semaphore], 76 | [AS_HELP_STRING([--disable-semaphore], [omit support for POSIX/SysV semaphores])]) 77 | AS_IF([test "x$enable_semaphore" == "xno"], [ 78 | AC_DEFINE([DISABLE_SEMAPHORE], 1, [Define to disable POSIX/SysV semaphore support.])]) 79 | AM_CONDITIONAL([DISABLE_SEMAPHORE], [test "x$enable_semaphore" == "xno"]) 80 | # --enable-stdio/--disable-stdio 81 | AC_ARG_ENABLE([stdio], 82 | [AS_HELP_STRING([--disable-stdio], [omit support for POSIX standard I/O])]) 83 | AS_IF([test "x$enable_stdio" == "xno"], [ 84 | AC_DEFINE([DISABLE_STDIO], 1, [Define to disable POSIX standard I/O support.])]) 85 | AM_CONDITIONAL([DISABLE_STDIO], [test "x$enable_stdio" == "xno"]) 86 | # --enable-socket/--disable-socket 87 | AC_ARG_ENABLE([socket], 88 | [AS_HELP_STRING([--disable-socket], [omit support for POSIX sockets])]) 89 | AS_IF([test "x$enable_socket" == "xno"], [ 90 | AC_DEFINE([DISABLE_SOCKET], 1, [Define to disable POSIX socket support.])]) 91 | AM_CONDITIONAL([DISABLE_SOCKET], [test "x$enable_socket" == "xno"]) 92 | # --enable-sysv/--disable-sysv 93 | AC_ARG_ENABLE([sysv], 94 | [AS_HELP_STRING([--disable-sysv], [omit support for SysV features])]) 95 | AS_IF([test "x$enable_sysv" == "xno"], [ 96 | AC_DEFINE([DISABLE_SYSV], 1, [Define to disable SysV feature support.])]) 97 | AM_CONDITIONAL([DISABLE_SYSV], [test "x$enable_sysv" == "xno"]) 98 | 99 | dnl Check for libraries: 100 | 101 | dnl Check for header files: 102 | AC_LANG_PUSH([C]) 103 | AC_HEADER_ASSERT 104 | AC_HEADER_STDBOOL 105 | AC_CHECK_HEADERS_ONCE([mqueue.h]) 106 | AC_LANG_POP([C]) 107 | 108 | dnl Check for types: 109 | 110 | dnl Check for structures: 111 | 112 | dnl Check for compiler characteristics: 113 | AC_CANONICAL_HOST 114 | AM_CPPFLAGS="$AM_CPPFLAGS -I\$(top_srcdir)/src -iquote \$(srcdir)" 115 | AM_CXXFLAGS="$AM_CXXFLAGS -Wall -Wextra -pipe" 116 | AM_LDFLAGS="$AM_LDFLAGS" 117 | AS_CASE([$host_os], [linux*], [AM_LDFLAGS="$AM_LDFLAGS -pthread -lrt"], []) 118 | TEST_CPPFLAGS="$AM_CPPFLAGS -DCATCH_CONFIG_MAIN" 119 | TEST_CXXFLAGS="$AM_CXXFLAGS" 120 | TEST_LDFLAGS="$AM_LDFLAGS" 121 | AC_SUBST([AM_CPPFLAGS]) 122 | AC_SUBST([AM_CXXFLAGS]) 123 | AC_SUBST([AM_LDFLAGS]) 124 | AC_SUBST([TEST_CPPFLAGS]) 125 | AC_SUBST([TEST_CXXFLAGS]) 126 | AC_SUBST([TEST_LDFLAGS]) 127 | 128 | dnl Check for library functions: 129 | AC_CHECK_FUNCS_ONCE([accept4]) 130 | AC_REPLACE_FUNCS([fdopendir fstatat linkat mkdirat mkfifoat openat readlinkat renameat symlinkat unlinkat]) 131 | 132 | dnl Check for system services: 133 | 134 | dnl Generate output: 135 | AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile src/posix++/Makefile test/Makefile]) 136 | AC_SUBST([PACKAGE_VERSION_MAJOR], ["VERSION_MAJOR"]) 137 | AC_SUBST([PACKAGE_VERSION_MINOR], ["VERSION_MINOR"]) 138 | AC_SUBST([PACKAGE_VERSION_PATCH], ["VERSION_PATCH"]) 139 | AC_CONFIG_FILES([src/posix++/version.h]) 140 | AH_BOTTOM([#include "libcompat.h"]) 141 | AC_OUTPUT 142 | -------------------------------------------------------------------------------- /src/posix++/error.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_ERROR_H 4 | #define POSIXXX_ERROR_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for va_list */ 13 | #include /* for std::error_*, std::system_* */ 14 | 15 | namespace posix { 16 | class error; 17 | class logic_error; 18 | class runtime_error; 19 | class fatal_error; 20 | class bad_descriptor; 21 | class bad_address; 22 | class invalid_argument; 23 | class connection_refused; 24 | 25 | extern void throw_error[[noreturn]](); 26 | 27 | extern void throw_error[[noreturn]](const char* origin, const char* format = nullptr, ...); 28 | 29 | extern void throw_error[[noreturn]](int code); 30 | extern void throw_error[[noreturn]](int code, const char* origin, const char* format = nullptr, ...); 31 | extern void throw_error[[noreturn]](int code, const char* origin, const char* format, va_list args); 32 | } 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | /** 37 | * Represents a POSIX runtime error. 38 | * 39 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 40 | */ 41 | class posix::error : public std::system_error { 42 | public: 43 | /** 44 | * Default constructor. 45 | * 46 | * Will use the current value of `errno` as the error code. 47 | */ 48 | error() noexcept; 49 | 50 | /** 51 | * Constructor. 52 | */ 53 | error(const std::error_code code) noexcept 54 | : std::system_error{code} {} 55 | 56 | /** 57 | * Constructor. 58 | */ 59 | error(const std::error_code code, 60 | const char* const what) noexcept 61 | : std::system_error{code, what} {} 62 | 63 | /** 64 | * Constructor. 65 | */ 66 | error(const int code) noexcept 67 | : std::system_error{code, std::generic_category()} {} 68 | 69 | /** 70 | * Constructor. 71 | */ 72 | error(const int code, 73 | const std::error_category& category) noexcept 74 | : std::system_error{code, category} {} 75 | 76 | /** 77 | * Constructor. 78 | */ 79 | error(const int code, 80 | const std::error_category& category, 81 | const char* const what) noexcept 82 | : std::system_error{code, category, what} {} 83 | 84 | /** 85 | * Returns the error number. 86 | */ 87 | int number() const noexcept { 88 | return code().value(); 89 | } 90 | }; 91 | 92 | //////////////////////////////////////////////////////////////////////////////// 93 | 94 | /** 95 | * Represents a POSIX logic error. 96 | * 97 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 98 | */ 99 | class posix::logic_error : public posix::error { 100 | public: 101 | using posix::error::error; /* inherit constructors */ 102 | }; 103 | 104 | //////////////////////////////////////////////////////////////////////////////// 105 | 106 | /** 107 | * Represents a POSIX runtime error. 108 | * 109 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 110 | */ 111 | class posix::runtime_error : public posix::error { 112 | public: 113 | using posix::error::error; /* inherit constructors */ 114 | }; 115 | 116 | //////////////////////////////////////////////////////////////////////////////// 117 | 118 | /** 119 | * Represents a POSIX runtime error that cannot be recovered from. 120 | * 121 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 122 | */ 123 | class posix::fatal_error : public posix::error { 124 | public: 125 | using posix::error::error; /* inherit constructors */ 126 | }; 127 | 128 | //////////////////////////////////////////////////////////////////////////////// 129 | 130 | /** 131 | * Represents a POSIX `EBADF` (Bad file descriptor) error. 132 | * 133 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 134 | */ 135 | class posix::bad_descriptor : public posix::logic_error { 136 | public: 137 | /** 138 | * Default constructor. 139 | */ 140 | bad_descriptor() noexcept; 141 | 142 | /** 143 | * Constructor. 144 | */ 145 | bad_descriptor(const char* origin) noexcept; 146 | }; 147 | 148 | //////////////////////////////////////////////////////////////////////////////// 149 | 150 | /** 151 | * Represents a POSIX `EFAULT` (Bad address) error. 152 | * 153 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 154 | */ 155 | class posix::bad_address : public posix::logic_error { 156 | public: 157 | /** 158 | * Default constructor. 159 | */ 160 | bad_address() noexcept; 161 | 162 | /** 163 | * Constructor. 164 | */ 165 | bad_address(const char* origin) noexcept; 166 | }; 167 | 168 | //////////////////////////////////////////////////////////////////////////////// 169 | 170 | /** 171 | * Represents a POSIX `EINVAL` (Invalid argument) error. 172 | * 173 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 174 | */ 175 | class posix::invalid_argument : public posix::logic_error { 176 | public: 177 | /** 178 | * Default constructor. 179 | */ 180 | invalid_argument() noexcept; 181 | 182 | /** 183 | * Constructor. 184 | */ 185 | invalid_argument(const char* origin) noexcept; 186 | }; 187 | 188 | //////////////////////////////////////////////////////////////////////////////// 189 | 190 | /** 191 | * Represents a POSIX `ECONNREFUSED` (Connection refused) error. 192 | * 193 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html 194 | */ 195 | class posix::connection_refused : public posix::runtime_error { 196 | public: 197 | /** 198 | * Default constructor. 199 | */ 200 | connection_refused() noexcept; 201 | 202 | /** 203 | * Constructor. 204 | */ 205 | connection_refused(const char* origin) noexcept; 206 | }; 207 | 208 | //////////////////////////////////////////////////////////////////////////////// 209 | 210 | #endif /* POSIXXX_ERROR_H */ 211 | -------------------------------------------------------------------------------- /src/posix++/mapped_file.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_MAPPED_FILE_H 4 | #define POSIXXX_MAPPED_FILE_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "file.h" 13 | #include "memory_mapping.h" 14 | 15 | #include /* for std::strlen() */ 16 | #include /* for std::string */ 17 | 18 | namespace posix { 19 | class mapped_file; 20 | class appendable_mapped_file; 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | /** 26 | * A memory-mapped file for random access. 27 | */ 28 | class posix::mapped_file : public posix::file { 29 | protected: 30 | std::size_t _size; 31 | std::size_t _offset; 32 | memory_mapping _mapping; 33 | 34 | public: 35 | static mapped_file open(const pathname& pathname, int flags, mode mode = 0); 36 | 37 | static mapped_file open(const directory& directory, const pathname& pathname, int flags, mode mode = 0); 38 | 39 | /** 40 | * Default constructor. 41 | */ 42 | mapped_file() noexcept 43 | : file{}, _size{0}, _offset{0}, _mapping{nullptr, 0} {} 44 | 45 | /** 46 | * Constructor. 47 | */ 48 | mapped_file(int dirfd, const char* pathname, int flags, mode mode = 0); 49 | 50 | /** 51 | * Move constructor. 52 | */ 53 | mapped_file(mapped_file&& other) noexcept; 54 | 55 | /** 56 | * Move assignment operator. 57 | */ 58 | mapped_file& operator=(mapped_file&& other) noexcept; 59 | 60 | /** 61 | * Checks whether this file's size is zero. 62 | */ 63 | bool empty() const noexcept { 64 | return size() == 0; 65 | } 66 | 67 | /** 68 | * Returns the current file size in bytes. 69 | */ 70 | std::size_t size() const noexcept { 71 | return _size; 72 | } 73 | 74 | /** 75 | * @copydoc posix::file::offset() 76 | */ 77 | std::size_t offset() const noexcept { 78 | return _offset; 79 | } 80 | 81 | /** 82 | * Synchronizes the memory mapping with secondary storage. 83 | * 84 | * @post `size()` returns the current file size 85 | */ 86 | void sync(); 87 | 88 | /** 89 | * @copydoc posix::file::rewind() 90 | */ 91 | void rewind() { 92 | seek(0, SEEK_SET); 93 | } 94 | 95 | /** 96 | * Checks whether the current file offset is at or past EOF. 97 | */ 98 | bool is_eof() const noexcept { 99 | return offset() >= size(); 100 | } 101 | 102 | /** 103 | * Returns a pointer to the mapped memory. 104 | */ 105 | template 106 | T* data(const std::size_t offset = 0) noexcept { 107 | return _mapping.data(offset); 108 | } 109 | 110 | /** 111 | * Returns a pointer to the mapped memory. 112 | */ 113 | template 114 | const T* data(const std::size_t offset = 0) const noexcept { 115 | return _mapping.data(offset); 116 | } 117 | 118 | /** 119 | * Returns a pointer to the mapped memory. 120 | */ 121 | std::uint8_t* data(const std::size_t offset = 0) noexcept { 122 | return _mapping.data(offset); 123 | } 124 | 125 | /** 126 | * Returns a pointer to the mapped memory. 127 | */ 128 | const std::uint8_t* data(const std::size_t offset = 0) const noexcept { 129 | return _mapping.data(offset); 130 | } 131 | 132 | /** 133 | * Returns the byte at the given offset. 134 | */ 135 | std::uint8_t operator[](const std::size_t offset) const noexcept { 136 | return _mapping[offset]; 137 | } 138 | 139 | /** 140 | * Returns or changes the current file offset. 141 | */ 142 | std::size_t seek(off_t offset, int whence = SEEK_SET); 143 | 144 | /** 145 | * Reads a line of text from this file. 146 | */ 147 | std::size_t read_line(std::string& buffer); 148 | 149 | /** 150 | * Reads data from this file until the given separator character 151 | * is encountered. 152 | */ 153 | std::size_t read_until(char separator, std::string& buffer); 154 | 155 | /** 156 | * Reads a character from this file. 157 | */ 158 | std::size_t read(char& result); 159 | 160 | /** 161 | * Reads data from this file. 162 | */ 163 | std::size_t read(void* buffer, std::size_t buffer_size); 164 | 165 | /** 166 | * Reads a string from this file. 167 | */ 168 | std::string read(); 169 | }; 170 | 171 | //////////////////////////////////////////////////////////////////////////////// 172 | 173 | /** 174 | * A memory-mapped file for append-only writes. 175 | */ 176 | class posix::appendable_mapped_file : public posix::mapped_file { 177 | public: 178 | static appendable_mapped_file open(const pathname& pathname, int flags, mode mode = 0); 179 | 180 | static appendable_mapped_file open(const directory& directory, const pathname& pathname, int flags, mode mode = 0); 181 | 182 | /** 183 | * Default constructor. 184 | */ 185 | appendable_mapped_file() noexcept 186 | : mapped_file{} {} 187 | 188 | /** 189 | * Constructor. 190 | */ 191 | using mapped_file::mapped_file; 192 | 193 | /** 194 | * Move constructor. 195 | */ 196 | appendable_mapped_file(appendable_mapped_file&& other) noexcept; 197 | 198 | /** 199 | * Move assignment operator. 200 | */ 201 | appendable_mapped_file& operator=(appendable_mapped_file&& other) noexcept; 202 | 203 | /** 204 | * @throws posix::runtime_error if an error occurs 205 | */ 206 | template 207 | std::size_t append(const T& data) { 208 | const char* const c_str = data.c_str(); 209 | return append(c_str, std::strlen(c_str)); 210 | } 211 | 212 | /** 213 | * @throws posix::runtime_error if an error occurs 214 | */ 215 | std::size_t append(const std::string& string) { 216 | return append(string.c_str(), string.size()); 217 | } 218 | 219 | /** 220 | * @throws posix::runtime_error if an error occurs 221 | */ 222 | std::size_t append(const void* data, std::size_t size); 223 | }; 224 | 225 | //////////////////////////////////////////////////////////////////////////////// 226 | 227 | #endif /* POSIXXX_MAPPED_FILE_H */ 228 | -------------------------------------------------------------------------------- /src/posix++/sysv_segment.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_SYSV_SEGMENT_H 4 | #define POSIXXX_SYSV_SEGMENT_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include /* for std::size_t */ 13 | #include /* for std::uint8_t */ 14 | #include /* for std::function */ 15 | #include /* for key_t */ 16 | #include /* for shmid_ds */ 17 | 18 | namespace posix { 19 | class sysv_segment; 20 | } 21 | 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | /** 26 | * Represents a System V shared memory segment. 27 | * 28 | * @note Instances of this class are movable, but not copyable. 29 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_342 30 | */ 31 | class posix::sysv_segment { 32 | int _id {-1}; 33 | void* _addr {nullptr}; 34 | std::size_t _size {0}; 35 | 36 | public: 37 | /** 38 | * Applies a function to every existing segment in the system. 39 | * 40 | * @throws posix::error on failure 41 | * @note This operation is Linux-specific. 42 | */ 43 | static void for_each(std::function callback); 44 | 45 | /** 46 | * Creates a new segment with a system-assigned, unique IPC key. 47 | * 48 | * @throws posix::error on failure 49 | */ 50 | static sysv_segment create_unique(std::size_t size, int flags = 0600); 51 | 52 | /** 53 | * Creates a new segment. 54 | * 55 | * @throws posix::error on failure 56 | */ 57 | static sysv_segment create(key_t key, std::size_t size, int flags = 0600); 58 | 59 | /** 60 | * Opens an existing segment. 61 | * 62 | * @throws posix::error on failure 63 | */ 64 | static sysv_segment open(key_t key); 65 | 66 | /** 67 | * Default constructor. 68 | */ 69 | sysv_segment() noexcept = delete; 70 | 71 | /** 72 | * Constructor. 73 | */ 74 | sysv_segment(const int shmid, 75 | void* const shmaddr = nullptr, 76 | std::size_t size = 0) 77 | : _id{shmid}, 78 | _addr{shmaddr}, 79 | _size{size} {} 80 | 81 | /** 82 | * Copy constructor. 83 | */ 84 | sysv_segment(const sysv_segment& other) noexcept = delete; 85 | 86 | /** 87 | * Move constructor. 88 | */ 89 | sysv_segment(sysv_segment&& other) noexcept = default; 90 | 91 | /** 92 | * Destructor. 93 | */ 94 | ~sysv_segment() noexcept; 95 | 96 | /** 97 | * Copy assignment operator. 98 | */ 99 | sysv_segment& operator=(const sysv_segment& other) noexcept = delete; 100 | 101 | /** 102 | * Move assignment operator. 103 | */ 104 | sysv_segment& operator=(sysv_segment&& other) noexcept = default; 105 | 106 | /** 107 | * Determines whether the segment is attached. 108 | */ 109 | bool is_attached() const noexcept { 110 | return _addr; 111 | } 112 | 113 | /** 114 | * Alias for `is_attached()`. 115 | * 116 | * @copydetails is_attached() 117 | */ 118 | bool is_mapped() const noexcept { 119 | return is_attached(); 120 | } 121 | 122 | /** 123 | * Returns the integer identifier for this segment. 124 | */ 125 | int id() const noexcept { 126 | return _id; 127 | } 128 | 129 | /** 130 | * Returns a pointer to the attached memory segment. 131 | * 132 | * @pre `is_attached()` must be `true`. 133 | */ 134 | template 135 | const T* data(const std::size_t offset = 0) const noexcept { 136 | return reinterpret_cast(data(offset)); 137 | } 138 | 139 | /** 140 | * Returns a pointer to the attached memory segment. 141 | * 142 | * @pre `is_attached()` must be `true`. 143 | */ 144 | template 145 | T* data(const std::size_t offset = 0) noexcept { 146 | return reinterpret_cast(data(offset)); 147 | } 148 | 149 | /** 150 | * Returns a pointer to the attached memory segment. 151 | * 152 | * @pre `is_attached()` must be `true`. 153 | */ 154 | const std::uint8_t* data(const std::size_t offset = 0) const noexcept { 155 | return reinterpret_cast(_addr) + offset; 156 | } 157 | 158 | /** 159 | * Returns a pointer to the attached memory segment. 160 | * 161 | * @pre `is_attached()` must be `true`. 162 | */ 163 | std::uint8_t* data(const std::size_t offset = 0) noexcept { 164 | return reinterpret_cast(_addr) + offset; 165 | } 166 | 167 | /** 168 | * Returns the byte size of this segment. 169 | * 170 | * @return the segment size, in bytes 171 | * @throws posix::error on failure 172 | */ 173 | std::size_t size() const; 174 | std::size_t size(); 175 | 176 | /** 177 | * Returns information from the associated kernel data structure. 178 | * 179 | * @throws posix::error on failure 180 | */ 181 | shmid_ds stat() const; 182 | 183 | /** 184 | * Attaches this segment to the process address space. 185 | * 186 | * @throws posix::error on failure 187 | * @note This operation is idempotent. 188 | */ 189 | void* attach(int flags = 0); 190 | 191 | /** 192 | * Detaches this segment from the process address space. 193 | * 194 | * @throws posix::error on failure 195 | * @note This operation is idempotent. 196 | */ 197 | void detach(); 198 | 199 | /** 200 | * Marks this segment to be destroyed. The segment will only actually be 201 | * removed after the last process detaches from it. 202 | * 203 | * @throws posix::error on failure 204 | * @note This operation is idempotent. 205 | */ 206 | void remove(); 207 | 208 | /** 209 | * Locks this segment into resident memory. 210 | * 211 | * @throws posix::error on failure 212 | * @note This operation is Linux-specific. 213 | */ 214 | void lock(); 215 | 216 | /** 217 | * Unlocks this segment, allowing it to be swapped out. 218 | * 219 | * @throws posix::error on failure 220 | * @note This operation is Linux-specific. 221 | */ 222 | void unlock(); 223 | 224 | void clear() noexcept; 225 | }; 226 | 227 | //////////////////////////////////////////////////////////////////////////////// 228 | 229 | #endif /* POSIXXX_SYSV_SEGMENT_H */ 230 | -------------------------------------------------------------------------------- /src/posix++/socket.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "socket.h" 8 | 9 | #include "error.h" 10 | 11 | #include /* for std::array */ 12 | #include /* for assert() */ 13 | #include /* for errno */ 14 | #include /* for std::uint8_t */ 15 | #include /* for std::strlen() */ 16 | #include /* for getsockopt(), listen(), recv(), send(), shutdown() */ 17 | 18 | using namespace posix; 19 | 20 | int 21 | socket::domain() const { 22 | int optval = 0; 23 | #ifdef SO_DOMAIN 24 | int optlen = sizeof(optval); 25 | getsockopt(SOL_SOCKET, SO_DOMAIN, &optval, &optlen); 26 | #endif 27 | return optval; 28 | } 29 | 30 | int 31 | socket::type() const { 32 | int optval = 0; 33 | #ifdef SO_TYPE 34 | int optlen = sizeof(optval); 35 | getsockopt(SOL_SOCKET, SO_TYPE, &optval, &optlen); 36 | #endif 37 | return optval; 38 | } 39 | 40 | int 41 | socket::protocol() const { 42 | int optval = 0; 43 | #ifdef SO_PROTOCOL 44 | int optlen = sizeof(optval); 45 | getsockopt(SOL_SOCKET, SO_PROTOCOL, &optval, &optlen); 46 | #endif 47 | return optval; 48 | } 49 | 50 | int 51 | socket::error() { 52 | int optval = 0; 53 | #ifdef SO_ERROR 54 | int optlen = sizeof(optval); 55 | getsockopt(SOL_SOCKET, SO_ERROR, &optval, &optlen); 56 | #endif 57 | return optval; 58 | } 59 | 60 | void 61 | socket::getsockopt(const int level, 62 | const int optname, 63 | void* const optval, 64 | int* const optlen) const { 65 | static_assert(sizeof(socklen_t) == sizeof(int), "sizeof(socklen_t) != sizeof(int)"); 66 | if (::getsockopt(fd(), level, optname, optval, reinterpret_cast(optlen)) == -1) { 67 | throw_error("getsockopt", "%d, %d, %d, %s, %s", fd(), level, optname, "optval", "optlen"); 68 | } 69 | } 70 | 71 | void 72 | socket::listen(const unsigned int backlog) { 73 | if (::listen(fd(), static_cast(backlog)) == -1) { 74 | throw_error("listen", "%d, %d", fd(), static_cast(backlog)); 75 | } 76 | } 77 | 78 | void 79 | socket::send(const std::string& string) { 80 | return send(string.data(), string.size()); 81 | } 82 | 83 | void 84 | socket::send(const char* const data) { 85 | assert(data != nullptr); 86 | 87 | return send(data, std::strlen(data)); 88 | } 89 | 90 | void 91 | socket::send(const void* const data, 92 | const std::size_t size) { 93 | assert(data != nullptr); 94 | 95 | std::size_t pos = 0; 96 | while (pos < size) { 97 | const ssize_t rc = ::send(fd(), reinterpret_cast(data) + pos, size - pos, 0); 98 | if (rc == -1) { 99 | switch (errno) { 100 | case EINTR: /* Interrupted system call */ 101 | continue; 102 | default: 103 | throw_error("send", "%d, %s, %zu, 0x%x", fd(), "chunk", size - pos, 0U); 104 | } 105 | } 106 | pos += rc; 107 | } 108 | } 109 | 110 | std::string 111 | socket::recv_chunk() { 112 | std::string buffer; 113 | recv([&buffer](const void* const chunk_data, const std::size_t chunk_size) -> bool { 114 | buffer.append(reinterpret_cast(chunk_data), chunk_size); 115 | return false; /* all done */ 116 | }); 117 | return buffer; 118 | } 119 | 120 | std::string 121 | socket::recv_string() { 122 | std::string buffer; 123 | recv(buffer); 124 | return buffer; 125 | } 126 | 127 | std::size_t 128 | socket::recv(std::string& buffer) { 129 | return recv([&buffer](const void* const chunk_data, const std::size_t chunk_size) -> bool { 130 | buffer.append(reinterpret_cast(chunk_data), chunk_size); 131 | return true; /* continue indefinitely */ 132 | }); 133 | } 134 | 135 | std::size_t 136 | socket::recv(std::function callback) { 137 | assert(callback != nullptr); 138 | 139 | std::size_t byte_count = 0; 140 | 141 | std::array buffer; 142 | for (;;) { 143 | const ssize_t rc = ::recv(fd(), buffer.data(), buffer.size(), 0); 144 | switch (rc) { 145 | case -1: 146 | switch (errno) { 147 | case EINTR: /* Interrupted system call */ 148 | continue; /* try again */ 149 | default: 150 | throw_error("recv", "%d, %s, %zu, 0x%x", fd(), "chunk", buffer.size(), 0U); 151 | } 152 | 153 | case 0: 154 | goto exit; /* peer has performed an orderly shutdown */ 155 | 156 | default: 157 | assert(rc > 0); 158 | const std::size_t chunk_size = static_cast(rc); 159 | byte_count += chunk_size; 160 | if (!callback(buffer.data(), chunk_size)) { 161 | goto exit; /* caller has requested early exit */ 162 | } 163 | } 164 | } 165 | 166 | exit: 167 | return byte_count; 168 | } 169 | 170 | std::size_t 171 | socket::recv(void* const buffer, 172 | const std::size_t buffer_size, 173 | const int flags) { 174 | assert(buffer != nullptr); 175 | 176 | std::size_t byte_count = 0; 177 | 178 | while (byte_count < buffer_size) { 179 | const ssize_t rc = ::recv(fd(), 180 | reinterpret_cast(buffer) + byte_count, 181 | buffer_size - byte_count, flags); 182 | switch (rc) { 183 | case -1: 184 | switch (errno) { 185 | case EINTR: /* Interrupted system call */ 186 | continue; /* try again */ 187 | default: 188 | throw_error("recv", "%d, %s, %zu, 0x%x", 189 | fd(), "chunk", buffer_size - byte_count, static_cast(flags)); 190 | } 191 | 192 | case 0: 193 | goto exit; /* peer has performed an orderly shutdown */ 194 | 195 | default: 196 | assert(rc > 0); 197 | const std::size_t chunk_size = static_cast(rc); 198 | byte_count += chunk_size; 199 | } 200 | } 201 | 202 | exit: 203 | return byte_count; 204 | } 205 | 206 | void 207 | socket::close_write() { 208 | shutdown(SHUT_WR); 209 | } 210 | 211 | void 212 | socket::close_read() { 213 | shutdown(SHUT_RD); 214 | } 215 | 216 | void 217 | socket::shutdown(const int how) { 218 | if (::shutdown(fd(), how) == -1) { 219 | throw_error("shutdown", "%d, %d", fd(), how); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/posix++/sysv_segment.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "error.h" 8 | #include "sysv_segment.h" 9 | 10 | #include /* for assert() */ 11 | #include /* for errno */ 12 | #include /* for std::sprintf() */ 13 | #include /* for std::memset() */ 14 | #include /* for key_t */ 15 | #include /* for shm*() on BSD */ 16 | #include /* for struct shmid_ds, shm*() */ 17 | 18 | using namespace posix; 19 | 20 | void 21 | sysv_segment::for_each(std::function callback) { 22 | #ifdef __linux__ 23 | struct shm_info shminfo; 24 | const int max_shmidx = shmctl(0, SHM_INFO, reinterpret_cast(&shminfo)); 25 | if (max_shmidx == -1) { 26 | throw_error("shmctl(SHM_INFO)"); 27 | } 28 | 29 | for (int shmidx = 0; shmidx <= max_shmidx; shmidx++) { 30 | shmid_ds ds; 31 | const int shmid = shmctl(shmidx, SHM_STAT, &ds); 32 | if (shmid == -1) { 33 | switch (errno) { 34 | case EINVAL: /* Invalid argument */ 35 | case EACCES: /* Permission denied */ 36 | continue; 37 | default: 38 | assert(errno != EFAULT); 39 | throw_error("shmctl(SHM_STAT)"); 40 | } 41 | } 42 | callback(sysv_segment(shmid)); 43 | } 44 | #else 45 | (void)callback; /* not used */ 46 | throw_error(ENOSYS); /* Function not implemented */ 47 | #endif /* __linux__ */ 48 | } 49 | 50 | sysv_segment 51 | sysv_segment::create_unique(const std::size_t size, 52 | const int flags) { 53 | return create(IPC_PRIVATE, size, IPC_EXCL | flags); 54 | } 55 | 56 | /** 57 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/shmget.html 58 | * @see http://man7.org/linux/man-pages/man2/shmget.2.html 59 | * @see http://www.freebsd.org/cgi/man.cgi?query=shmget&sektion=2 60 | * @see http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/shmget.2.html 61 | */ 62 | sysv_segment 63 | sysv_segment::create(const key_t key, 64 | const std::size_t size, 65 | const int flags) { 66 | int shmid; 67 | if ((shmid = shmget(key, size, IPC_CREAT | flags)) == -1) { 68 | throw_error("shmget"); 69 | } 70 | return sysv_segment{shmid, nullptr, size}; 71 | } 72 | 73 | /** 74 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/shmget.html 75 | * @see http://man7.org/linux/man-pages/man2/shmget.2.html 76 | * @see http://www.freebsd.org/cgi/man.cgi?query=shmget&sektion=2 77 | * @see http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/shmget.2.html 78 | */ 79 | sysv_segment 80 | sysv_segment::open(const key_t key) { 81 | int shmid; 82 | if ((shmid = shmget(key, 0, 0)) == -1) { 83 | assert(errno != ENOSPC); 84 | throw_error("shmget"); 85 | } 86 | return sysv_segment(shmid); 87 | } 88 | 89 | sysv_segment::~sysv_segment() noexcept { 90 | if (_addr) { 91 | if (shmdt(_addr) == -1) { 92 | /* Ignore any errors from shmdt(). */ 93 | } 94 | _addr = nullptr; 95 | } 96 | if (_id != -1) { 97 | _id = -1; 98 | } 99 | } 100 | 101 | std::size_t 102 | sysv_segment::size() const { 103 | return _size ? _size : stat().shm_segsz; 104 | } 105 | 106 | std::size_t 107 | sysv_segment::size() { 108 | return _size ? _size : (_size = stat().shm_segsz); 109 | } 110 | 111 | shmid_ds 112 | sysv_segment::stat() const { 113 | shmid_ds ds; 114 | if (shmctl(_id, IPC_STAT, &ds) == -1) { 115 | assert(errno != EFAULT); 116 | throw_error("shmctl(IPC_STAT)"); 117 | } 118 | return ds; 119 | } 120 | 121 | /** 122 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/shmat.html 123 | * @see http://man7.org/linux/man-pages/man2/shmat.2.html 124 | * @see http://www.freebsd.org/cgi/man.cgi?query=shmat&sektion=2 125 | * @see http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/shmat.2.html 126 | */ 127 | void* 128 | sysv_segment::attach(const int flags) { 129 | if (!_addr) { 130 | if ((_addr = shmat(_id, nullptr, flags)) == reinterpret_cast(-1)) { 131 | throw_error("shmat"); 132 | } 133 | assert(_addr != nullptr); 134 | } 135 | return _addr; 136 | } 137 | 138 | /** 139 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/shmdt.html 140 | * @see http://man7.org/linux/man-pages/man2/shmdt.2.html 141 | * @see http://www.freebsd.org/cgi/man.cgi?query=shmdt&sektion=2 142 | * @see http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/shmdt.2.html 143 | */ 144 | void 145 | sysv_segment::detach() { 146 | if (_addr) { 147 | if (shmdt(_addr) == -1) { 148 | throw_error("shmdt"); 149 | } 150 | _addr = nullptr; 151 | } 152 | } 153 | 154 | /** 155 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/shmctl.html 156 | * @see http://man7.org/linux/man-pages/man2/shmctl.2.html 157 | * @see http://www.freebsd.org/cgi/man.cgi?query=shmctl&sektion=2 158 | * @see http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/shmctl.2.html 159 | */ 160 | void 161 | sysv_segment::remove() { 162 | if (_id != -1) { 163 | if (shmctl(_id, IPC_RMID, nullptr) == -1) { 164 | assert(errno != EFAULT); 165 | if (errno != EINVAL && errno != EIDRM) { 166 | #if 1 167 | char buffer[256]; 168 | std::sprintf(buffer, "shmctl(%d, IPC_RMID, NULL)", _id); 169 | throw_error(buffer); 170 | #else 171 | throw_error("shmctl(IPC_RMID)"); 172 | #endif 173 | } 174 | } 175 | } 176 | } 177 | 178 | void 179 | sysv_segment::lock() { 180 | #ifdef SHM_LOCK 181 | if (shmctl(_id, SHM_LOCK, nullptr) == -1) { 182 | assert(errno != EFAULT); 183 | throw_error("shmctl(SHM_LOCK)"); 184 | } 185 | #else 186 | throw_error(ENOSYS); /* Function not implemented */ 187 | #endif 188 | } 189 | 190 | void 191 | sysv_segment::unlock() { 192 | #ifdef SHM_UNLOCK 193 | if (shmctl(_id, SHM_UNLOCK, nullptr) == -1) { 194 | assert(errno != ENOMEM); 195 | assert(errno != EFAULT); 196 | throw_error("shmctl(SHM_UNLOCK)"); 197 | } 198 | #else 199 | throw_error(ENOSYS); /* Function not implemented */ 200 | #endif 201 | } 202 | 203 | void 204 | sysv_segment::clear() noexcept { 205 | assert(is_attached()); 206 | std::memset(data(), 0, size()); 207 | } 208 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | *********************************** 2 | libposix++: C++14 wrapper for POSIX 3 | *********************************** 4 | 5 | .. image:: https://api.travis-ci.org/dryproject/libposix.svg?branch=master 6 | :target: https://travis-ci.org/dryproject/libposix 7 | :alt: Travis CI build status 8 | 9 | .. image:: https://scan.coverity.com/projects/3229/badge.svg 10 | :target: https://scan.coverity.com/projects/3229 11 | :alt: Coverity Scan build status 12 | 13 | Features 14 | ======== 15 | 16 | * Provides lightweight C++11 abstractions encapsulating core POSIX interfaces. 17 | * Targets Linux 3.x+, FreeBSD 9.x+, and Mac OS X 10.7+ (in that order). 18 | * No runtime dependencies other than the system's C++ standard library. 19 | * No build prerequisites beyond the Autotools toolchain and a C++11 compiler. 20 | * Compatible with Clang and GCC, or any standard C++11 implementation. 21 | * 100% free and unencumbered `public domain `_ software, 22 | usable in any context and for any purpose. 23 | 24 | Design Principles 25 | ================= 26 | 27 | * Provides zero-cost abstractions wrapping file descriptors. 28 | * Ensures that file descriptors are opened with ``O_CLOEXEC``. 29 | * Mitigates various race conditions entailed by the POSIX interfaces. 30 | * Encapsulates ``EINTR`` and ``EAGAIN`` error handling. 31 | * Differentiates logic errors, runtime errors, and fatal errors. 32 | * Avoids the transitive inclusion of POSIX headers where possible. 33 | 34 | Caveats 35 | ======= 36 | 37 | **Caveat utilitor**: the project's top-level ``posix`` namespace may have to 38 | be changed to something else in the future, as the C++ standard now frowns 39 | upon using it: 40 | 41 | C++11 §17.6.4.2.2 [namespace.posix] 42 | 43 | The behavior of a C++ program is undefined if it adds declarations or 44 | definitions to namespace posix or to a namespace within namespace posix 45 | unless otherwise specified. The namespace posix is reserved for use by 46 | ISO/IEC 9945 and other POSIX standards. 47 | 48 | Error Handling 49 | ============== 50 | 51 | This library makes a careful distinction between three different classes of 52 | error conditions, all deriving from the ``posix::error`` base class: 53 | 54 | * **Logic errors**, represented by ``posix::logic_error``. Errors of this 55 | class are thrown due to programming mistakes where the POSIX interfaces 56 | are being used in violation of documented preconditions. A common strategy 57 | for handling this class of error conditions is to abort the program with a 58 | core dump, facilitating introspection to locate and remedy the bug. 59 | * **Fatal errors**, represented by ``posix::fatal_error``. Errors of this 60 | class are thrown due to the exhaustion of critical system resources, such 61 | as available memory (``ENOMEM``) and disk space (``ENOSPC``), or due to 62 | attempts to exceed applicable system resource limits, such as the maximum 63 | number of open file descriptors (``EMFILE`` and ``ENFILE``). A typical 64 | strategy for handling this class of error conditions is to terminate the 65 | program with a descriptive error message. More robust programs and shared 66 | libraries may implement more complicated strategies, such as retrying the 67 | top-level operation after first letting most of the call stack unwind in 68 | order to free up scarce resources. 69 | * **Runtime errors**, represented by ``posix::runtime_error``. Errors of 70 | this class are thrown as a matter of course to indicate various 71 | exceptional conditions such as missing files (``ENOENT``) or insufficient 72 | privileges (``EPERM`` and ``EACCES``). These conditions are generally 73 | recoverable and robust programs will take care to catch and handle them. 74 | 75 | .. note:: 76 | 77 | The distinction between logic errors and runtime errors mirrors that 78 | found in the C++11 standard library, where the ```` header 79 | defines the standard exception base classes ``std::logic_error`` and 80 | ``std::runtime_error``. 81 | 82 | The following example demonstrates error handling with ``libposix++``:: 83 | 84 | try { 85 | // ... code goes here ... 86 | } 87 | catch (const posix::logic_error& error) { 88 | /* Handle programmer errors such as precondition violations: */ 89 | std::abort(); 90 | } 91 | catch (const posix::fatal_error& error) { 92 | /* Handle fatal errors such as system resource exhaustion: */ 93 | std::fprintf(stderr, "Fatal error: %s\n", error.what()); 94 | std::exit(EX_OSERR); 95 | } 96 | catch (const posix::runtime_error& error) { 97 | /* Handle any runtime errors that were expected at this point: */ 98 | switch (error.number()) { 99 | case EPERM: /* Permission denied */ 100 | case EACCES: /* No such file or directory */ 101 | case ENOENT: /* Operation not permitted */ 102 | // ... code goes here ... 103 | break; 104 | default: throw; 105 | } 106 | } 107 | 108 | Build Prerequisites 109 | =================== 110 | 111 | * Clang_ (>= 3.2) or GCC_ (>= 4.8) 112 | * Autoconf_ (>= 2.68) 113 | * Automake_ (>= 1.11) 114 | * Libtool_ (>= 2.2) 115 | 116 | .. note:: 117 | 118 | Older releases may work, but are not actively tested for. 119 | 120 | .. _Clang: http://clang.llvm.org/ 121 | .. _GCC: http://gcc.gnu.org/ 122 | .. _Autoconf: http://www.gnu.org/software/autoconf/ 123 | .. _Automake: http://www.gnu.org/software/automake/ 124 | .. _Libtool: http://www.gnu.org/software/libtool/ 125 | 126 | Installation 127 | ============ 128 | 129 | Installation on Unix 130 | -------------------- 131 | 132 | :: 133 | 134 | $ ./autogen.sh 135 | $ ./configure # on Linux 136 | $ ./configure --with-stdlib=libc++ # on FreeBSD / Mac OS X 137 | $ make 138 | $ sudo make install 139 | $ sudo ldconfig # on Linux 140 | 141 | Elsewhere 142 | ========= 143 | 144 | Author 145 | ====== 146 | 147 | This project is part of the `DRY `_ initiative. 148 | 149 | * `Arto Bendiken `_ - http://ar.to/ 150 | 151 | Donations 152 | ========= 153 | 154 | If you found this software useful and would like to encourage its 155 | maintenance and further development, please consider making a $5 donation 156 | to the author(s) via Gratipay_ or Bitcoin_. 157 | 158 | .. _Gratipay: https://gratipay.com/bendiken/ 159 | .. _Bitcoin: bitcoin:1LevW42qPf44ynzrPrkvQpvuchfxgHvr9w?label=libposix.dryproject.org&message=Donation 160 | 161 | License 162 | ======= 163 | 164 | This is free and unencumbered public domain software. For more information, 165 | see http://unlicense.org/ or the accompanying ``UNLICENSE`` file. 166 | -------------------------------------------------------------------------------- /src/posix++/mapped_file.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "directory.h" 8 | #include "error.h" 9 | #include "mapped_file.h" 10 | #include "pathname.h" 11 | 12 | #include /* for std::max(), std::min() */ 13 | #include /* for assert() */ 14 | #include /* for errno */ 15 | #include /* for std::memmove() */ 16 | #include /* for AT_FDCWD */ 17 | #include /* for _SC_PAGE_SIZE, sysconf() */ 18 | #include /* for std::swap() */ 19 | 20 | #ifdef __linux__ 21 | #include /* for MREMAP_MAYMOVE */ 22 | #endif 23 | 24 | using namespace posix; 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | mapped_file 29 | mapped_file::open(const pathname& pathname, 30 | const int flags, 31 | const mode mode) { 32 | 33 | return mapped_file{AT_FDCWD, pathname.c_str(), flags, mode}; 34 | } 35 | 36 | mapped_file 37 | mapped_file::open(const directory& directory, 38 | const pathname& pathname, 39 | const int flags, 40 | const mode mode) { 41 | 42 | return mapped_file{directory.fd(), pathname.c_str(), flags, mode}; 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | namespace { 48 | static std::size_t system_page_size() { 49 | return static_cast(::sysconf(_SC_PAGE_SIZE)); 50 | } 51 | } 52 | 53 | mapped_file::mapped_file(const int dirfd, 54 | const char* const pathname, 55 | int flags, 56 | const mode mode) 57 | : file{dirfd, pathname, flags, mode}, 58 | _size{file::size()}, 59 | _offset{file::seek(0, SEEK_CUR)}, 60 | _mapping{*this, std::max(_size, system_page_size())} { 61 | 62 | assert(_mapping.data()); 63 | } 64 | 65 | mapped_file::mapped_file(mapped_file&& other) noexcept 66 | : mapped_file{} { 67 | 68 | std::swap(_fd, other._fd); 69 | std::swap(_size, other._size); 70 | std::swap(_offset, other._offset); 71 | std::swap(_mapping, other._mapping); 72 | } 73 | 74 | mapped_file& 75 | mapped_file::operator=(mapped_file&& other) noexcept { 76 | if (this != &other) { 77 | std::swap(_fd, other._fd); 78 | std::swap(_size, other._size); 79 | std::swap(_offset, other._offset); 80 | std::swap(_mapping, other._mapping); 81 | } 82 | return *this; 83 | } 84 | 85 | //////////////////////////////////////////////////////////////////////////////// 86 | 87 | void 88 | mapped_file::sync() { 89 | if (_mapping.writable()) { 90 | file::sync(); 91 | } 92 | 93 | std::size_t new_size = file::size(); 94 | if (new_size != _size) { 95 | _size = new_size; 96 | new_size = std::max(_size, system_page_size()); 97 | #ifdef __linux__ 98 | _mapping.remap(new_size, MREMAP_MAYMOVE); 99 | #else 100 | throw_error(ENOSYS); // TODO: _mapping = ... 101 | #endif 102 | } 103 | } 104 | 105 | std::size_t 106 | mapped_file::seek(const off_t offset, 107 | const int whence) { 108 | 109 | switch (whence) { 110 | case SEEK_CUR: { 111 | _offset += offset; // TODO: error handling 112 | break; 113 | } 114 | case SEEK_SET: { 115 | _offset = file::seek(offset, whence); 116 | break; 117 | } 118 | case SEEK_END: 119 | default: { 120 | _offset = file::seek(offset, whence); 121 | if (_offset > _mapping.size()) { 122 | // TODO: _mapping.extend(); 123 | } 124 | break; 125 | } 126 | } 127 | return _offset; 128 | } 129 | 130 | std::size_t 131 | mapped_file::read_line(std::string& buffer) { 132 | return read_until('\n', buffer); /* read_until() updates _offset */ 133 | } 134 | 135 | std::size_t 136 | mapped_file::read_until(const char separator, 137 | std::string& buffer) { 138 | std::size_t byte_count = 0; 139 | char byte; 140 | while (read(byte)) { /* read() updates _offset */ 141 | byte_count++; 142 | if (byte == separator) 143 | break; /* all done */ 144 | buffer.push_back(byte); 145 | } 146 | return byte_count; 147 | } 148 | 149 | std::size_t 150 | mapped_file::read(char& result) { 151 | if (is_eof()) { 152 | return 0; /* EOF */ 153 | } 154 | result = *_mapping.data(_offset); 155 | _offset++; 156 | return 1; 157 | } 158 | 159 | std::size_t 160 | mapped_file::read(void* const buffer, 161 | const std::size_t buffer_size) { 162 | assert(buffer != nullptr); 163 | if (is_eof()) { 164 | return 0; /* EOF */ 165 | } 166 | const auto length = std::min(buffer_size, _size - _offset); 167 | std::memmove(buffer, _mapping.data(_offset), length); 168 | _offset += length; 169 | return length; 170 | } 171 | 172 | std::string 173 | mapped_file::read() { 174 | std::string buffer; 175 | if (!is_eof()) { 176 | const auto length = _size - _offset; 177 | buffer.append(_mapping.data(_offset), length); 178 | _offset += length; 179 | assert(is_eof()); 180 | } 181 | return buffer; 182 | } 183 | 184 | //////////////////////////////////////////////////////////////////////////////// 185 | 186 | appendable_mapped_file 187 | appendable_mapped_file::open(const pathname& pathname, 188 | const int flags, 189 | const mode mode) { 190 | 191 | return appendable_mapped_file{AT_FDCWD, pathname.c_str(), flags, mode}; 192 | } 193 | 194 | appendable_mapped_file 195 | appendable_mapped_file::open(const directory& directory, 196 | const pathname& pathname, 197 | const int flags, 198 | const mode mode) { 199 | 200 | return appendable_mapped_file{directory.fd(), pathname.c_str(), flags, mode}; 201 | } 202 | 203 | //////////////////////////////////////////////////////////////////////////////// 204 | 205 | appendable_mapped_file::appendable_mapped_file(appendable_mapped_file&& other) noexcept 206 | : appendable_mapped_file{} { 207 | 208 | std::swap(_fd, other._fd); 209 | std::swap(_size, other._size); 210 | std::swap(_offset, other._offset); 211 | std::swap(_mapping, other._mapping); 212 | } 213 | 214 | appendable_mapped_file& 215 | appendable_mapped_file::operator=(appendable_mapped_file&& other) noexcept { 216 | if (this != &other) { 217 | std::swap(_fd, other._fd); 218 | std::swap(_size, other._size); 219 | std::swap(_offset, other._offset); 220 | std::swap(_mapping, other._mapping); 221 | } 222 | return *this; 223 | } 224 | 225 | //////////////////////////////////////////////////////////////////////////////// 226 | 227 | std::size_t 228 | appendable_mapped_file::append(const void* const data, 229 | const std::size_t size) { 230 | 231 | const auto offset = seek(0, SEEK_END); 232 | 233 | write(data, size); 234 | _offset += size; 235 | 236 | return offset; 237 | } 238 | -------------------------------------------------------------------------------- /src/posix++/descriptor.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifndef POSIXXX_DESCRIPTOR_H 4 | #define POSIXXX_DESCRIPTOR_H 5 | 6 | #ifndef __cplusplus 7 | #error " requires a C++ compiler" 8 | #endif 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #include "mode.h" 13 | 14 | #include /* for std::size_t */ 15 | #include /* for std::set */ 16 | #include /* for std::string */ 17 | #include /* for std::swap() */ 18 | 19 | namespace posix { 20 | struct descriptor; 21 | class group; 22 | class user; 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | /** 28 | * Represents a POSIX file descriptor. 29 | * 30 | * @see http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_165 31 | * @see http://en.wikipedia.org/wiki/File_descriptor 32 | */ 33 | struct posix::descriptor { 34 | public: 35 | /** 36 | * Default constructor. The descriptor is invalid after construction. 37 | */ 38 | descriptor() noexcept {} 39 | 40 | /** 41 | * Constructor. 42 | */ 43 | descriptor(const int fd) noexcept { 44 | _fd = fd; 45 | } 46 | 47 | /** 48 | * Copy constructor. 49 | */ 50 | descriptor(const descriptor& other); /* may throw */ 51 | 52 | /** 53 | * Move constructor. 54 | */ 55 | descriptor(descriptor&& other) noexcept 56 | : descriptor{} { 57 | std::swap(_fd, other._fd); 58 | } 59 | 60 | /** 61 | * Copy assignment operator. 62 | */ 63 | descriptor& operator=(descriptor other) noexcept { 64 | std::swap(_fd, other._fd); 65 | return *this; 66 | } 67 | 68 | /** 69 | * Move assignment operator. 70 | */ 71 | descriptor& operator=(descriptor&& other) noexcept { 72 | if (this != &other) { 73 | close(); 74 | } 75 | std::swap(_fd, other._fd); 76 | return *this; 77 | } 78 | 79 | /** 80 | * Destructor. Invokes `close()` if the descriptor is valid. 81 | */ 82 | ~descriptor() noexcept; 83 | 84 | /** 85 | * Assigns a new value to this descriptor. 86 | */ 87 | descriptor& assign(const int fd) noexcept { 88 | close(); 89 | _fd = fd; 90 | return *this; 91 | } 92 | 93 | /** 94 | * Releases the ownership of the native integer descriptor and returns 95 | * it. 96 | * 97 | * @post This descriptor is in an invalid state. 98 | */ 99 | int release() noexcept { 100 | const int fd = _fd; 101 | _fd = -1; 102 | return fd; 103 | } 104 | 105 | /** 106 | * ... 107 | */ 108 | bool operator==(const descriptor& other) const { 109 | return _fd == other._fd; 110 | } 111 | 112 | /** 113 | * ... 114 | */ 115 | bool operator!=(const descriptor& other) const { 116 | return !operator==(other); 117 | } 118 | 119 | /** 120 | * Returns the underlying native integer descriptor. 121 | */ 122 | int operator*() const { 123 | return _fd; 124 | } 125 | 126 | /** 127 | * ... 128 | */ 129 | explicit operator bool() const { 130 | return valid(); 131 | } 132 | 133 | /** 134 | * Returns the underlying native integer descriptor. 135 | */ 136 | inline int fd() const noexcept { 137 | return _fd; 138 | } 139 | 140 | /** 141 | * Checks whether this descriptor is valid. 142 | */ 143 | inline bool valid() const noexcept { 144 | return _fd >= 0; 145 | } 146 | 147 | /** 148 | * Checks whether this descriptor is readable. 149 | */ 150 | bool readable() const; 151 | 152 | /** 153 | * Checks whether this descriptor is writable. 154 | */ 155 | bool writable() const; 156 | 157 | /** 158 | * Returns the file descriptor flags. 159 | */ 160 | int flags() const; 161 | 162 | /** 163 | * Returns the file status flags. 164 | */ 165 | int status() const; 166 | 167 | /** 168 | * Returns the state of the close-on-exec flag. 169 | */ 170 | bool cloexec() const; 171 | 172 | /** 173 | * Sets or clears the close-on-exec flag. 174 | */ 175 | void cloexec(bool state); 176 | 177 | /** 178 | * Duplicates this descriptor. 179 | * 180 | * @note Preserves the state of the close-on-exec flag in the copy. 181 | */ 182 | descriptor dup() const; 183 | 184 | /** 185 | * ... 186 | */ 187 | int fcntl(int cmd) const; 188 | 189 | /** 190 | * ... 191 | */ 192 | int fcntl(int cmd, int arg); 193 | 194 | /** 195 | * ... 196 | */ 197 | int fcntl(int cmd, void* arg); 198 | 199 | /** 200 | * ... 201 | */ 202 | void chown(const user& user, const group& group); 203 | 204 | /** 205 | * ... 206 | */ 207 | void chmod(const mode mode); 208 | 209 | /** 210 | * @see http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html 211 | */ 212 | bool poll(short events, short* revents = nullptr, int timeout = -1); 213 | 214 | /** 215 | * Writes a line to this descriptor. 216 | */ 217 | inline void write_line(const std::string& string) { 218 | write_line(string.c_str()); 219 | } 220 | 221 | /** 222 | * Writes a line to this descriptor. 223 | */ 224 | void write_line(const char* data); 225 | 226 | /** 227 | * Writes a string to this descriptor. 228 | */ 229 | inline void write(const std::string& string) { 230 | write(string.c_str(), string.size()); 231 | } 232 | 233 | /** 234 | * Writes data to this descriptor. 235 | */ 236 | void write(const char* data); 237 | 238 | /** 239 | * Writes a character to this descriptor. 240 | */ 241 | void write(char c); 242 | 243 | /** 244 | * Writes data to this descriptor. 245 | * 246 | * Will either write all the given data, or throw an error. 247 | * 248 | * Retries the operation automatically in case an `EINTR` 249 | * (interrupted system call) error is encountered. 250 | */ 251 | void write(const void* data, std::size_t size); 252 | 253 | /** 254 | * Reads lines of text from this descriptor until EOF. 255 | */ 256 | std::size_t read_lines(std::set& result) const; 257 | 258 | /** 259 | * Reads a line of text from this descriptor. 260 | */ 261 | std::size_t read_line(std::string& buffer) const; 262 | 263 | /** 264 | * Reads data from this descriptor until the given separator character 265 | * is encountered. 266 | */ 267 | std::size_t read_until(char separator, std::string& buffer) const; 268 | 269 | /** 270 | * Reads a character from this descriptor. 271 | */ 272 | std::size_t read(char& result) const; 273 | 274 | /** 275 | * Reads data from this descriptor. 276 | */ 277 | std::size_t read(void* buffer, std::size_t buffer_size) const; 278 | 279 | /** 280 | * Reads a string from this descriptor. 281 | */ 282 | std::string read() const; 283 | 284 | /** 285 | * ... 286 | */ 287 | void sync(); 288 | 289 | /** 290 | * Closes this descriptor. 291 | * 292 | * @note this method is idempotent 293 | */ 294 | void close() noexcept; 295 | 296 | protected: 297 | /** 298 | * The underlying native integer descriptor. 299 | */ 300 | int _fd = -1; 301 | }; 302 | 303 | //////////////////////////////////////////////////////////////////////////////// 304 | 305 | #endif /* POSIXXX_DESCRIPTOR_H */ 306 | -------------------------------------------------------------------------------- /src/posix++/directory.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "directory.h" 8 | #include "error.h" 9 | #include "pathname.h" 10 | 11 | #include /* for assert() */ 12 | #include /* for renameat() */ 13 | #include /* for fdopendir() */ 14 | #include /* for AT_FDCWD, fcntl() */ 15 | #include /* for close(), readlinkat(), unlinkat() */ 16 | #include /* for fstatat() */ 17 | #include /* for DIR */ 18 | 19 | using namespace posix; 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | directory 24 | directory::temporary(const char* const /*basename*/) { 25 | return open("/tmp"); // TODO: improve on this 26 | } 27 | 28 | directory 29 | directory::open(const int dirfd, const char* const pathname) { 30 | assert(dirfd > 0 || dirfd == AT_FDCWD); 31 | assert(pathname != nullptr); 32 | assert(*pathname != '\0'); 33 | 34 | int flags = O_RDONLY; 35 | #ifdef O_CLOEXEC 36 | flags |= O_CLOEXEC; /* POSIX.1-2008 (Linux, FreeBSD) */ 37 | #endif 38 | #ifdef O_DIRECTORY 39 | flags |= O_DIRECTORY; /* Linux-specific */ 40 | #endif 41 | #ifdef O_NOATIME 42 | //flags |= O_NOATIME; /* Linux-specific */ // FIXME 43 | #endif 44 | 45 | int fd; 46 | if ((fd = openat(dirfd, pathname, flags)) == -1) { 47 | throw_error("openat", "%d, \"%s\", 0x%x", 48 | dirfd, pathname, static_cast(flags)); 49 | } 50 | 51 | return directory(fd); 52 | } 53 | 54 | directory 55 | directory::open(const pathname& pathname) { 56 | return open(AT_FDCWD, pathname.c_str()); 57 | } 58 | 59 | directory 60 | directory::open(const char* const pathname) { 61 | return open(AT_FDCWD, pathname); 62 | } 63 | 64 | directory 65 | directory::open(const directory& directory, 66 | const pathname& pathname) { 67 | return open(directory.fd(), pathname.c_str()); 68 | } 69 | 70 | directory 71 | directory::open(const directory& directory, 72 | const char* const pathname) { 73 | return open(directory.fd(), pathname); 74 | } 75 | 76 | //////////////////////////////////////////////////////////////////////////////// 77 | 78 | std::size_t 79 | directory::count(const char* const pathname) const { 80 | assert(pathname != nullptr); 81 | assert(*pathname != '\0'); 82 | 83 | struct stat buffer; 84 | 85 | if (fstatat(fd(), pathname, &buffer, AT_SYMLINK_NOFOLLOW) == -1) { 86 | switch (errno) { 87 | case ENOENT: /* No such file or directory */ 88 | return 0; 89 | default: 90 | throw_error("fstatat", "%d, \"%s\", %s, %s", fd(), pathname, "buffer", "AT_SYMLINK_NOFOLLOW"); 91 | } 92 | } 93 | 94 | return 1; 95 | } 96 | 97 | bool 98 | directory::stat(const char* const pathname, 99 | struct stat& result, 100 | const int flags) const { 101 | assert(pathname != nullptr); 102 | assert(*pathname != '\0'); 103 | 104 | if (fstatat(fd(), pathname, &result, flags) == -1) { 105 | switch (errno) { 106 | case ENOENT: /* No such file or directory */ 107 | return false; 108 | default: 109 | throw_error("fstatat", "%d, \"%s\", %p, 0x%x", fd(), pathname, &result, flags); 110 | } 111 | } 112 | 113 | return true; 114 | } 115 | 116 | void 117 | directory::link(const char* const old_pathname, 118 | const char* const new_pathname) const { 119 | assert(old_pathname != nullptr); 120 | assert(*old_pathname != '\0'); 121 | assert(new_pathname != nullptr); 122 | assert(*new_pathname != '\0'); 123 | 124 | if (linkat(fd(), old_pathname, fd(), new_pathname, 0) == -1) { 125 | throw_error("linkat", "%d, \"%s\", %d, \"%s\", 0x%x", 126 | fd(), old_pathname, fd(), new_pathname, 0U); 127 | } 128 | } 129 | 130 | void 131 | directory::symlink(const char* const old_pathname, 132 | const char* const new_pathname) const { 133 | assert(old_pathname != nullptr); 134 | assert(*old_pathname != '\0'); 135 | assert(new_pathname != nullptr); 136 | assert(*new_pathname != '\0'); 137 | 138 | if (symlinkat(old_pathname, fd(), new_pathname) == -1) { 139 | throw_error("symlinkat", "\"%s\", %d, \"%s\"", 140 | old_pathname, fd(), new_pathname); 141 | } 142 | } 143 | 144 | void 145 | directory::mkdir(const char* const pathname, 146 | const mode mode) const { 147 | assert(pathname != nullptr); 148 | assert(*pathname != '\0'); 149 | 150 | if (mkdirat(fd(), pathname, static_cast(mode)) == -1) { 151 | throw_error("mkdirat", "%d, \"%s\", 0%o", 152 | fd(), pathname, static_cast(mode)); 153 | } 154 | } 155 | 156 | void 157 | directory::rmdir(const char* const pathname) const { 158 | return unlink(pathname, AT_REMOVEDIR); 159 | } 160 | 161 | void 162 | directory::unlink(const char* const pathname) const { 163 | return unlink(pathname, 0); 164 | } 165 | 166 | void 167 | directory::unlink(const char* const pathname, 168 | const int flags) const { 169 | assert(pathname != nullptr); 170 | assert(*pathname != '\0'); 171 | 172 | if (unlinkat(fd(), pathname, flags) == -1) { 173 | throw_error("unlinkat", "%d, \"%s\", 0x%x", 174 | fd(), pathname, static_cast(flags)); 175 | } 176 | } 177 | 178 | void 179 | directory::rename(const char* const old_pathname, 180 | const char* const new_pathname) const { 181 | rename(old_pathname, *this, new_pathname); 182 | } 183 | 184 | void 185 | directory::rename(const char* const old_pathname, 186 | const directory& new_directory, 187 | const char* const new_pathname) const { 188 | assert(old_pathname != nullptr); 189 | assert(*old_pathname != '\0'); 190 | assert(new_pathname != nullptr); 191 | assert(*new_pathname != '\0'); 192 | 193 | if (renameat(fd(), old_pathname, new_directory.fd(), new_pathname) == -1) { 194 | throw_error("renameat", "%d, \"%s\", %d, \"%s\"", 195 | fd(), old_pathname, new_directory.fd(), new_pathname); 196 | } 197 | } 198 | 199 | pathname 200 | directory::readlink(const char* const pathname) const { 201 | char buffer[PATH_MAX]; 202 | 203 | const int rc = readlinkat(fd(), pathname, buffer, sizeof(buffer) - 1); 204 | if (rc == -1) { 205 | throw_error("readlinkat", "%d, \"%s\", %s, %zu", fd(), pathname, "buffer", sizeof(buffer) - 1); 206 | } 207 | 208 | assert(rc < sizeof(buffer)); 209 | buffer[rc] = '\0'; 210 | 211 | return posix::pathname(buffer); 212 | } 213 | 214 | void 215 | directory::for_each(std::function callback) const { 216 | const int dirfd = dup(/*true*/).release(); 217 | 218 | DIR* dir; 219 | if (!(dir = fdopendir(dirfd))) { 220 | throw_error("fdopendir", "%d", dirfd); 221 | } 222 | 223 | entry entry {0, 0, {}}; 224 | 225 | try { 226 | const struct dirent* dirent; 227 | while ((dirent = readdir(dir))) { 228 | #ifdef __linux__ 229 | entry.type = dirent->d_type; 230 | #endif 231 | entry.inode = dirent->d_ino; 232 | entry.name = dirent->d_name; 233 | callback(entry); 234 | } 235 | 236 | closedir(dir); 237 | } 238 | catch (...) { 239 | /* std::bad_alloc from std::string, or else of callback() origin */ 240 | closedir(dir); 241 | throw; 242 | } 243 | } 244 | 245 | directory::iterator 246 | directory::begin() const { 247 | return directory::iterator(*this); 248 | } 249 | 250 | directory::iterator 251 | directory::end() const { 252 | return directory::iterator(); 253 | } 254 | 255 | const directory::iterator 256 | directory::cbegin() const { 257 | return directory::iterator(*this); 258 | } 259 | 260 | const directory::iterator 261 | directory::cend() const { 262 | return directory::iterator(); 263 | } 264 | 265 | directory::iterator::iterator(const directory& dir) { 266 | const int dirfd = dir.dup(/*true*/).release(); 267 | 268 | if (!(_dirp = fdopendir(dirfd))) { 269 | throw_error("fdopendir", "%d", dirfd); 270 | } 271 | } 272 | 273 | directory::iterator::~iterator() noexcept { 274 | if (_dirp) { 275 | if (closedir(reinterpret_cast(_dirp)) == -1) { 276 | /* Ignore any errors from closedir(). */ 277 | } 278 | _dirp = nullptr; 279 | } 280 | } 281 | 282 | bool 283 | directory::iterator::operator!=(const directory::iterator& other) { 284 | return (void)other, true; // TODO 285 | } 286 | 287 | directory::iterator& 288 | directory::iterator::operator++() { 289 | return *this; // TODO 290 | } 291 | 292 | std::string 293 | directory::iterator::operator*() { 294 | return std::string(); // TODO 295 | } 296 | -------------------------------------------------------------------------------- /src/posix++/local_socket.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "local_socket.h" 8 | 9 | #include "error.h" 10 | #include "pathname.h" 11 | 12 | #include /* for std::array */ 13 | #include /* for assert() */ 14 | #include /* for errno */ 15 | #include /* for std::uint8_t */ 16 | #include /* for std::memset(), std::strcpy(), std::strlen() */ 17 | #include /* for F_*, fcntl() */ 18 | #include /* for std::length_error, std::logic_error */ 19 | #include /* for AF_LOCAL, CMSG_*, accept(), connect(), recvmsg(), sendmsg(), socket(), socketpair() */ 20 | #include /* for struct sockaddr_un */ 21 | 22 | using namespace posix; 23 | 24 | local_socket::local_socket() : socket() { 25 | int flags = 0; 26 | #ifdef SOCK_CLOEXEC 27 | /* Nonstandard Linux extension to set O_CLOEXEC atomically: */ 28 | flags |= SOCK_CLOEXEC; 29 | #endif 30 | 31 | int sockfd; 32 | if ((sockfd = ::socket(AF_LOCAL, SOCK_STREAM | flags, 0)) == -1) { 33 | throw_error("socket(AF_LOCAL, SOCK_STREAM)"); 34 | } 35 | 36 | assign(sockfd); 37 | #ifndef SOCK_CLOEXEC 38 | cloexec(true); 39 | #endif 40 | } 41 | 42 | std::pair 43 | local_socket::pair() { 44 | int flags = 0; 45 | #ifdef SOCK_CLOEXEC 46 | /* Nonstandard Linux extension to set O_CLOEXEC atomically: */ 47 | flags |= SOCK_CLOEXEC; 48 | #endif 49 | 50 | int fds[2] = {0, 0}; 51 | if (::socketpair(AF_LOCAL, SOCK_STREAM | flags, 0, fds) == -1) { 52 | throw_error("socketpair(AF_LOCAL, SOCK_STREAM)"); 53 | } 54 | 55 | std::pair pair{local_socket(fds[0]), local_socket(fds[1])}; 56 | #ifndef SOCK_CLOEXEC 57 | pair.first.cloexec(true); 58 | pair.second.cloexec(true); 59 | #endif 60 | 61 | return pair; 62 | } 63 | 64 | local_socket 65 | local_socket::bind(const pathname& pathname) { 66 | assert(!pathname.empty()); 67 | 68 | local_socket socket = local_socket(); 69 | 70 | struct sockaddr_un addr; 71 | addr.sun_family = AF_LOCAL; 72 | 73 | if (pathname.size() >= sizeof(addr.sun_path)) { 74 | throw std::length_error{"socket pathname exceeds maximum length"}; 75 | } 76 | std::strncpy(addr.sun_path, pathname.c_str(), sizeof(addr.sun_path) - 1); 77 | addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; 78 | 79 | const socklen_t addrlen = sizeof(addr.sun_family) + std::strlen(addr.sun_path); 80 | 81 | retry: 82 | if (::bind(socket.fd(), reinterpret_cast(&addr), addrlen) == -1) { 83 | switch (errno) { 84 | case EINTR: /* Interrupted system call */ 85 | goto retry; 86 | default: 87 | throw_error("bind"); 88 | } 89 | } 90 | 91 | return socket; 92 | } 93 | 94 | local_socket 95 | local_socket::connect(const pathname& pathname) { 96 | assert(!pathname.empty()); 97 | 98 | local_socket socket = local_socket(); 99 | 100 | struct sockaddr_un addr; 101 | addr.sun_family = AF_LOCAL; 102 | 103 | if (pathname.size() >= sizeof(addr.sun_path)) { 104 | throw std::length_error{"socket pathname exceeds maximum length"}; 105 | } 106 | std::strncpy(addr.sun_path, pathname.c_str(), sizeof(addr.sun_path) - 1); 107 | addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; 108 | 109 | const socklen_t addrlen = sizeof(addr.sun_family) + std::strlen(addr.sun_path); 110 | 111 | retry: 112 | if (::connect(socket.fd(), reinterpret_cast(&addr), addrlen) == -1) { 113 | switch (errno) { 114 | case EINTR: /* Interrupted system call */ 115 | goto retry; 116 | default: 117 | throw_error("connect"); 118 | } 119 | } 120 | 121 | return socket; 122 | } 123 | 124 | local_socket 125 | local_socket::accept() { 126 | int connfd; 127 | retry: 128 | #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) 129 | /* Nonstandard Linux extension to set O_CLOEXEC atomically: */ 130 | if ((connfd = ::accept4(fd(), nullptr, nullptr, SOCK_CLOEXEC)) == -1) { 131 | #else 132 | if ((connfd = ::accept(fd(), nullptr, nullptr)) == -1) { 133 | #endif 134 | switch (errno) { 135 | case EINTR: /* Interrupted system call */ 136 | goto retry; 137 | default: 138 | throw_error("accept"); 139 | } 140 | } 141 | 142 | local_socket connection(connfd); 143 | #if !(defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)) 144 | connection.cloexec(true); 145 | #endif 146 | 147 | return connection; 148 | } 149 | 150 | void 151 | local_socket::send_descriptor(const descriptor& descriptor) { 152 | #ifdef __linux__ 153 | /* The control buffer must be large enough to hold a single frame of 154 | * cmsghdr ancillary data: */ 155 | std::array cmsg_buffer; 156 | static_assert(sizeof(struct cmsghdr) <= cmsg_buffer.size(), 157 | "sizeof(struct cmsghdr) > cmsg_buffer.size()"); 158 | static_assert(CMSG_SPACE(sizeof(int)) <= cmsg_buffer.size(), 159 | "CMSG_SPACE(sizeof(int)) > cmsg_buffer.size()"); 160 | static_assert(CMSG_LEN(sizeof(int)) <= cmsg_buffer.size(), 161 | "CMSG_LEN(sizeof(int)) > cmsg_buffer.size()"); 162 | 163 | /* Linux and Solaris require there to be at least 1 byte of actual data: */ 164 | std::array data_buffer = {{'\0'}}; 165 | 166 | struct iovec iov; 167 | std::memset(&iov, 0, sizeof(iov)); 168 | iov.iov_base = data_buffer.data(); 169 | iov.iov_len = data_buffer.size(); 170 | 171 | struct msghdr msg; 172 | std::memset(&msg, 0, sizeof(msghdr)); 173 | msg.msg_iov = &iov; 174 | msg.msg_iovlen = 1; 175 | msg.msg_control = cmsg_buffer.data(); 176 | msg.msg_controllen = cmsg_buffer.size(); 177 | 178 | struct cmsghdr* const cmsg = CMSG_FIRSTHDR(&msg); 179 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 180 | cmsg->cmsg_level = SOL_SOCKET; 181 | cmsg->cmsg_type = SCM_RIGHTS; 182 | *reinterpret_cast(CMSG_DATA(cmsg)) = descriptor.fd(); 183 | 184 | retry: 185 | const ssize_t rc = ::sendmsg(fd(), &msg, 0); 186 | if (rc == -1) { 187 | switch (errno) { 188 | case EINTR: /* Interrupted system call */ 189 | goto retry; 190 | default: 191 | throw_error("sendmsg"); 192 | } 193 | } 194 | #else /* __linux__ */ 195 | (void)descriptor; /* not used */ 196 | throw_error(ENOSYS); /* Function not implemented */ 197 | #endif /* __linux__ */ 198 | } 199 | 200 | descriptor 201 | local_socket::recv_descriptor() { 202 | #ifdef __linux__ 203 | descriptor result; 204 | 205 | /* The control buffer must be large enough to hold a single frame of 206 | * cmsghdr ancillary data: */ 207 | std::array cmsg_buffer; 208 | static_assert(sizeof(struct cmsghdr) <= cmsg_buffer.size(), 209 | "sizeof(struct cmsghdr) > cmsg_buffer.size()"); 210 | static_assert(CMSG_SPACE(sizeof(int)) <= cmsg_buffer.size(), 211 | "CMSG_SPACE(sizeof(int)) > cmsg_buffer.size()"); 212 | static_assert(CMSG_LEN(sizeof(int)) <= cmsg_buffer.size(), 213 | "CMSG_LEN(sizeof(int)) > cmsg_buffer.size()"); 214 | 215 | /* Linux and Solaris require there to be at least 1 byte of actual data: */ 216 | std::array data_buffer; 217 | 218 | struct iovec iov; 219 | std::memset(&iov, 0, sizeof(iov)); 220 | iov.iov_base = data_buffer.data(); 221 | iov.iov_len = data_buffer.size(); 222 | 223 | struct msghdr msg; 224 | std::memset(&msg, 0, sizeof(msghdr)); 225 | msg.msg_iov = &iov; 226 | msg.msg_iovlen = 1; 227 | msg.msg_control = cmsg_buffer.data(); 228 | msg.msg_controllen = cmsg_buffer.size(); 229 | 230 | int flags = 0; 231 | #ifdef MSG_CMSG_CLOEXEC 232 | flags |= MSG_CMSG_CLOEXEC; /* Linux only */ 233 | #endif 234 | 235 | retry: 236 | const ssize_t rc = ::recvmsg(fd(), &msg, flags); 237 | if (rc == -1) { 238 | switch (errno) { 239 | case EINTR: /* Interrupted system call */ 240 | goto retry; 241 | default: 242 | throw_error("recvmsg"); 243 | } 244 | } 245 | 246 | const struct cmsghdr* const cmsg = CMSG_FIRSTHDR(&msg); 247 | if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { 248 | if (cmsg->cmsg_level != SOL_SOCKET) { 249 | throw std::logic_error("invalid cmsg_level"); 250 | } 251 | if (cmsg->cmsg_type != SCM_RIGHTS) { 252 | throw std::logic_error("invalid cmsg_type"); 253 | } 254 | result.assign(*reinterpret_cast(CMSG_DATA(cmsg))); 255 | } 256 | 257 | #ifndef MSG_CMSG_CLOEXEC 258 | result.cloexec(true); 259 | #endif 260 | 261 | return result; 262 | #else /* __linux__ */ 263 | throw_error(ENOSYS); /* Function not implemented */ 264 | #endif /* __linux__ */ 265 | } 266 | -------------------------------------------------------------------------------- /src/posix++/descriptor.cc: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | #include 5 | #endif 6 | 7 | #include "descriptor.h" 8 | 9 | #include "error.h" 10 | #include "group.h" 11 | #include "mode.h" 12 | #include "user.h" 13 | 14 | #include /* for std::array */ 15 | #include /* for assert() */ 16 | #include /* for errno */ 17 | #include /* for std::uint8_t */ 18 | #include /* for std::strlen() */ 19 | #include /* for F_*, fcntl() */ 20 | #include /* for struct pollfd, poll() */ 21 | #include /* for std::invalid_argument */ 22 | #include /* for std::string */ 23 | #include /* for fchmod() */ 24 | #include /* for close(), fchown(), fsync(), read(), write() */ 25 | 26 | using namespace posix; 27 | 28 | static const std::string invalid_in_copy_constructor = 29 | "invalid descriptor passed to copy constructor"; 30 | 31 | descriptor::descriptor(const descriptor& other) { 32 | if (other.valid()) { 33 | const bool cloexec_flag = other.cloexec(); 34 | #ifdef F_DUPFD_CLOEXEC 35 | /* POSIX.1-2008 feature to set FD_CLOEXEC atomically: */ 36 | const int fcntl_cmd = cloexec_flag ? F_DUPFD_CLOEXEC : F_DUPFD; 37 | _fd = ::fcntl(other._fd, fcntl_cmd, 0); 38 | #else 39 | _fd = ::fcntl(other._fd, F_DUPFD, 0); 40 | #endif 41 | 42 | if (_fd == -1) { 43 | switch (errno) { 44 | case EBADF: /* Bad file descriptor */ 45 | throw std::invalid_argument(invalid_in_copy_constructor); 46 | default: 47 | throw_error("fcntl", "%d, %s, %d", other._fd, "F_DUPFD", 0); 48 | } 49 | } 50 | 51 | #ifndef F_DUPFD_CLOEXEC 52 | if (cloexec_flag) { 53 | cloexec(true); 54 | } 55 | #endif 56 | } 57 | } 58 | 59 | descriptor::~descriptor() noexcept { 60 | close(); 61 | } 62 | 63 | bool 64 | descriptor::readable() const { 65 | const int status_ = status(); 66 | return status_ & O_RDONLY || status_ & O_RDWR; 67 | } 68 | 69 | bool 70 | descriptor::writable() const { 71 | const int status_ = status(); 72 | return status_ & O_WRONLY || status_ & O_RDWR; 73 | } 74 | 75 | int 76 | descriptor::flags() const { 77 | return fcntl(F_GETFD); 78 | } 79 | 80 | int 81 | descriptor::status() const { 82 | return fcntl(F_GETFL); 83 | } 84 | 85 | bool 86 | descriptor::cloexec() const { 87 | return fcntl(F_GETFD) & FD_CLOEXEC; 88 | } 89 | 90 | void 91 | descriptor::cloexec(const bool state) { 92 | const int flags = fcntl(F_GETFD); 93 | fcntl(F_SETFD, state ? flags | FD_CLOEXEC : flags & ~FD_CLOEXEC); 94 | } 95 | 96 | descriptor 97 | descriptor::dup() const { 98 | return descriptor(*this); /* rely on copy constructor */ 99 | } 100 | 101 | int 102 | descriptor::fcntl(const int cmd) const { 103 | const int result = ::fcntl(_fd, cmd); 104 | if (result == -1) { 105 | throw_error("fcntl", "%d, %d", _fd, cmd); 106 | } 107 | return result; 108 | } 109 | 110 | int 111 | descriptor::fcntl(const int cmd, 112 | const int arg) { 113 | const int result = ::fcntl(_fd, cmd, arg); 114 | if (result == -1) { 115 | throw_error("fcntl", "%d, %d, %d", _fd, cmd, arg); 116 | } 117 | return result; 118 | } 119 | 120 | int 121 | descriptor::fcntl(const int cmd, 122 | void* const arg) { 123 | const int result = ::fcntl(_fd, cmd, arg); 124 | if (result == -1) { 125 | throw_error("fcntl", "%d, %d, %s", _fd, cmd, "arg"); 126 | } 127 | return result; 128 | } 129 | 130 | void 131 | descriptor::chown(const user& user, 132 | const group& group) { 133 | const uid_t uid = static_cast(user.id()); 134 | const gid_t gid = static_cast(group.id()); 135 | 136 | if (fchown(_fd, uid, gid) == -1) { 137 | throw_error("fchown", "%d, %u, %u", _fd, uid, gid); 138 | } 139 | } 140 | 141 | void 142 | descriptor::chmod(const mode mode) { 143 | if (fchmod(_fd, static_cast(mode)) == -1) { 144 | throw_error("fchmod", "%d, 0%o", _fd, static_cast(mode)); 145 | } 146 | } 147 | 148 | bool 149 | descriptor::poll(const short events, 150 | short* const revents, 151 | const int timeout) { 152 | struct pollfd fds = {fd(), events, 0}; 153 | retry: 154 | const int rc = ::poll(&fds, 1, timeout); 155 | if (rc == -1) { 156 | switch (errno) { 157 | case EINTR: /* Interrupted system call */ 158 | goto retry; /* try again */ 159 | default: 160 | assert(errno != EFAULT); 161 | throw_error("poll", "{%d}, %d, %d", fds.fd, 1, timeout); 162 | } 163 | } 164 | if (revents) *revents = fds.revents; 165 | return rc > 0; 166 | } 167 | 168 | void 169 | descriptor::write_line(const char* const data) { 170 | assert(data != nullptr); 171 | 172 | return write(data), write('\n'); 173 | } 174 | 175 | void 176 | descriptor::write(const char* const data) { 177 | assert(data != nullptr); 178 | 179 | return write(data, std::strlen(data)); 180 | } 181 | 182 | void 183 | descriptor::write(const char c) { 184 | char data[2] = {c, '\0'}; 185 | return write(data, 1); 186 | } 187 | 188 | void 189 | descriptor::write(const void* const data, 190 | const std::size_t size) { 191 | assert(data != nullptr); 192 | 193 | std::size_t pos = 0; 194 | while (pos < size) { 195 | const ssize_t rc = ::write(fd(), reinterpret_cast(data) + pos, size - pos); 196 | if (rc == -1) { 197 | switch (errno) { 198 | case EINTR: /* Interrupted system call */ 199 | continue; 200 | default: 201 | throw_error("write", "%d, %s, %zu", fd(), "chunk", size - pos); 202 | } 203 | } 204 | pos += rc; 205 | } 206 | } 207 | 208 | std::size_t 209 | descriptor::read_lines(std::set& result) const { 210 | std::size_t total_byte_count = 0, byte_count = 0; 211 | std::string line; 212 | while ((byte_count = read_line(line))) { 213 | total_byte_count += byte_count; 214 | result.insert(line); 215 | line.clear(); 216 | } 217 | return total_byte_count; 218 | } 219 | 220 | std::size_t 221 | descriptor::read_line(std::string& buffer) const { 222 | return read_until('\n', buffer); 223 | } 224 | 225 | std::size_t 226 | descriptor::read_until(const char separator, 227 | std::string& buffer) const { 228 | std::size_t byte_count = 0; 229 | 230 | char character; 231 | while (read(character)) { 232 | byte_count++; 233 | 234 | if (character == separator) 235 | break; /* all done */ 236 | 237 | buffer.push_back(character); 238 | } 239 | 240 | return byte_count; 241 | } 242 | 243 | std::size_t 244 | descriptor::read(char& result) const { 245 | retry: 246 | const ssize_t rc = ::read(fd(), &result, sizeof(result)); 247 | switch (rc) { 248 | case -1: 249 | switch (errno) { 250 | case EINTR: /* Interrupted system call */ 251 | goto retry; /* try again */ 252 | default: 253 | assert(errno != EFAULT); 254 | throw_error("read", "%d, %s, %zu", fd(), "result", sizeof(result)); 255 | } 256 | 257 | case 0: 258 | return rc; /* EOF */ 259 | 260 | default: 261 | assert(rc == 1); 262 | return rc; 263 | } 264 | } 265 | 266 | std::size_t 267 | descriptor::read(void* const buffer, 268 | const std::size_t buffer_size) const { 269 | assert(buffer != nullptr); 270 | 271 | std::size_t byte_count = 0; 272 | 273 | while (byte_count < buffer_size) { 274 | const ssize_t rc = ::read(fd(), 275 | reinterpret_cast(buffer) + byte_count, 276 | buffer_size - byte_count); 277 | switch (rc) { 278 | case -1: 279 | switch (errno) { 280 | case EINTR: /* Interrupted system call */ 281 | continue; /* try again */ 282 | default: 283 | throw_error("read", "%d, %s, %zu", fd(), "chunk", buffer_size - byte_count); 284 | } 285 | 286 | case 0: 287 | goto exit; /* end of file */ 288 | 289 | default: 290 | assert(rc > 0); 291 | const std::size_t chunk_size = static_cast(rc); 292 | byte_count += chunk_size; 293 | } 294 | } 295 | 296 | exit: 297 | return byte_count; 298 | } 299 | 300 | std::string 301 | descriptor::read() const { 302 | std::string result; 303 | std::array buffer; 304 | 305 | for (;;) { 306 | const ssize_t rc = ::read(_fd, buffer.data(), buffer.size()); 307 | switch (rc) { 308 | case -1: 309 | switch (errno) { 310 | case EINTR: /* Interrupted system call */ 311 | continue; /* try again */ 312 | default: 313 | assert(errno != EFAULT); 314 | throw_error("read", "%d, %s, %zu", _fd, "buffer", buffer.size()); 315 | } 316 | 317 | case 0: 318 | return result; /* all done, EOF reached */ 319 | 320 | default: 321 | assert(rc > 0); 322 | result.append(buffer.data(), static_cast(rc)); 323 | } 324 | } 325 | } 326 | 327 | void 328 | descriptor::sync() { 329 | if (fsync(_fd) == -1) { 330 | throw_error("fsync", "%d", _fd); 331 | } 332 | } 333 | 334 | void 335 | descriptor::close() noexcept { 336 | if (valid()) { 337 | if (::close(_fd) == -1) { 338 | /* Ignore any errors from close(). */ 339 | } 340 | _fd = -1; 341 | } 342 | } 343 | --------------------------------------------------------------------------------