#ifndef __NGINX_UTIL_H #define __NGINX_UTIL_H #include <array> #include <cerrno> #include <cstdio> #include <cstdlib> #include <cstring> #include <fstream> #include <string> #include <string_view> #include <thread> #include <unistd.h> #include <vector> #ifndef NO_UBUS #include "ubus-cxx.hpp" #endif static constexpr auto NGINX_UTIL = std::string_view{"/usr/bin/nginx-util"}; static constexpr auto NGINX_CONF = std::string_view{"/etc/nginx/nginx.conf"}; static constexpr auto CONF_DIR = std::string_view{"/etc/nginx/conf.d/"}; static constexpr auto LAN_NAME = std::string_view{"_lan"}; static constexpr auto LAN_LISTEN =std::string_view{"/var/lib/nginx/lan.listen"}; static constexpr auto LAN_LISTEN_DEFAULT = std::string_view{"/var/lib/nginx/lan.listen.default"}; // mode: optional ios::binary and/or ios::app (default ios::trunc) void write_file(const std::string_view & name, const std::string & str, std::ios_base::openmode flag=std::ios::trunc); // mode: optional ios::binary (internally ios::ate|ios::in) auto read_file(const std::string_view & name, std::ios_base::openmode mode=std::ios::in) -> std::string; // all S must be convertible to const char[] template<typename ...S> auto call(const std::string & program, S... args) -> pid_t; void create_lan_listen(); void init_lan(); void get_env(); // --------------------- partial implementation: ------------------------------ void write_file(const std::string_view & name, const std::string & str, const std::ios_base::openmode flag) { auto tmp = std::string{name}; if ( (flag & std::ios::ate) == 0 && (flag & std::ios::app) == 0 ) { tmp += ".tmp-XXXXXX"; auto fd = mkstemp(&tmp[0]); if (fd==-1 || close(fd)!=0) { throw std::runtime_error("write_file error: cannot access " + tmp); } } try { std::ofstream file(tmp.data(), flag); if (!file.good()) { throw std::ofstream::failure ("write_file error: cannot open " + std::string{tmp}); } file<<str<<std::flush; file.close(); } catch(...) { if (tmp!=name) { remove(tmp.c_str()); } //remove can fail. throw; } if (rename(tmp.c_str(), name.data()) != 0) { throw std::runtime_error ("write_file error: cannot move " + tmp + " to " + name.data()); } } auto read_file(const std::string_view & name, const std::ios_base::openmode mode) -> std::string { std::ifstream file(name.data(), mode|std::ios::ate); if (!file.good()) { throw std::ifstream::failure( "read_file error: cannot open " + std::string{name}); } std::string ret{}; const size_t size = file.tellg(); ret.reserve(size); file.seekg(0); ret.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); file.close(); return ret; } template<typename ...S> auto call(const char * program, S... args) -> pid_t { pid_t pid = fork(); if (pid==0) { //child: std::array<char *, sizeof...(args)+2> argv = { strdup(program), strdup(args)..., nullptr }; execv(program, argv.data()); // argv cannot be const char * const[]! _exit(EXIT_FAILURE); // exec never returns. } else if (pid>0) { //parent: return pid; } std::string errmsg = "call error: cannot fork ("; errmsg += std::to_string(errno) + "): " + std::strerror(errno); throw std::runtime_error(errmsg); } #endif