00001 #include "cetlib/PluginTypeDeducer.h"
00002 #include "fhiclcpp/ParameterSet.h"
00003
00004 #include "messagefacility/MessageService/ELdestination.h"
00005 #include "messagefacility/Utilities/ELseverityLevel.h"
00006 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // v2_02_01 is s67
00007 # include "messagefacility/MessageService/MessageDrop.h"
00008 #else
00009 # include "messagefacility/MessageLogger/MessageLogger.h"
00010 #endif
00011 #include "messagefacility/Utilities/exception.h"
00012 #include "cetlib/compiler_macros.h"
00013
00014
00015 #include <iostream>
00016 #include <fstream>
00017 #include <memory>
00018 #include <algorithm>
00019 #include <arpa/inet.h>
00020 #include <ifaddrs.h>
00021 #include <netdb.h>
00022 #include <netinet/in.h>
00023
00024
00025
00026 #include <boost/asio.hpp>
00027 #include <boost/algorithm/string.hpp>
00028
00029 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // format changed to format_ for s67
00030 #define format_ format
00031 #endif
00032
00033 namespace mfplugins
00034 {
00035 using mf::service::ELdestination;
00036 using mf::ELseverityLevel;
00037 using mf::ErrorObj;
00038 using boost::asio::ip::udp;
00039
00044 class ELUDP : public ELdestination
00045 {
00046 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
00047 struct Config
00048 {
00049 fhicl::TableFragment<ELdestination::Config> elDestConfig;
00050 fhicl::Atom<int> error_max{ fhicl::Name{ "error_turnoff_threshold" },fhicl::Comment{ "Number of errors before turning off destination (default: 0, don't turn off)" },0 };
00051 fhicl::Atom<int> error_report{ fhicl::Name{ "error_report_backoff_factor" },fhicl::Comment{ "Print an error message every N errors" },100 };
00052 fhicl::Atom<std::string> host{ fhicl::Name{ "host" },fhicl::Comment{ "Address to send messages to" }, "227.128.12.27" };
00053 fhicl::Atom<int> port{ fhicl::Name{ "port" },fhicl::Comment{ "Port to send messages to" },5140 };
00054 };
00055 using Parameters = fhicl::WrappedTable<Config>;
00056 #endif
00057 public:
00058 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
00059 ELUDP(const fhicl::ParameterSet& pset);
00060 #else
00061 ELUDP(Parameters const& pset);
00062 #endif
00063
00064 virtual void fillPrefix(std::ostringstream&, const ErrorObj&) override;
00065
00066 virtual void fillUsrMsg(std::ostringstream&, const ErrorObj&) override;
00067
00068 virtual void fillSuffix(std::ostringstream&, const ErrorObj&) override {}
00069
00070 virtual void routePayload(const std::ostringstream&, const ErrorObj&) override;
00071
00072 private:
00073 void reconnect_(bool quiet = false);
00074
00075
00076 int error_report_backoff_factor_;
00077 int error_max_;
00078 std::string host_;
00079 int port_;
00080
00081
00082 boost::asio::io_service io_service_;
00083 udp::socket socket_;
00084 udp::endpoint remote_endpoint_;
00085 int consecutive_success_count_;
00086 int error_count_;
00087 int next_error_report_;
00088 int seqNum_;
00089
00090
00091 long pid_;
00092 std::string hostname_;
00093 std::string hostaddr_;
00094 std::string app_;
00095 };
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
00107 ELUDP::ELUDP(const fhicl::ParameterSet& pset)
00108 : ELdestination(pset)
00109 , error_report_backoff_factor_(pset.get<int>("error_report_backoff_factor", 100))
00110 , error_max_(pset.get<int>("error_turnoff_threshold", 0))
00111 , host_(pset.get<std::string>("host", "227.128.12.27"))
00112 , port_(pset.get<int>("port", 5140))
00113 #else
00114 ELUDP::ELUDP(Parameters const& pset)
00115 : ELdestination(pset().elDestConfig())
00116 , error_report_backoff_factor_(pset().error_report())
00117 , error_max_(pset().error_max())
00118 , host_(pset().host())
00119 , port_(pset().port())
00120 #endif
00121 , io_service_()
00122 , socket_(io_service_)
00123 , remote_endpoint_()
00124 , consecutive_success_count_(0)
00125 , error_count_(0)
00126 , next_error_report_(1)
00127 , seqNum_(0)
00128 , pid_(static_cast<long>(getpid()))
00129 {
00130 reconnect_();
00131
00132
00133 char hostname_c[1024];
00134 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
00135
00136
00137 hostent* host = nullptr;
00138 host = gethostbyname(hostname_c);
00139
00140 if (host != nullptr)
00141 {
00142
00143 char* ip = inet_ntoa(*(struct in_addr *)host->h_addr);
00144 hostaddr_ = ip;
00145 }
00146 else
00147 {
00148
00149 struct ifaddrs* ifAddrStruct = nullptr;
00150 struct ifaddrs* ifa = nullptr;
00151 void* tmpAddrPtr = nullptr;
00152
00153 if (getifaddrs(&ifAddrStruct))
00154 {
00155
00156 hostaddr_ = "127.0.0.1";
00157 }
00158 else
00159 {
00160
00161 for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
00162 {
00163 if (ifa->ifa_addr->sa_family == AF_INET)
00164 {
00165
00166 tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
00167 char addressBuffer[INET_ADDRSTRLEN];
00168 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
00169 hostaddr_ = addressBuffer;
00170 }
00171
00172 else if (ifa->ifa_addr->sa_family == AF_INET6)
00173 {
00174
00175 tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
00176 char addressBuffer[INET6_ADDRSTRLEN];
00177 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
00178 hostaddr_ = addressBuffer;
00179 }
00180
00181
00182 if (!hostaddr_.empty()
00183 && hostaddr_.compare("127.0.0.1")
00184 && hostaddr_.compare("::1"))
00185 break;
00186 }
00187
00188 if (hostaddr_.empty())
00189 hostaddr_ = "127.0.0.1";
00190 }
00191 }
00192
00193 #if 0
00194
00195 std::string exe;
00196 std::ostringstream pid_ostr;
00197 pid_ostr << "/proc/" << pid_ << "/exe";
00198 exe = realpath(pid_ostr.str().c_str(), NULL);
00199
00200 size_t end = exe.find('\0');
00201 size_t start = exe.find_last_of('/', end);
00202
00203 app_ = exe.substr(start + 1, end - start - 1);
00204 #else
00205
00206 std::stringstream ss;
00207 ss << "//proc//" << pid_ << "//cmdline";
00208 std::ifstream procfile{ ss.str().c_str() };
00209
00210 std::string procinfo;
00211
00212 if (procfile.is_open())
00213 {
00214 procfile >> procinfo;
00215 procfile.close();
00216 }
00217
00218 size_t end = procinfo.find('\0');
00219 size_t start = procinfo.find_last_of('/', end);
00220
00221 app_ = procinfo.substr(start + 1, end - start - 1);
00222 #endif
00223 }
00224
00225 void ELUDP::reconnect_(bool quiet)
00226 {
00227 boost::system::error_code ec;
00228 socket_.open(udp::v4());
00229 socket_.set_option(boost::asio::socket_base::reuse_address(true), ec);
00230 if (ec && !quiet)
00231 {
00232 std::cerr << "An error occurred setting reuse_address to true: "
00233 << ec.message() << std::endl;
00234 }
00235
00236 if (boost::iequals(host_, "Broadcast") || host_ == "255.255.255.255")
00237 {
00238 socket_.set_option(boost::asio::socket_base::broadcast(true), ec);
00239 remote_endpoint_ = udp::endpoint(boost::asio::ip::address_v4::broadcast(), port_);
00240 }
00241 else
00242 {
00243 udp::resolver resolver(io_service_);
00244 udp::resolver::query query(udp::v4(), host_, std::to_string(port_));
00245 remote_endpoint_ = *resolver.resolve(query);
00246 }
00247
00248 socket_.connect(remote_endpoint_, ec);
00249 if (ec)
00250 {
00251 if (!quiet)
00252 {
00253 std::cerr << "An Error occurred in connect(): " << ec.message() << std::endl
00254 << " endpoint = " << remote_endpoint_ << std::endl;
00255 }
00256 error_count_++;
00257 }
00258
00259
00260
00261
00262 }
00263
00264
00265
00266
00267 void ELUDP::fillPrefix(std::ostringstream& oss, const ErrorObj& msg)
00268 {
00269 const auto& xid = msg.xid();
00270
00271 auto id = xid.id();
00272 auto module = xid.module();
00273 auto app = app_;
00274 std::replace(id.begin(), id.end(), '|', '!');
00275 std::replace(app.begin(), app.end(), '|', '!');
00276 std::replace(module.begin(), module.end(), '|', '!');
00277
00278 oss << format_.timestamp(msg.timestamp()) << "|";
00279 oss << std::to_string(++seqNum_) << "|";
00280 oss << hostname_ << "|";
00281 oss << hostaddr_ << "|";
00282 oss << xid.severity().getName() << "|";
00283 oss << id << "|";
00284 oss << app << "|";
00285 # if MESSAGEFACILITY_HEX_VERSION >= 0x20201 // an indication of s67
00286 oss << pid_ << "|";
00287 oss << mf::GetIteration() << "|";
00288 #else
00289 oss << pid_ << "|";
00290 oss << mf::MessageDrop::instance()->iteration << "|";
00291 #endif
00292 oss << module << "|";
00293 #if MESSAGEFACILITY_HEX_VERSION >= 0x20201
00294 oss << msg.filename() << "|" << std::to_string(msg.lineNumber()) << "|";
00295 #endif
00296 }
00297
00298
00299
00300
00301 void ELUDP::fillUsrMsg(std::ostringstream& oss, const ErrorObj& msg)
00302 {
00303 std::ostringstream tmposs;
00304
00305 for (auto const& val : msg.items())
00306 {
00307 tmposs << val;
00308 }
00309
00310
00311 const std::string& usrMsg = !tmposs.str().compare(0, 1, "\n") ? tmposs.str().erase(0, 1) : tmposs.str();
00312
00313 oss << usrMsg;
00314 }
00315
00316
00317
00318
00319 void ELUDP::routePayload(const std::ostringstream& oss, const ErrorObj&)
00320 {
00321 if (error_count_ < error_max_ || error_max_ == 0)
00322 {
00323 auto pid = pid_;
00324
00325 auto string = "UDPMFMESSAGE" + std::to_string(pid) + "|" + oss.str();
00326
00327 auto message = boost::asio::buffer(string);
00328 bool error = true;
00329 try
00330 {
00331 socket_.send_to(message, remote_endpoint_);
00332 ++consecutive_success_count_;
00333 if (consecutive_success_count_ >= 5)
00334 {
00335 error_count_ = 0;
00336 next_error_report_ = 1;
00337 }
00338 error = false;
00339 }
00340 catch (boost::system::system_error& err)
00341 {
00342 consecutive_success_count_ = 0;
00343 ++error_count_;
00344 if (error_count_ == next_error_report_)
00345 {
00346 std::cerr << "An exception occurred when trying to send message " << std::to_string(seqNum_) << " to "
00347 << remote_endpoint_ << std::endl
00348 << " message = " << oss.str() << std::endl
00349 << " exception = " << err.what() << std::endl;
00350 next_error_report_ *= error_report_backoff_factor_;
00351 }
00352 }
00353 if (error)
00354 {
00355 try
00356 {
00357 reconnect_(true);
00358 }
00359 catch (...) {}
00360 }
00361 }
00362 }
00363 }
00364
00365
00366
00367
00368
00369
00370
00371 #ifndef EXTERN_C_FUNC_DECLARE_START
00372 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
00373 #endif
00374
00375 EXTERN_C_FUNC_DECLARE_START
00376 auto makePlugin(const std::string&,
00377 const fhicl::ParameterSet& pset)
00378 {
00379 return std::make_unique<mfplugins::ELUDP>(pset);
00380 }
00381 }
00382
00383 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)