diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 03254e9c..87ea9620 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -23,3 +23,4 @@ luna_app(stat.cpp stat) luna_app(uname.cpp uname) luna_app(base64.cpp base64) luna_app(login.cpp login) +luna_app(ipc-test.cpp ipc-test) diff --git a/apps/ipc-test.cpp b/apps/ipc-test.cpp new file mode 100644 index 00000000..41311e44 --- /dev/null +++ b/apps/ipc-test.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +int main() +{ + int pfds[2]; + + if (pipe(pfds) < 0) + { + perror("pipe"); + return 1; + } + + pid_t child = fork(); + if (child == 0) + { + close(pfds[1]); + + char buffer[4096]; + size_t nread = read(pfds[0], buffer, sizeof(buffer) - 1); + buffer[nread] = 0; + close(pfds[0]); + + puts(buffer); + + return 0; + } + else if (child == -1) + { + perror("fork"); + return 1; + } + + close(pfds[0]); + + const char* string = "Hello from a child process who just received this message from its parent!"; + write(pfds[1], string, strlen(string)); + close(pfds[1]); + + wait(NULL); + + return 0; +} diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index fb56e7ef..622aa392 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -39,6 +39,7 @@ set(SOURCES src/sys/link.cpp src/sys/uname.cpp src/fs/VFS.cpp + src/fs/Pipe.cpp src/fs/tmpfs/FileSystem.cpp src/fs/devices/DeviceRegistry.cpp src/fs/devices/NullDevice.cpp diff --git a/kernel/src/fs/Pipe.cpp b/kernel/src/fs/Pipe.cpp new file mode 100644 index 00000000..5b232b7b --- /dev/null +++ b/kernel/src/fs/Pipe.cpp @@ -0,0 +1,62 @@ +#include "fs/Pipe.h" +#include "thread/Scheduler.h" + +Result Pipe::create(SharedPtr& rpipe, SharedPtr& wpipe) +{ + auto pipe = TRY(make_shared()); + + auto writer = TRY(make_shared()); + auto reader = TRY(make_shared()); + + auto auth = Scheduler::current()->auth; + + pipe->m_writer = writer.ptr(); + pipe->m_reader = reader.ptr(); + + writer->m_pipe = reader->m_pipe = pipe; + writer->chown(auth.euid, auth.egid); + reader->chown(auth.euid, auth.egid); + + rpipe = reader; + wpipe = writer; + + return {}; +} + +Result Pipe::read(u8* buf, usize, usize length) +{ + if (length > m_data_buffer.size()) length = m_data_buffer.size(); + + memcpy(buf, m_data_buffer.data(), length); + + memmove(m_data_buffer.data(), m_data_buffer.data() + length, m_data_buffer.size() - length); + + TRY(m_data_buffer.try_resize(m_data_buffer.size() - length)); + + return length; +} + +Result Pipe::write(const u8* buf, usize, usize length) +{ + if (!m_reader) return length; + + u8* slice = TRY(m_data_buffer.slice_at_end(length)); + memcpy(slice, buf, length); + + return length; +} + +bool Pipe::blocking() const +{ + return !m_data_buffer.size() && m_writer; +} + +PipeWriter::~PipeWriter() +{ + m_pipe->m_writer = nullptr; +} + +PipeReader::~PipeReader() +{ + m_pipe->m_reader = nullptr; +} diff --git a/kernel/src/fs/Pipe.h b/kernel/src/fs/Pipe.h new file mode 100644 index 00000000..441f59d1 --- /dev/null +++ b/kernel/src/fs/Pipe.h @@ -0,0 +1,134 @@ +#pragma once +#include "fs/VFS.h" +#include + +class PipeInodeBase; +class PipeReader; +class PipeWriter; + +class Pipe +{ + public: + static Result create(SharedPtr& rpipe, SharedPtr& wpipe); + + Result read(u8* buf, usize, usize length); + + Result write(const u8* buf, usize, usize length); + + bool blocking() const; + + private: + Buffer m_data_buffer; + PipeWriter* m_writer; + PipeReader* m_reader; + + friend class PipeInodeBase; + friend class PipeReader; + friend class PipeWriter; +}; + +class PipeInodeBase : public VFS::FileInode +{ + public: + Result truncate(usize) override + { + return err(ENOTSUP); + } + + bool blocking() const override + { + return m_pipe->blocking(); + } + + usize size() const override + { + return m_pipe->m_data_buffer.size(); + } + + void did_link() override + { + } + + void did_unlink() override + { + } + + Result chmod(mode_t) override + { + return err(ENOTSUP); + } + + Result chown(u32 uid, u32 gid) override + { + m_uid = uid; + m_gid = gid; + return {}; + } + + usize inode_number() const override + { + return 0; + } + + VFS::FileSystem* fs() const override + { + return nullptr; + } + + virtual ~PipeInodeBase() = default; + + friend class Pipe; + + protected: + SharedPtr m_pipe; + u32 m_uid { 0 }; + u32 m_gid { 0 }; +}; + +class PipeWriter : public PipeInodeBase +{ + public: + Result read(u8*, usize, usize) const override + { + // A FileDescriptor pointing to a PipeWriter should never be opened for reading. + check(false); + } + + Result write(const u8* buf, usize offset, usize length) override + { + return m_pipe->write(buf, offset, length); + } + + mode_t mode() const override + { + return 0200; + } + + ~PipeWriter(); + + friend class Pipe; +}; + +class PipeReader : public PipeInodeBase +{ + public: + Result read(u8* buf, usize offset, usize length) const override + { + return m_pipe->read(buf, offset, length); + } + + Result write(const u8*, usize, usize) override + { + // A FileDescriptor pointing to a PipeReader should never be opened for writing. + check(false); + } + + mode_t mode() const override + { + return 0400; + } + + ~PipeReader(); + + friend class Pipe; +}; diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index 9e550a99..2e881d99 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -81,7 +81,7 @@ namespace VFS virtual Result chown(u32 uid, u32 gid) = 0; // Generic VFS-related methods - virtual FileSystem& fs() const = 0; + virtual FileSystem* fs() const = 0; virtual ~Inode() = default; diff --git a/kernel/src/fs/tmpfs/FileSystem.h b/kernel/src/fs/tmpfs/FileSystem.h index cd3220a7..0cf0808b 100644 --- a/kernel/src/fs/tmpfs/FileSystem.h +++ b/kernel/src/fs/tmpfs/FileSystem.h @@ -50,9 +50,9 @@ namespace TmpFS m_inode_number = inum; } - VFS::FileSystem& fs() const override + VFS::FileSystem* fs() const override { - return *m_fs; + return m_fs; } usize inode_number() const override @@ -138,9 +138,9 @@ namespace TmpFS m_device = device; } - VFS::FileSystem& fs() const override + VFS::FileSystem* fs() const override { - return *m_fs; + return m_fs; } usize inode_number() const override @@ -305,9 +305,9 @@ namespace TmpFS return {}; } - VFS::FileSystem& fs() const override + VFS::FileSystem* fs() const override { - return *m_fs; + return m_fs; } usize inode_number() const override diff --git a/kernel/src/sys/file.cpp b/kernel/src/sys/file.cpp index fbace6c9..20749ead 100644 --- a/kernel/src/sys/file.cpp +++ b/kernel/src/sys/file.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "fs/Pipe.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -166,3 +167,26 @@ Result sys_dup2(Registers*, SyscallArgs args) return (u64)newfd; } + +Result sys_pipe(Registers*, SyscallArgs args) +{ + int* pfds = (int*)args[0]; + + Thread* current = Scheduler::current(); + + int rfd = TRY(current->allocate_fd(0)); + int wfd = TRY(current->allocate_fd(rfd + 1)); + + if (!MemoryManager::copy_to_user_typed(pfds, &rfd)) return err(EFAULT); + if (!MemoryManager::copy_to_user_typed(pfds + 1, &wfd)) return err(EFAULT); + + SharedPtr rpipe; + SharedPtr wpipe; + + TRY(Pipe::create(rpipe, wpipe)); + + current->fd_table[rfd] = FileDescriptor { rpipe, 0, O_RDONLY }; + current->fd_table[wfd] = FileDescriptor { wpipe, 0, O_WRONLY }; + + return 0; +} diff --git a/kernel/src/sys/mknod.cpp b/kernel/src/sys/mknod.cpp index 3240efdf..20f9a464 100644 --- a/kernel/src/sys/mknod.cpp +++ b/kernel/src/sys/mknod.cpp @@ -27,7 +27,7 @@ Result sys_mknod(Registers*, SyscallArgs args) auto parent = TRY(VFS::resolve_path(dirname.chars(), current->auth, current->current_directory)); if (!VFS::can_write(parent, current->auth)) return err(EACCES); - auto inode = TRY(parent->fs().create_device_inode(maj, min)); + auto inode = TRY(parent->fs()->create_device_inode(maj, min)); TRY(parent->add_entry(inode, basename.chars())); TRY(inode->chmod(mode)); diff --git a/kernel/src/sys/open.cpp b/kernel/src/sys/open.cpp index 930915e7..3a57f3ea 100644 --- a/kernel/src/sys/open.cpp +++ b/kernel/src/sys/open.cpp @@ -59,7 +59,7 @@ Result sys_openat(Registers*, SyscallArgs args) if (flags & O_TMPFILE) { if (inode->type() != VFS::InodeType::Directory) return err(EINVAL); - inode = TRY(inode->fs().create_file_inode()); + inode = TRY(inode->fs()->create_file_inode()); inode->chmod(mode); inode->chown(current->auth.euid, current->auth.egid); } diff --git a/libc/include/unistd.h b/libc/include/unistd.h index cfae95bd..904f7e61 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -127,6 +127,9 @@ extern "C" /* Set the current network hostname. */ int sethostname(const char* buf, size_t len); + /* Create a pipe for inter-process communication. */ + int pipe(int pfds[2]); + #ifdef __cplusplus } #endif diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index e6764b3a..ca9e008e 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -399,4 +399,10 @@ extern "C" long rc = syscall(SYS_sethostname, buf, len); __errno_return(rc, int); } + + int pipe(int pfds[2]) + { + long rc = syscall(SYS_pipe, (int*)pfds); + __errno_return(rc, int); + } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 1075d955..5210cb83 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -4,7 +4,7 @@ _e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(openat) _e(close) _e(read) _e(getpid) _e(write) \ _e(lseek) _e(mkdir) _e(execve) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(getuid) \ _e(geteuid) _e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(chmod) _e(chown) \ - _e(ioctl) _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) + _e(ioctl) _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) enum Syscalls {