#ifndef CRUCIBLE_FD_H #define CRUCIBLE_FD_H #include "crucible/namedptr.h" #include #include #include // open #include #include #include // ioctl #include #include // socket #include // pread/pwrite #include namespace crucible { using namespace std; // IOHandle is a file descriptor owner object. It closes them when destroyed. // Most of the functions here don't use it because these functions don't own FDs. // All good names for such objects are taken. class IOHandle { IOHandle(const IOHandle &) = delete; IOHandle(IOHandle &&) = delete; IOHandle& operator=(IOHandle &&) = delete; IOHandle& operator=(const IOHandle &) = delete; int m_fd; void close(); public: virtual ~IOHandle(); IOHandle(int fd = -1); int get_fd() const; }; class Fd { static NamedPtr s_named_ptr; shared_ptr m_handle; public: using resource_type = IOHandle; Fd(); Fd(int fd); Fd &operator=(int fd); Fd &operator=(const shared_ptr &); operator int() const; bool operator!() const; shared_ptr operator->() const; }; void set_relative_path(string path); string relative_path(); // Functions named "foo_or_die" throw exceptions on failure. // Attempt to open the file with the given mode int open_or_die(const string &file, int flags = O_RDONLY, mode_t mode = 0777); int openat_or_die(int dir_fd, const string &file, int flags = O_RDONLY, mode_t mode = 0777); // Decode open parameters string o_flags_ntoa(int flags); string o_mode_ntoa(mode_t mode); // mmap with its one weird error case void *mmap_or_die(void *addr, size_t length, int prot, int flags, int fd, off_t offset); // Decode mmap parameters string mmap_prot_ntoa(int prot); string mmap_flags_ntoa(int flags); // Unlink, rename void rename_or_die(const string &from, const string &to); void renameat_or_die(int fromfd, const string &frompath, int tofd, const string &topath); void ftruncate_or_die(int fd, off_t size); // Read or write structs: // There is a template specialization to read or write strings // Three-arg version of read_or_die/write_or_die throws an error on incomplete read/writes // Four-arg version returns number of bytes read/written through reference arg void read_or_die(int fd, void *buf, size_t size); template void read_or_die(int fd, T& buf) { return read_or_die(fd, static_cast(&buf), sizeof(buf)); } void read_partial_or_die(int fd, void *buf, size_t size_wanted, size_t &size_read); template void read_partial_or_die(int fd, T& buf, size_t &size_read) { return read_partial_or_die(fd, static_cast(&buf), sizeof(buf), size_read); } void pread_or_die(int fd, void *buf, size_t size, off_t offset); template void pread_or_die(int fd, T& buf, off_t offset) { return pread_or_die(fd, static_cast(&buf), sizeof(buf), offset); } void write_or_die(int fd, const void *buf, size_t size); template void write_or_die(int fd, const T& buf) { return write_or_die(fd, static_cast(&buf), sizeof(buf)); } void write_partial_or_die(int fd, const void *buf, size_t size_wanted, size_t &size_written); template void write_partial_or_die(int fd, const T& buf, size_t &size_written) { return write_partial_or_die(fd, static_cast(&buf), sizeof(buf), size_written); } void pwrite_or_die(int fd, const void *buf, size_t size, off_t offset); template void pwrite_or_die(int fd, const T& buf, off_t offset) { return pwrite_or_die(fd, static_cast(&buf), sizeof(buf), offset); } // Specialization for strings which reads/writes the string content, not the struct string template<> void write_or_die(int fd, const string& str); template<> void pread_or_die(int fd, string& str, off_t offset); template<> void pread_or_die>(int fd, vector& str, off_t offset); template<> void pread_or_die>(int fd, vector& str, off_t offset); template<> void pwrite_or_die(int fd, const string& str, off_t offset); template<> void pwrite_or_die>(int fd, const vector& str, off_t offset); template<> void pwrite_or_die>(int fd, const vector& str, off_t offset); // A different approach to reading a simple string string read_string(int fd, size_t size); // A lot of Unix API wants you to initialize a struct and call // one function to fill it, another function to throw it away, // and has some unknown third thing you have to do when there's // an error. That's also a C++ object with an exception-throwing // constructor. struct Stat : public stat { Stat(); Stat(int f); Stat(const string &filename); Stat &fstat(int fd); Stat &lstat(const string &filename); }; int ioctl_iflags_get(int fd); void ioctl_iflags_set(int fd, int attr); string st_mode_ntoa(mode_t mode); // Because it's not trivial to do correctly string readlink_or_die(const string &path); // Determine the name of a FD by readlink through /proc/self/fd/ string name_fd(int fd); // Returns Fd objects because it does own them. pair socketpair_or_die(int domain = AF_UNIX, int type = SOCK_STREAM, int protocol = 0); // like unique_lock but for flock instead of mutexes...and not trying // to hide the many and subtle differences between those two things *at all*. class Flock { int m_fd; bool m_locked; Flock(const Flock &) = delete; Flock(Flock &&) = delete; Flock &operator=(const Flock &) = delete; Flock &operator=(Flock &&) = delete; public: Flock(); Flock(int fd); Flock(int fd, bool init_locked_state); ~Flock(); void lock(); void try_lock(); void unlock(); bool owns_lock(); operator bool(); int fd(); }; // Doesn't use Fd objects because it's usually just used to replace stdin/stdout/stderr. void dup2_or_die(int fd_in, int fd_out); } #endif // CRUCIBLE_FD_H