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