diff options
Diffstat (limited to 'lib/mlibc/options/posix/generic/posix_stdio.cpp')
-rw-r--r-- | lib/mlibc/options/posix/generic/posix_stdio.cpp | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/posix_stdio.cpp b/lib/mlibc/options/posix/generic/posix_stdio.cpp new file mode 100644 index 0000000..fc77a54 --- /dev/null +++ b/lib/mlibc/options/posix/generic/posix_stdio.cpp @@ -0,0 +1,209 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include <bits/ensure.h> +#include <mlibc/ansi-sysdeps.hpp> +#include <mlibc/debug.hpp> +#include <mlibc/file-io.hpp> +#include <mlibc/posix-file-io.hpp> +#include <mlibc/posix-sysdeps.hpp> + +struct popen_file : mlibc::fd_file { + popen_file(int fd, void (*do_dispose)(abstract_file *) = nullptr) + : fd_file(fd, do_dispose) {} + + pid_t get_popen_pid() { + return _popen_pid; + } + + void set_popen_pid(pid_t new_pid) { + _popen_pid = new_pid; + } + +private: + // Underlying PID in case of popen() + pid_t _popen_pid; +}; + +FILE *fmemopen(void *buf, size_t size, const char *__restrict mode) { + int flags = mlibc::fd_file::parse_modestring(mode); + + return frg::construct<mlibc::fmemopen_mem_file>(getAllocator(), buf, size, flags, + mlibc::file_dispose_cb<mlibc::mem_file>); +} + +int pclose(FILE *stream) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1); + + auto file = static_cast<popen_file *>(stream); + + int status; + pid_t pid = file->get_popen_pid(); + + fclose(file); + + if (mlibc::sys_waitpid(pid, &status, 0, NULL, &pid) != 0) { + errno = ECHILD; + return -1; + } + + return status; +} + +FILE *popen(const char *command, const char *typestr) { + bool is_write; + pid_t child; + FILE *ret = nullptr; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork && mlibc::sys_dup2 && mlibc::sys_execve && + mlibc::sys_sigprocmask && mlibc::sys_sigaction && mlibc::sys_pipe, nullptr); + + if (typestr == NULL) { + errno = EINVAL; + return nullptr; + } + + if (strstr(typestr, "w") != NULL) { + is_write = true; + } else if (strstr(typestr, "r") != NULL) { + is_write = false; + } else { + errno = EINVAL; + return nullptr; + } + + bool cloexec = false; + if (strstr(typestr, "e") != NULL) { + // Set FD_CLOEXEC on the new file descriptor + cloexec = true; + } + + int fds[2]; + if (int e = mlibc::sys_pipe(fds, 0)) { + errno = e; + return nullptr; + } + + struct sigaction new_sa, old_int, old_quit; + sigset_t new_mask, old_mask; + + new_sa.sa_handler = SIG_IGN; + new_sa.sa_flags = 0; + sigemptyset(&new_sa.sa_mask); + mlibc::sys_sigaction(SIGINT, &new_sa, &old_int); + mlibc::sys_sigaction(SIGQUIT, &new_sa, &old_quit); + + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGCHLD); + mlibc::sys_sigprocmask(SIG_BLOCK, &new_mask, &old_mask); + + int parent_end = is_write ? 1 : 0; + int child_end = is_write ? 0 : 1; + + if (int e = mlibc::sys_fork(&child)) { + errno = e; + mlibc::sys_close(fds[0]); + mlibc::sys_close(fds[1]); + } else if (!child) { + // For the child + mlibc::sys_sigaction(SIGINT, &old_int, nullptr); + mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr); + mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr); + + mlibc::sys_close(fds[parent_end]); + + if (mlibc::sys_dup2(fds[child_end], 0, is_write ? 0 : 1)) { + __ensure(!"sys_dup2() failed in popen()"); + } + mlibc::sys_close(fds[child_end]); + + const char *args[] = { + "sh", "-c", command, nullptr + }; + + mlibc::sys_execve("/bin/sh", const_cast<char **>(args), environ); + _Exit(127); + } else { + // For the parent + mlibc::sys_close(fds[child_end]); + + ret = frg::construct<popen_file>( + getAllocator(), + fds[parent_end], + mlibc::file_dispose_cb<popen_file> + ); + __ensure(ret); + + auto file = static_cast<popen_file *>(ret); + + file->set_popen_pid(child); + + if (cloexec == true) { + fcntl(file->fd(), F_SETFD, O_CLOEXEC); + } + } + + mlibc::sys_sigaction(SIGINT, &old_int, nullptr); + mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr); + mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr); + + return ret; +} + +FILE *open_memstream(char **buf, size_t *sizeloc) { + return frg::construct<mlibc::memstream_mem_file>(getAllocator(), buf, sizeloc, O_RDWR, + mlibc::file_dispose_cb<mlibc::mem_file>); +} + +int fseeko(FILE *file_base, off_t offset, int whence) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + if(int e = file->seek(offset, whence); e) { + errno = e; + return -1; + } + return 0; +} + +off_t ftello(FILE *file_base) { + auto file = static_cast<mlibc::abstract_file *>(file_base); + off_t current_offset; + if(int e = file->tell(¤t_offset); e) { + errno = e; + return -1; + } + return current_offset; +} + +int dprintf(int fd, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vdprintf(fd, format, args); + va_end(args); + return result; +} + +int vdprintf(int fd, const char *format, __builtin_va_list args) { + mlibc::fd_file file{fd}; + int ret = vfprintf(&file, format, args); + file.flush(); + return ret; +} + +char *fgetln(FILE *, size_t *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +FILE *fopencookie(void *cookie, const char *__restrict mode, cookie_io_functions_t funcs) { + int flags = mlibc::fd_file::parse_modestring(mode); + + return frg::construct<mlibc::cookie_file>(getAllocator(), cookie, flags, funcs, + mlibc::file_dispose_cb<mlibc::cookie_file>); +} |