/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ /** * \file jinf.cpp * * Qpid asynchronous store plugin library * * This file contains the code for the mrg::journal::jinf class. * * See jinf.h comments for details of this class. * * \author Kim van der Riet */ #include "jrnl/jinf.h" #include #include #include #include #include "qpid/legacystore/jrnl/file_hdr.h" #include "qpid/legacystore/jrnl/jcntl.h" #include "qpid/legacystore/jrnl/jerrno.h" #include "qpid/legacystore/jrnl/lp_map.h" #include #include namespace mrg { namespace journal { jinf::jinf(const std::string& jinf_filename, bool validate_flag): _jver(0), _filename(jinf_filename), _num_jfiles(0), _ae(false), _ae_max_jfiles(0), _jfsize_sblks(0), _sblk_size_dblks(0), _dblk_size(0), _wcache_pgsize_sblks(0), _wcache_num_pages(0), _rcache_pgsize_sblks(0), _rcache_num_pages(0), _tm_ptr(0), _valid_flag(false), _analyzed_flag(false), _initial_owi(false), _frot(false) { read(_filename); if (validate_flag) validate(); } jinf::jinf(const std::string& jid, const std::string& jdir, const std::string& base_filename, const u_int16_t num_jfiles, const bool auto_expand, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, const u_int32_t wcache_pgsize_sblks, const u_int16_t wcache_num_pages, const timespec& ts): _jver(RHM_JDAT_VERSION), _jid(jid), _jdir(jdir), _base_filename(base_filename), _ts(ts), _num_jfiles(num_jfiles), _ae(auto_expand), _ae_max_jfiles(ae_max_jfiles), _jfsize_sblks(jfsize_sblks), _sblk_size_dblks(JRNL_SBLK_SIZE), _dblk_size(JRNL_DBLK_SIZE), _wcache_pgsize_sblks(wcache_pgsize_sblks), _wcache_num_pages(wcache_num_pages), _rcache_pgsize_sblks(JRNL_RMGR_PAGE_SIZE), _rcache_num_pages(JRNL_RMGR_PAGES), _tm_ptr(std::localtime(&ts.tv_sec)), _valid_flag(false), _analyzed_flag(false), _initial_owi(false) { set_filename(); } jinf::~jinf() {} void jinf::validate() { bool err = false; std::ostringstream oss; if (_jver != RHM_JDAT_VERSION) { oss << "File \"" << _filename << "\": "; oss << "RHM_JDAT_VERSION mismatch: found=" << (int)_jver; oss << "; required=" << RHM_JDAT_VERSION << std::endl; err = true; } if (_num_jfiles < JRNL_MIN_NUM_FILES) { oss << "File \"" << _filename << "\": "; oss << "Number of journal files too small: found=" << _num_jfiles; oss << "; minimum=" << JRNL_MIN_NUM_FILES << std::endl; err = true; } if (_num_jfiles > JRNL_MAX_NUM_FILES) { oss << "File \"" << _filename << "\": "; oss << "Number of journal files too large: found=" << _num_jfiles; oss << "; maximum=" << JRNL_MAX_NUM_FILES << std::endl; err = true; } if (_ae) { if (_ae_max_jfiles < _num_jfiles) { oss << "File \"" << _filename << "\": "; oss << "Number of journal files exceeds auto-expansion limit: found=" << _num_jfiles; oss << "; maximum=" << _ae_max_jfiles; err = true; } if (_ae_max_jfiles > JRNL_MAX_NUM_FILES) { oss << "File \"" << _filename << "\": "; oss << "Auto-expansion file limit too large: found=" << _ae_max_jfiles; oss << "; maximum=" << JRNL_MAX_NUM_FILES; err = true; } } if (_jfsize_sblks < JRNL_MIN_FILE_SIZE) { oss << "File \"" << _filename << "\": "; oss << "Journal file size too small: found=" << _jfsize_sblks; oss << "; minimum=" << JRNL_MIN_FILE_SIZE << " (sblks)" << std::endl; err = true; } if (_sblk_size_dblks != JRNL_SBLK_SIZE) { oss << "File \"" << _filename << "\": "; oss << "JRNL_SBLK_SIZE mismatch: found=" << _sblk_size_dblks; oss << "; required=" << JRNL_SBLK_SIZE << std::endl; err = true; } if (_dblk_size != JRNL_DBLK_SIZE) { oss << "File \"" << _filename << "\": "; oss << "JRNL_DBLK_SIZE mismatch: found=" << _dblk_size; oss << "; required=" << JRNL_DBLK_SIZE << std::endl; err = true; } if (err) throw jexception(jerrno::JERR_JINF_CVALIDFAIL, oss.str(), "jinf", "validate"); _valid_flag = true; } void jinf::analyze() { lp_map early_map; // map for all owi flags same as pfid 0 lp_map late_map; // map for all owi flags opposite to pfid 0 bool late_latch = false; // latch for owi switchover if (!_valid_flag) validate(); bool done = false; for (u_int16_t pfid=0; pfid<_num_jfiles && !done; pfid++) { std::ostringstream oss; if (_jdir.at(_jdir.size() - 1) == '/') oss << _jdir << _base_filename << "."; else oss << _jdir << "/" << _base_filename << "."; oss << std::setw(4) << std::setfill('0') << std::hex << pfid; oss << "." << JRNL_DATA_EXTENSION; // Check size of each file is consistent and expected u_int32_t fsize = get_filesize(oss.str()); if (fsize != (_jfsize_sblks + 1) * _sblk_size_dblks * _dblk_size) { std::ostringstream oss1; oss1 << "File \"" << oss.str() << "\": size=" << fsize << "; expected=" << ((_jfsize_sblks + 1) * _sblk_size_dblks * _dblk_size); throw jexception(jerrno::JERR_JINF_BADFILESIZE, oss1.str(), "jinf", "analyze"); } std::ifstream jifs(oss.str().c_str()); if (!jifs.good()) throw jexception(jerrno::JERR__FILEIO, oss.str(), "jinf", "analyze"); file_hdr fhdr; jifs.read((char*)&fhdr, sizeof(fhdr)); if (fhdr._magic != RHM_JDAT_FILE_MAGIC) // No file header { if (fhdr._magic != 0) throw jexception(jerrno::JERR_JINF_INVALIDFHDR, oss.str(), "jinf", "analyze"); if (!pfid) // pfid 0 == lid 0 cannot be empty throw jexception(jerrno::JERR_JINF_JDATEMPTY, oss.str(), "jinf", "analyze"); _frot = true; done = true; } else { assert(pfid == fhdr._pfid); if (pfid == 0) { _initial_owi = fhdr.get_owi(); early_map.insert(fhdr._lfid, pfid); } else { if (_initial_owi == fhdr.get_owi()) { early_map.insert(fhdr._lfid, pfid); if (late_latch && (!_ae || _num_jfiles == JRNL_MIN_NUM_FILES)) throw jexception(jerrno::JERR_JINF_OWIBAD, oss.str(), "jinf", "analyze"); } else { late_map.insert(fhdr._lfid, pfid); late_latch = true; } } } jifs.close(); } // for (pfid) // If this is not the first rotation, all files should be in either early or late maps if (!_frot) assert(early_map.size() + late_map.size() == _num_jfiles); _pfid_list.clear(); late_map.get_pfid_list(_pfid_list); early_map.get_pfid_list(_pfid_list); // Check OWI consistency // for (u_int16_t lfid=0; lfid<_num_jfiles && !done; lfid++) // { // throw jexception(jerrno::JERR_JINF_OWIBAD, oss.str(), "jinf", "analyze"); // } _analyzed_flag = true; } void jinf::write() { std::ostringstream oss; oss << _jdir << "/" << _base_filename << "." << JRNL_INFO_EXTENSION; std::ofstream of(oss.str().c_str(), std::ofstream::out | std::ofstream::trunc); if (!of.good()) throw jexception(jerrno::JERR__FILEIO, oss.str(), "jinf", "write"); of << xml_str(); of.close(); } u_int16_t jinf::incr_num_jfiles() { if (_num_jfiles >= JRNL_MAX_NUM_FILES) throw jexception(jerrno::JERR_JINF_TOOMANYFILES, "jinf", "incr_num_jfiles"); return ++_num_jfiles; } u_int16_t jinf::get_first_pfid() { if (!_analyzed_flag) analyze(); return *_pfid_list.begin(); } u_int16_t jinf::get_last_pfid() { if (!_analyzed_flag) analyze(); return *_pfid_list.rbegin(); } jinf::pfid_list& jinf::get_pfid_list() { if (!_analyzed_flag) analyze(); return _pfid_list; } void jinf::get_normalized_pfid_list(pfid_list& pfid_list) { if (!_analyzed_flag) analyze(); pfid_list.clear(); u_int16_t s = _pfid_list.size(); u_int16_t iz = 0; // index of 0 value while (_pfid_list[iz] && iz < s) iz++; assert(_pfid_list[iz] == 0); for (u_int16_t i = iz; i < iz + s; i++) pfid_list.push_back(_pfid_list[i % s]); assert(pfid_list[0] == 0); assert(pfid_list.size() == s); } bool jinf::get_initial_owi() { if (!_analyzed_flag) analyze(); return _initial_owi; } bool jinf::get_frot() { if (!_analyzed_flag) analyze(); return _frot; } std::string jinf::to_string() const { std::ostringstream oss; oss << std::setfill('0'); oss << "Journal ID \"" << _jid << "\" initialized " << (_tm_ptr->tm_year + 1900) << "/"; oss << std::setw(2) << (_tm_ptr->tm_mon + 1) << "/" << std::setw(2) << _tm_ptr->tm_mday << " "; oss << std::setw(2) << _tm_ptr->tm_hour << ":" << std::setw(2) << _tm_ptr->tm_min << ":"; oss << std::setw(2) << _tm_ptr->tm_sec << "." << std::setw(9) << _ts.tv_nsec << ":" << std::endl; oss << " Journal directory: \"" << _jdir << "\"" << std::endl; oss << " Journal base filename: \"" << _base_filename << "\"" << std::endl; oss << " Journal version: " << (unsigned)_jver << std::endl; oss << " Number of journal files: " << _num_jfiles << std::endl; // TODO: Uncomment these lines when auto-expand is enabled. // oss << " Auto-expand mode: " << (_ae ? "enabled" : "disabled") << std::endl; // if (_ae) oss << " Max. number of journal files (in auto-expand mode): " << _ae_max_jfiles << std::endl; oss << " Journal file size: " << _jfsize_sblks << " sblks" << std::endl; oss << " Softblock size (JRNL_SBLK_SIZE): " << _sblk_size_dblks << " dblks" << std::endl; oss << " Datablock size (JRNL_DBLK_SIZE): " << _dblk_size << " bytes" << std::endl; oss << " Write page size: " << _wcache_pgsize_sblks << " sblks" << std::endl; oss << " Number of write pages: " << _wcache_num_pages << std::endl; oss << " Read page size (JRNL_RMGR_PAGE_SIZE): " << _rcache_pgsize_sblks << " sblks" << std::endl; oss << " Number of read pages (JRNL_RMGR_PAGES): " << _rcache_num_pages << std::endl; return oss.str(); } std::string jinf::xml_str() const { // TODO: This is *not* an XML writer, rather for simplicity, it uses literals. I'm sure a more elegant way can be // found to do this using the real thing... std::ostringstream oss; oss << std::setfill('0'); oss << "" << std::endl; oss << "" << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " tm_year + 1900) << "/"; oss << std::setw(2) << (_tm_ptr->tm_mon + 1) << "/" << std::setw(2) << _tm_ptr->tm_mday << " "; oss << std::setw(2) << _tm_ptr->tm_hour << ":" << std::setw(2) << _tm_ptr->tm_min << ":"; oss << std::setw(2) << _tm_ptr->tm_sec << "." << std::setw(9) << _ts.tv_nsec; oss << "\" />" << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; if (_ae) oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << " " << std::endl; oss << "" << std::endl; return oss.str(); } void jinf::set_filename() { std::ostringstream oss; oss << _jdir << "/" << _base_filename << "." << JRNL_INFO_EXTENSION; _filename = oss.str().c_str(); } void jinf::read(const std::string& jinf_filename) { // TODO: This is *not* an XML reader, rather for simplicity, it is a brute-force line reader which relies on string // recognition. It relies on the format of xml_str() above; it will not handle a XML restructuring. // *** Can it be replaced cheaply by a real XML reader? Should it be, or is this sufficient? *** char buff[1024]; // limit of line input length std::ifstream jinfs(jinf_filename.c_str()); if (!jinfs.good()) throw jexception(jerrno::JERR__FILEIO, jinf_filename.c_str(), "jinf", "read"); u_int32_t charcnt = 0; while (jinfs.good()) { jinfs.getline(buff, 1023); charcnt += std::strlen(buff); if (std::strstr(buff, "journal_version")) _jver = u_int16_value(buff); else if(std::strstr(buff, "id_string")) string_value(_jid, buff); else if(std::strstr(buff, "directory")) string_value(_jdir, buff); else if(std::strstr(buff, "base_filename")) string_value(_base_filename, buff); else if(std::strstr(buff, "number_jrnl_files")) _num_jfiles = u_int16_value(buff); else if(std::strstr(buff, "auto_expand_max_jrnl_files")) _ae_max_jfiles = u_int16_value(buff); else if(std::strstr(buff, "auto_expand")) _ae = bool_value(buff); else if(std::strstr(buff, "jrnl_file_size_sblks")) _jfsize_sblks = u_int32_value(buff); else if(std::strstr(buff, "JRNL_SBLK_SIZE")) _sblk_size_dblks = u_int16_value(buff); else if(std::strstr(buff, "JRNL_DBLK_SIZE")) _dblk_size = u_int32_value(buff); else if(std::strstr(buff, "wcache_pgsize_sblks")) _wcache_pgsize_sblks = u_int32_value(buff); else if(std::strstr(buff, "wcache_num_pages")) _wcache_num_pages = u_int32_value(buff); else if(std::strstr(buff, "JRNL_RMGR_PAGE_SIZE")) _rcache_pgsize_sblks = u_int32_value(buff); else if(std::strstr(buff, "JRNL_RMGR_PAGES")) _rcache_num_pages = u_int32_value(buff); else if(std::strstr(buff, "nanoseconds")) _ts.tv_nsec = u_int32_value(buff); else if(std::strstr(buff, "seconds")) { _ts.tv_sec = u_int32_value(buff); _tm_ptr = std::localtime(&_ts.tv_sec); } } jinfs.close(); if (charcnt == 0) throw jexception(jerrno::JERR_JINF_ZEROLENFILE, jinf_filename.c_str(), "jinf", "read"); } bool jinf::bool_value(char* line) const { return std::strcmp(find_value(line), "true") == 0; } u_int16_t jinf::u_int16_value(char* line) const { return std::atoi(find_value(line)); } u_int32_t jinf::u_int32_value(char* line) const { return std::atol(find_value(line)); } std::string& jinf::string_value(std::string& str, char* line) const { str.assign(find_value(line)); return str; } char* jinf::find_value(char* line) const { const char* target1_str = "value=\""; int target2_char = '\"'; char* t1 = std::strstr(line, target1_str); if (t1 == 0) { std::ostringstream oss; oss << "File \"" << _filename << "\": line=" << line; throw jexception(jerrno::JERR_JINF_NOVALUESTR, oss.str(), "jinf", "find_value"); } t1 += std::strlen(target1_str); char* t2 = std::strchr(t1, target2_char); if (t2 == 0) { std::ostringstream oss; oss << "File \"" << _filename << "\": line=" << line; throw jexception(jerrno::JERR_JINF_BADVALUESTR, oss.str(), "jinf", "find_value"); } *t2 = '\0'; return t1; } u_int32_t jinf::get_filesize(const std::string& file_name) const { struct stat s; if (::stat(file_name.c_str(), &s)) { std::ostringstream oss; oss << "stat: file=\"" << file_name << "\"" << FORMAT_SYSERR(errno); throw jexception(jerrno::JERR_JINF_STAT, oss.str(), "jinf", "get_filesize"); } if (!S_ISREG(s.st_mode)) // not a regular file, { std::ostringstream oss; oss << "File \"" << file_name << "\" is not a regular file: mode=0x" << std::hex << s.st_mode; throw jexception(jerrno::JERR_JINF_NOTREGFILE, oss.str(), "jinf", "get_filesize"); } return u_int32_t(s.st_size); } } // namespace journal } // namespace mrg