support 2nd RCPT TO and PRDR
[ghsmtp.git] / osutil.cpp
blob0a295548446c7b323e67034ca116be889bf255a3
1 #include "osutil.hpp"
3 #include "iobuffer.hpp"
5 #include <regex>
7 #include <boost/algorithm/string/classification.hpp>
8 #include <boost/algorithm/string/split.hpp>
10 #include <pwd.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
14 #include <gflags/gflags.h>
15 namespace gflags {
18 DEFINE_string(config_dir, "", "path to support/config files");
20 #include <sys/utsname.h>
21 #include <unistd.h>
23 #include <netdb.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
27 #include <glog/logging.h>
29 namespace osutil {
31 fs::path get_config_dir()
33 if (!FLAGS_config_dir.empty()) {
34 return FLAGS_config_dir;
36 else {
37 return get_exe_path().parent_path();
39 // Maybe work from some installed location...
40 // if ends in /bin, switch to /share or /etc
42 // auto path = get_exe_path().parent_path();
43 // if (fs::is_directory(path) && (path.filename() == "bin")) {
44 // auto share = path;
45 // share.replace_filename("share");
46 // if (fs::exists(share) && fs::is_directory(share))
47 // path = share;
48 // }
52 fs::path get_exe_path()
54 // The std::experimental::filesystem::read_symlink() as shipped with
55 // GCC 7.2.1 20170915 included with Fedora 27 is unusable when lstat
56 // returns st_size of zero, as happens with /proc/self/exe.
58 // This problem has been corrected in later versions, but this code
59 // should work on everything POSIX.
61 auto constexpr exe = "/proc/self/exe";
63 auto constexpr max_link = 4 * 1024;
64 char buf[max_link];
66 auto const len{::readlink(exe, buf, max_link)};
68 PCHECK(len != -1) << "readlink";
69 if (len == max_link) {
70 LOG(FATAL) << exe << " link too long";
72 buf[len] = '\0';
73 return fs::path(buf);
76 fs::path get_home_dir()
78 auto const homedir_ev{getenv("HOME")};
79 if (homedir_ev) {
80 return homedir_ev;
82 else {
83 errno = 0; // See GETPWNAM(3)
84 passwd* pw;
85 PCHECK(pw = getpwuid(getuid()));
86 return pw->pw_dir;
90 std::string get_hostname()
92 utsname un;
93 PCHECK(uname(&un) == 0);
95 auto node = std::string(un.nodename);
97 // auto labels{std::vector<std::string>{}};
98 // boost::algorithm::split(labels, node,
99 // boost::algorithm::is_any_of("."));
100 // if (labels.size() < 2) {
101 // node += ".digilicious.com";
102 // }
104 return node;
107 uint16_t get_port(char const* const service, char const* const proto)
109 char* ep = nullptr;
110 auto const service_no{strtoul(service, &ep, 10)};
111 if (ep && (*ep == '\0')) {
112 CHECK_LE(service_no, std::numeric_limits<uint16_t>::max());
113 return static_cast<uint16_t>(service_no);
116 iobuffer<char> str_buf{1024}; // suggested by getservbyname_r(3)
118 auto result_buf{servent{}};
119 servent* result_ptr = nullptr;
120 while (getservbyname_r(service, proto, &result_buf, str_buf.data(),
121 str_buf.size(), &result_ptr)
122 == ERANGE) {
123 CHECK_LT(str_buf.size(), 64 * 1024); // ridiculous
124 str_buf.resize(str_buf.size() * 2);
126 if (result_ptr == nullptr) {
127 LOG(FATAL) << "service " << service << " unknown";
129 return ntohs(result_buf.s_port);
132 std::vector<fs::path> list_directory(
133 fs::path const& path,
134 // pattern should be std::string_view see p0506r0:
135 // <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0506r0.pdf>
136 // maybe C++20?
137 std::string const& pattern)
139 std::vector<fs::path> ret;
141 auto const traits = std::regex_constants::ECMAScript
142 #if defined(__APPLE__) || defined(_WIN32)
143 | std::regex_constants::icase
144 #endif
147 std::regex const pattern_regex(pattern, traits);
149 auto const di = fs::directory_iterator(path);
150 for (auto const& it : di) {
151 auto const it_filename = it.path().filename().string();
152 std::smatch matches;
153 if (std::regex_match(it_filename, matches, pattern_regex)) {
154 ret.push_back(it.path());
158 return ret;
161 } // namespace osutil