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 #include "mfextensions/Receivers/detail/TCPConnect.hh"
00024
00025 #define TRACE_NAME "UDP_mfPlugin"
00026 #include "trace.h"
00027
00028
00029 #include <boost/algorithm/string.hpp>
00030
00031 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // format changed to format_ for s67
00032 #define format_ format
00033 #endif
00034
00035 namespace mfplugins
00036 {
00037 using mf::service::ELdestination;
00038 using mf::ELseverityLevel;
00039 using mf::ErrorObj;
00040
00045 class ELUDP : public ELdestination
00046 {
00047 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
00048 struct Config
00049 {
00050 fhicl::TableFragment<ELdestination::Config> elDestConfig;
00051 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 };
00052 fhicl::Atom<int> error_report{ fhicl::Name{ "error_report_backoff_factor" },fhicl::Comment{ "Print an error message every N errors" },100 };
00053 fhicl::Atom<std::string> host{ fhicl::Name{ "host" },fhicl::Comment{ "Address to send messages to" }, "227.128.12.27" };
00054 fhicl::Atom<int> port{ fhicl::Name{ "port" },fhicl::Comment{ "Port to send messages to" },5140 };
00055 fhicl::Atom<bool> multicast_enabled{ fhicl::Name{"multicast_enabled"},fhicl::Comment{"Whether messages should be sent via multicast"}, false };
00057 fhicl::Atom<std::string> output_address{ fhicl::Name{ "multicast_interface_ip" }, fhicl::Comment{ "Use this hostname for multicast output(to assign to the proper NIC)" }, "0.0.0.0" };
00058 };
00059 using Parameters = fhicl::WrappedTable<Config>;
00060 #endif
00061 public:
00066 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
00067 ELUDP(const fhicl::ParameterSet& pset);
00068 #else
00069 ELUDP(Parameters const& pset);
00070 #endif
00071
00077 virtual void fillPrefix(std::ostringstream& o, const ErrorObj& e) override;
00078
00084 virtual void fillUsrMsg(std::ostringstream& o, const ErrorObj& e) override;
00085
00089 virtual void fillSuffix(std::ostringstream&, const ErrorObj&) override {}
00090
00096 virtual void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
00097
00098 private:
00099 void reconnect_();
00100
00101
00102 int error_report_backoff_factor_;
00103 int error_max_;
00104 std::string host_;
00105 int port_;
00106 bool multicast_enabled_;
00107 std::string multicast_out_addr_;
00108
00109 int message_socket_;
00110 struct sockaddr_in message_addr_;
00111
00112
00113 int consecutive_success_count_;
00114 int error_count_;
00115 int next_error_report_;
00116 int seqNum_;
00117
00118
00119 long pid_;
00120 std::string hostname_;
00121 std::string hostaddr_;
00122 std::string app_;
00123 };
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
00135 ELUDP::ELUDP(const fhicl::ParameterSet& pset)
00136 : ELdestination(pset)
00137 , error_report_backoff_factor_(pset.get<int>("error_report_backoff_factor", 100))
00138 , error_max_(pset.get<int>("error_turnoff_threshold", 0))
00139 , host_(pset.get<std::string>("host", "227.128.12.27"))
00140 , port_(pset.get<int>("port", 5140))
00141 , multicast_enabled_(pset.get<bool>("multicast_enabled", false))
00142 , multicast_out_addr_(pset.get<std::string>("multicast_interface_ip", pset.get<std::string>("output_address", "0.0.0.0")))
00143 #else
00144 ELUDP::ELUDP(Parameters const& pset)
00145 : ELdestination(pset().elDestConfig())
00146 , error_report_backoff_factor_(pset().error_report())
00147 , error_max_(pset().error_max())
00148 , host_(pset().host())
00149 , port_(pset().port())
00150 , multicast_enabled_(pset().multicast_enabled())
00151 , multicast_out_addr_(pset().output_address())
00152 #endif
00153 , message_socket_(-1)
00154 , consecutive_success_count_(0)
00155 , error_count_(0)
00156 , next_error_report_(1)
00157 , seqNum_(0)
00158 , pid_(static_cast<long>(getpid()))
00159 {
00160
00161 char hostname_c[1024];
00162 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
00163
00164
00165 hostent* host = nullptr;
00166 host = gethostbyname(hostname_c);
00167
00168 if (host != nullptr)
00169 {
00170
00171 char* ip = inet_ntoa(*(struct in_addr *)host->h_addr);
00172 hostaddr_ = ip;
00173 }
00174 else
00175 {
00176
00177 struct ifaddrs* ifAddrStruct = nullptr;
00178 struct ifaddrs* ifa = nullptr;
00179 void* tmpAddrPtr = nullptr;
00180
00181 if (getifaddrs(&ifAddrStruct))
00182 {
00183
00184 hostaddr_ = "127.0.0.1";
00185 }
00186 else
00187 {
00188
00189 for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
00190 {
00191 if (ifa->ifa_addr->sa_family == AF_INET)
00192 {
00193
00194 tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
00195 char addressBuffer[INET_ADDRSTRLEN];
00196 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
00197 hostaddr_ = addressBuffer;
00198 }
00199
00200 else if (ifa->ifa_addr->sa_family == AF_INET6)
00201 {
00202
00203 tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
00204 char addressBuffer[INET6_ADDRSTRLEN];
00205 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
00206 hostaddr_ = addressBuffer;
00207 }
00208
00209
00210 if (!hostaddr_.empty()
00211 && hostaddr_.compare("127.0.0.1")
00212 && hostaddr_.compare("::1"))
00213 break;
00214 }
00215
00216 if (hostaddr_.empty())
00217 hostaddr_ = "127.0.0.1";
00218 }
00219 }
00220
00221 #if 0
00222
00223 std::string exe;
00224 std::ostringstream pid_ostr;
00225 pid_ostr << "/proc/" << pid_ << "/exe";
00226 exe = realpath(pid_ostr.str().c_str(), NULL);
00227
00228 size_t end = exe.find('\0');
00229 size_t start = exe.find_last_of('/', end);
00230
00231 app_ = exe.substr(start + 1, end - start - 1);
00232 #else
00233
00234 std::stringstream ss;
00235 ss << "//proc//" << pid_ << "//cmdline";
00236 std::ifstream procfile{ ss.str().c_str() };
00237
00238 std::string procinfo;
00239
00240 if (procfile.is_open())
00241 {
00242 procfile >> procinfo;
00243 procfile.close();
00244 }
00245
00246 size_t end = procinfo.find('\0');
00247 size_t start = procinfo.find_last_of('/', end);
00248
00249 app_ = procinfo.substr(start + 1, end - start - 1);
00250 #endif
00251 }
00252
00253 void ELUDP::reconnect_()
00254 {
00255 message_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
00256 if (message_socket_ < 0)
00257 {
00258 TLOG(TLVL_ERROR) << "I failed to create the socket for sending Data messages! err=" << strerror(errno);
00259 exit(1);
00260 }
00261 int sts = ResolveHost(host_.c_str(), port_, message_addr_);
00262 if (sts == -1)
00263 {
00264 TLOG(TLVL_ERROR) << "Unable to resolve Data message address, err=" << strerror(errno);
00265 exit(1);
00266 }
00267
00268 if (multicast_out_addr_ == "0.0.0.0")
00269 {
00270 multicast_out_addr_.reserve(HOST_NAME_MAX);
00271 sts = gethostname(&multicast_out_addr_[0], HOST_NAME_MAX);
00272 if (sts < 0)
00273 {
00274 TLOG(TLVL_ERROR) << "Could not get current hostname, err=" << strerror(errno);
00275 exit(1);
00276 }
00277 }
00278
00279 if (multicast_out_addr_ != "localhost")
00280 {
00281 struct in_addr addr;
00282 sts = GetInterfaceForNetwork(multicast_out_addr_.c_str(), addr);
00283
00284 if (sts == -1)
00285 {
00286 TLOG(TLVL_ERROR) << "Unable to resolve multicast interface address, err=" << strerror(errno);
00287 exit(1);
00288 }
00289
00290 if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) == -1)
00291 {
00292 TLOG(TLVL_ERROR) << "Cannot set outgoing interface, err=" << strerror(errno);
00293 exit(1);
00294 }
00295 }
00296 int yes = 1;
00297 if (setsockopt(message_socket_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
00298 {
00299 TLOG(TLVL_ERROR) << "Unable to enable port reuse on message socket, err=" << strerror(errno);
00300 exit(1);
00301 }
00302 if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0)
00303 {
00304 TLOG(TLVL_ERROR) << "Unable to enable multicast loopback on message socket, err=" << strerror(errno);
00305 exit(1);
00306 }
00307 if (setsockopt(message_socket_, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(int)) == -1)
00308 {
00309 TLOG(TLVL_ERROR) << "Cannot set message socket to broadcast, err=" << strerror(errno);
00310 exit(1);
00311 }
00312 }
00313
00314
00315
00316
00317 void ELUDP::fillPrefix(std::ostringstream& oss, const ErrorObj& msg)
00318 {
00319 const auto& xid = msg.xid();
00320
00321 auto id = xid.id();
00322 auto module = xid.module();
00323 auto app = app_;
00324 std::replace(id.begin(), id.end(), '|', '!');
00325 std::replace(app.begin(), app.end(), '|', '!');
00326 std::replace(module.begin(), module.end(), '|', '!');
00327
00328 oss << format_.timestamp(msg.timestamp()) << "|";
00329 oss << std::to_string(++seqNum_) << "|";
00330 oss << hostname_ << "|";
00331 oss << hostaddr_ << "|";
00332 oss << xid.severity().getName() << "|";
00333 oss << id << "|";
00334 oss << app << "|";
00335 # if MESSAGEFACILITY_HEX_VERSION >= 0x20201 // an indication of s67
00336 oss << pid_ << "|";
00337 oss << mf::GetIteration() << "|";
00338 #else
00339 oss << pid_ << "|";
00340 oss << mf::MessageDrop::instance()->iteration << "|";
00341 #endif
00342 oss << module << "|";
00343 #if MESSAGEFACILITY_HEX_VERSION >= 0x20201
00344 oss << msg.filename() << "|" << std::to_string(msg.lineNumber()) << "|";
00345 #endif
00346 }
00347
00348
00349
00350
00351 void ELUDP::fillUsrMsg(std::ostringstream& oss, const ErrorObj& msg)
00352 {
00353 std::ostringstream tmposs;
00354
00355 for (auto const& val : msg.items())
00356 {
00357 tmposs << val;
00358 }
00359
00360
00361 const std::string& usrMsg = !tmposs.str().compare(0, 1, "\n") ? tmposs.str().erase(0, 1) : tmposs.str();
00362
00363 oss << usrMsg;
00364 }
00365
00366
00367
00368
00369 void ELUDP::routePayload(const std::ostringstream& oss, const ErrorObj&)
00370 {
00371 if (message_socket_ == -1) reconnect_();
00372 if (error_count_ < error_max_ || error_max_ == 0)
00373 {
00374 char str[INET_ADDRSTRLEN];
00375 inet_ntop(AF_INET, &(message_addr_.sin_addr), str, INET_ADDRSTRLEN);
00376
00377 auto string = "UDPMFMESSAGE" + std::to_string(pid_) + "|" + oss.str();
00378 auto sts = sendto(message_socket_, string.c_str(), string.size(), 0, (struct sockaddr *)&message_addr_, sizeof(message_addr_));
00379
00380 if (sts < 0)
00381 {
00382 consecutive_success_count_ = 0;
00383 ++error_count_;
00384 if (error_count_ == next_error_report_)
00385 {
00386 TLOG(TLVL_ERROR) << "Error sending message " << seqNum_ << " to " << host_ << ", errno=" << errno << " (" << strerror(errno) << ")";
00387 next_error_report_ *= error_report_backoff_factor_;
00388 }
00389 }
00390 else
00391 {
00392 ++consecutive_success_count_;
00393 if (consecutive_success_count_ >= 5)
00394 {
00395 error_count_ = 0;
00396 next_error_report_ = 1;
00397 }
00398 }
00399 }
00400 }
00401 }
00402
00403
00404
00405
00406
00407
00408
00409 #ifndef EXTERN_C_FUNC_DECLARE_START
00410 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
00411 #endif
00412
00413 EXTERN_C_FUNC_DECLARE_START
00414 auto makePlugin(const std::string&,
00415 const fhicl::ParameterSet& pset)
00416 {
00417 return std::make_unique<mfplugins::ELUDP>(pset);
00418 }
00419 }
00420
00421 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)