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