/* This file is part of Jellyfish. Jellyfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jellyfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jellyfish. If not, see . */ #ifndef __JELLYFISH_GENERIC_FILE_HEADER_HPP__ #define __JELLYFISH_GENERIC_FILE_HEADER_HPP__ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_NSGETEXECUTABLEPATH #include #endif #include #include #include #include #include #include #include #include #include #include namespace jellyfish { /// Generic file header. It contains by default the hostname, the /// current time, the current working directory and the path to the /// executable. class generic_file_header { protected: static const int MAX_HEADER_DIGITS = 9; Json::Value root_; size_t offset_; // Nb of bytes past header struct buffer { char* data; buffer(size_t size) : data(new char[size]) { } ~buffer() { delete [] data; } }; struct restore_fmtflags { std::ostream& os_; std::ios::fmtflags flags_; std::streamsize width_; char fill_; restore_fmtflags(std::ostream& os) : os_(os), flags_(os.flags(std::ios::fmtflags())), width_(os.width()), fill_(os.fill()) { } ~restore_fmtflags() { os_.flags(flags_); os_.width(width_); os_.fill(fill_); } }; static void chomp(std::string& s) { size_t found = s.find_last_not_of(" \t\f\v\n\r"); if (found != std::string::npos) s.erase(found+1); else s.clear(); } public: explicit generic_file_header(int alignment = 0) { root_["alignment"] = alignment; } bool operator==(const generic_file_header& rhs) const { std::cerr << "operator== " << (root_ == rhs.root_) << "\n"; return root_ == rhs.root_; } bool operator!=(const generic_file_header& rhs) const { return root_ != rhs.root_; } /// Write the header to an output stream. The format will be: the /// length written in text and decimal, followed by the header in /// terse JSON format, followed by some padding to align according /// to the `alignment_` member. void write(std::ostream& os) { restore_fmtflags flags(os); Json::FastWriter writer; std::string header = writer.write(root_); chomp(header); int align = alignment(); int padding = 0; size_t hlen = header.size(); if(align > 0) { padding = (MAX_HEADER_DIGITS + header.size()) % align; if(padding) hlen += align - padding; } os << std::dec << std::right << std::setw(MAX_HEADER_DIGITS) << std::setfill('0') << hlen; os.write(header.c_str(), header.size()); offset_ = MAX_HEADER_DIGITS + hlen; if(padding) { char pad[align - padding]; memset(pad, '\0', align - padding); os.write(pad, align - padding); } } /// Read an input stream to search for a header. If one is found, /// true is returned. In that case, the position in the input stream points after the header and padding. /// /// If false is returned, the parsing failed. The /// position in the input stream may have changed and the keys /// present in this header may be anything. bool read(std::istream& is) { std::string len; int i; for(i = 0; i < MAX_HEADER_DIGITS && isdigit(is.peek()); ++i) len += is.get(); if(is.peek() != '{') return false; unsigned long hlen = atol(len.c_str()); if(hlen < 2) return false; offset_ = MAX_HEADER_DIGITS + hlen; buffer hbuf(hlen); is.read(hbuf.data, hlen); if(!is.good()) return false; const char* end = hbuf.data + hlen; while(end > hbuf.data && *(end - 1) == '\0') --end; Json::Reader reader; if(!reader.parse(hbuf.data, end, root_, false)) return false; return true; } const Json::Value root() const { return root_; } void fill_standard() { root_["hostname"] = get_hostname(); root_["pwd"] = get_pwd(); root_["time"] = get_localtime(); root_["exe_path"] = get_exe_path(); } std::string operator[](const std::string& key) const { return root_.get(key, "").asString(); } std::string operator[](const char* key) const { return root_.get(key, "").asString(); } int alignment() const { return std::max(0, root_.get("alignment", 0).asInt()); } size_t offset() const { return offset_; } std::vector cmdline() const { std::vector res; for(unsigned int i = 0; i < root_["cmdline"].size(); ++i) res.push_back(root_["cmdline"][i].asString()); return res; } void set_cmdline(int argc, char* argv[]) { root_["cmdline"].clear(); for(int i = 0; i < argc; i++) root_["cmdline"].append(argv[i]); } protected: std::string get_hostname() const { struct utsname buf; if(uname(&buf) == -1) return ""; return buf.nodename; } std::string get_pwd() const { #ifdef PATH_MAX size_t len = PATH_MAX; #else size_t len = 1024; #endif char path[len + 1]; if(!getcwd(path, len + 1)) path[0] = '\0'; return path; } std::string get_localtime() const { time_t t = time(0); std::string res(ctime(&t)); chomp(res); return res; } std::string get_exe_path() const { #ifdef HAVE_NSGETEXECUTABLEPATH return get_exe_path_macosx(); #else return get_exe_path_linux(); #endif } #ifdef HAVE_NSGETEXECUTABLEPATH std::string get_exe_path_macosx() const { #ifdef MAXPATHLEN size_t len = MAXPATHLEN; #else size_t len = 1024; #endif char path[len + 1]; if(_NSGetExecutablePath(path, (uint32_t*)&len) == -1) return ""; return std::string(path); } #endif // HAVE_NSGETEXECUTABLEPATH std::string get_exe_path_linux() const { #ifdef PATH_MAX size_t len = PATH_MAX; #else size_t len = 1024; #endif char path[len + 1]; ssize_t l = readlink("/proc/self/exe", path, len + 1); if(l == -1) return ""; return std::string(path, l); } }; inline std::ostream& operator<<(std::ostream& os, const generic_file_header& h) { Json::StyledWriter w; return os << w.write(h.root()); } } // namespace jellyfish #endif /* __JELLYFISH_GENERIC_FILE_HEADER_HPP__ */