// // Created by imper on 1/17/22. // #ifndef EXECUTE_PROCESS_LINUX_EXECUTE_PROCESS_LINUX_ #define EXECUTE_PROCESS_LINUX_EXECUTE_PROCESS_LINUX_ #include #include #include #include #include #define EXECUTION_FAILED INT32_MIN namespace exec { namespace pipe { enum : int { read = 0, write, size }; } /// file descriptor redirection information class class fd_redirection { public: inline fd_redirection(int source, int destination) : source(source), destination(destination) { } [[nodiscard]] inline int get_source() const { return source; } [[nodiscard]] inline int get_destination() const { return destination; } private: friend inline void process_redirection(std::shared_ptr redirection); /// \brief file descriptor which will be redirected to \b destination int source; /// \brief file descriptor to which will be redirected \b source int destination; }; /// linux file descriptor wrapper class class linux_file_descriptor { public: inline explicit linux_file_descriptor(int fd) : fd(fd) { } /// \brief Converts file descriptor to string /// \return file descriptor designation string [[nodiscard]] inline std::string& to_string() const { auto& res = *new std::string; switch (fd) { case 0: res = ""; break; case 1: res = ""; break; case 2: res = ""; break; default: res = "<" + std::to_string(fd) + ">"; } return res; } /// \brief Creates redirection /// \param destfd Destination file descriptor /// \return Redirection from \b this.fd to \b destfd.fd [[nodiscard]] inline std::shared_ptr redirect_to(const linux_file_descriptor& destfd) const { return redirect_to(destfd.fd); } /// \brief Creates redirection /// \param destfd Destination file descriptor /// \return Redirection from \b this.fd to \b destfd [[nodiscard]] inline std::shared_ptr redirect_to(int destfd) const { int this_mode = ::fcntl(this->fd, F_GETFL); int destfd_mode = ::fcntl(destfd, F_GETFL); switch (this_mode & O_ACCMODE) { case O_RDONLY: switch (destfd_mode & O_ACCMODE) { case O_RDONLY: case O_RDWR: return std::make_shared(destfd, this->fd); } break; case O_WRONLY: switch (destfd_mode & O_ACCMODE) { case O_WRONLY: case O_RDWR: return std::make_shared(this->fd, destfd); } break; case O_RDWR: switch (destfd_mode & O_ACCMODE) { case O_RDONLY: return std::make_shared(destfd, this->fd); case O_WRONLY: return std::make_shared(this->fd, destfd); } break; } return std::make_shared(-1, -1); } private: /// \brief Raw linux file descriptor int fd; }; /// standard input FD extern linux_file_descriptor std_in; /// standard output FD extern linux_file_descriptor std_out; /// standard error FD extern linux_file_descriptor std_err; /// \brief redirects stream from \b fd_source to \b fd_destination /// /// \param fd_source Source file descriptor /// \param fd_destination Destination file descriptor inline static std::shared_ptr operator>(const linux_file_descriptor& fd_source, const linux_file_descriptor& fd_destination) { return fd_source.redirect_to(fd_destination); } /// \brief redirects stream from \b fd_source to \b fd_destination /// /// \param fd_source Source file descriptor /// \param fd_destination Destination file descriptor inline static std::shared_ptr operator>(const linux_file_descriptor& fd_source, int fd_destination) { return fd_source.redirect_to(fd_destination); } /// \brief redirects stream from \b fd_source to \b fd_destination /// /// \param fd_source Source file descriptor /// \param fd_destination Destination file descriptor inline std::shared_ptr operator>(int fd_source, const linux_file_descriptor& fd_destination) { return fd_destination.redirect_to(fd_source); } /// \brief redirects stream from \b fd_source to \b filename /// /// \param fd_source Source file descriptor /// \param file Pointer to write-open destination file inline std::shared_ptr operator>(const linux_file_descriptor& fd_source, FILE* file) { if (file) return fd_source.redirect_to(file->_fileno); else return std::make_shared(-1, -1); } template concept fd_redirection_type = std::is_same_v>; /// \brief performs redirection of file descriptor according to \b redirection \b information ////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// this function uses dup2() call to set \b redirection.source fd equal to \b redirection.destination fd /// /// \param redirection Redirection information object (usually result of \b operator>() call) inline void process_redirection(std::shared_ptr redirection) { ::dup2(redirection->destination, redirection->source); ::close(redirection->destination); } /// \brief performs multiple redirections of file descriptors according to \b redirection \b information ////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// this function uses dup2() call to set \b redirection.source fd equal to \b redirection.destination fd /// /// \param redirections_head First redirection information object /// \param redirections_tail Other redirection information objects template inline void process_redirections(fd_redirection_t redirections_head, fd_redirect_t_other... redirections_tail) { process_redirection(redirections_head); if constexpr(sizeof...(redirections_tail) > 0) process_redirections(redirections_tail...); } /// \brief executes application ////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// this function uses fork() call to create child process and execvpe() call to replace program with another /// /// \param argc Arguments count (argv size) /// \param argv An array of null-terminated strings that represents command line arguments where the first argument, /// by convention, should point to the filename associated with the file being executed /// \param environment An array of pointers to null-terminated strings and must be terminated by a null pointer /// \param redirections list of file descriptor redirections template inline pid_t execute_program(char* const* argv, char* const* environment, fd_redirection_t... redirections) { errno = 0; pid_t pid = ::fork(); if (!pid) // child process { if constexpr(sizeof...(redirections) > 0) process_redirections(redirections...); if (environment) // environment != nullptr { ::execvpe(argv[0], argv, environment); } else // environment == nullptr { ::execvp(argv[0], argv); } } return pid; } inline int wait_for_program(pid_t pid) { if (pid > 0) { int stat; ::waitpid(pid, &stat, 0); return WEXITSTATUS(stat); } return EXECUTION_FAILED; } } #endif //EXECUTE_PROCESS_LINUX_EXECUTE_PROCESS_LINUX_