diff options
Diffstat (limited to 'cpp/src/qpid/legacystore/jrnl/jinf.cpp')
-rw-r--r-- | cpp/src/qpid/legacystore/jrnl/jinf.cpp | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/cpp/src/qpid/legacystore/jrnl/jinf.cpp b/cpp/src/qpid/legacystore/jrnl/jinf.cpp new file mode 100644 index 0000000000..4117bd3581 --- /dev/null +++ b/cpp/src/qpid/legacystore/jrnl/jinf.cpp @@ -0,0 +1,540 @@ +/* + * + * 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 <cstdlib> +#include <cstring> +#include <ctime> +#include <fstream> +#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 <sstream> +#include <sys/stat.h> + +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 << "<?xml version=\"1.0\" ?>" << std::endl; + oss << "<jrnl>" << std::endl; + oss << " <journal_version value=\"" << (unsigned)_jver << "\" />" << std::endl; + oss << " <journal_id>" << std::endl; + oss << " <id_string value=\"" << _jid << "\" />" << std::endl; + oss << " <directory value=\"" << _jdir << "\" />" << std::endl; + oss << " <base_filename value=\"" << _base_filename << "\" />" << std::endl; + oss << " </journal_id>" << std::endl; + oss << " <creation_time>" << std::endl; + oss << " <seconds value=\"" << _ts.tv_sec << "\" />" << std::endl; + oss << " <nanoseconds value=\"" << _ts.tv_nsec << "\" />" << std::endl; + oss << " <string value=\"" << (_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; + oss << "\" />" << std::endl; + oss << " </creation_time>" << std::endl; + oss << " <journal_file_geometry>" << std::endl; + oss << " <number_jrnl_files value=\"" << _num_jfiles << "\" />" << std::endl; + oss << " <auto_expand value=\"" << (_ae ? "true" : "false") << "\" />" << std::endl; + if (_ae) oss << " <auto_expand_max_jrnl_files value=\"" << _ae_max_jfiles << "\" />" << std::endl; + oss << " <jrnl_file_size_sblks value=\"" << _jfsize_sblks << "\" />" << std::endl; + oss << " <JRNL_SBLK_SIZE value=\"" << _sblk_size_dblks << "\" />" << std::endl; + oss << " <JRNL_DBLK_SIZE value=\"" << _dblk_size << "\" />" << std::endl; + oss << " </journal_file_geometry>" << std::endl; + oss << " <cache_geometry>" << std::endl; + oss << " <wcache_pgsize_sblks value=\"" << _wcache_pgsize_sblks << "\" />" << std::endl; + oss << " <wcache_num_pages value=\"" << _wcache_num_pages << "\" />" << std::endl; + oss << " <JRNL_RMGR_PAGE_SIZE value=\"" << _rcache_pgsize_sblks << "\" />" << std::endl; + oss << " <JRNL_RMGR_PAGES value=\"" << _rcache_num_pages << "\" />" << std::endl; + oss << " </cache_geometry>" << std::endl; + oss << "</jrnl>" << 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 |