#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include 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(getAllocator(), buf, size, flags, mlibc::file_dispose_cb); } int pclose(FILE *stream) { MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1); auto file = static_cast(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(args), environ); _Exit(127); } else { // For the parent mlibc::sys_close(fds[child_end]); ret = frg::construct( getAllocator(), fds[parent_end], mlibc::file_dispose_cb ); __ensure(ret); auto file = static_cast(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(getAllocator(), buf, sizeloc, O_RDWR, mlibc::file_dispose_cb); } int fseeko(FILE *file_base, off_t offset, int whence) { auto file = static_cast(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(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(getAllocator(), cookie, flags, funcs, mlibc::file_dispose_cb); }