$treeview $search $mathjax $extrastylesheet
artdaq_mfextensions
v1_03_03
$projectbrief
|
$projectbrief
|
$searchbox |
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 "cetlib/compiler_macros.h" 00012 #include "messagefacility/Utilities/exception.h" 00013 00014 // C/C++ includes 00015 #include <arpa/inet.h> 00016 #include <ifaddrs.h> 00017 #include <netdb.h> 00018 #include <netinet/in.h> 00019 #include <algorithm> 00020 #include <fstream> 00021 #include <iostream> 00022 #include <memory> 00023 #include "mfextensions/Receivers/detail/TCPConnect.hh" 00024 00025 #define TRACE_NAME "UDP_mfPlugin" 00026 #include "trace.h" 00027 00028 // Boost includes 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 using mf::ELseverityLevel; 00037 using mf::ErrorObj; 00038 using mf::service::ELdestination; 00039 00044 class ELUDP : public ELdestination { 00045 struct Config { 00046 fhicl::TableFragment<ELdestination::Config> elDestConfig; 00047 fhicl::Atom<int> error_max{ 00048 fhicl::Name{"error_turnoff_threshold"}, 00049 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"}, 00051 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 fhicl::Atom<bool> multicast_enabled{fhicl::Name{"multicast_enabled"}, 00055 fhicl::Comment{"Whether messages should be sent via multicast"}, false}; 00058 fhicl::Atom<std::string> output_address{ 00059 fhicl::Name{"multicast_interface_ip"}, 00060 fhicl::Comment{"Use this hostname for multicast output(to assign to the proper NIC)"}, "0.0.0.0"}; 00061 }; 00062 using Parameters = fhicl::WrappedTable<Config>; 00063 00064 public: 00069 ELUDP(Parameters const& pset); 00070 00076 virtual void fillPrefix(std::ostringstream& o, const ErrorObj& e) override; 00077 00083 virtual void fillUsrMsg(std::ostringstream& o, const ErrorObj& e) override; 00084 00088 virtual void fillSuffix(std::ostringstream&, const ErrorObj&) override {} 00089 00095 virtual void routePayload(const std::ostringstream& o, const ErrorObj& e) override; 00096 00097 private: 00098 void reconnect_(); 00099 00100 // Parameters 00101 int error_report_backoff_factor_; 00102 int error_max_; 00103 std::string host_; 00104 int port_; 00105 bool multicast_enabled_; 00106 std::string multicast_out_addr_; 00107 00108 int message_socket_; 00109 struct sockaddr_in message_addr_; 00110 00111 // Other stuff 00112 int consecutive_success_count_; 00113 int error_count_; 00114 int next_error_report_; 00115 int seqNum_; 00116 00117 long pid_; 00118 std::string hostname_; 00119 std::string hostaddr_; 00120 std::string app_; 00121 }; 00122 00123 // END DECLARATION 00124 //====================================================================== 00125 // BEGIN IMPLEMENTATION 00126 00127 //====================================================================== 00128 // ELUDP c'tor 00129 //====================================================================== 00130 00131 ELUDP::ELUDP(Parameters const& pset) 00132 : ELdestination(pset().elDestConfig()), 00133 error_report_backoff_factor_(pset().error_report()), 00134 error_max_(pset().error_max()), 00135 host_(pset().host()), 00136 port_(pset().port()), 00137 multicast_enabled_(pset().multicast_enabled()), 00138 multicast_out_addr_(pset().output_address()), 00139 message_socket_(-1), 00140 consecutive_success_count_(0), 00141 error_count_(0), 00142 next_error_report_(1), 00143 seqNum_(0), 00144 pid_(static_cast<long>(getpid())) { 00145 // hostname 00146 char hostname_c[1024]; 00147 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host"; 00148 00149 // host ip address 00150 hostent* host = nullptr; 00151 host = gethostbyname(hostname_c); 00152 00153 if (host != nullptr) { 00154 // ip address from hostname if the entry exists in /etc/hosts 00155 char* ip = inet_ntoa(*(struct in_addr*)host->h_addr); 00156 hostaddr_ = ip; 00157 } else { 00158 // enumerate all network interfaces 00159 struct ifaddrs* ifAddrStruct = nullptr; 00160 struct ifaddrs* ifa = nullptr; 00161 void* tmpAddrPtr = nullptr; 00162 00163 if (getifaddrs(&ifAddrStruct)) { 00164 // failed to get addr struct 00165 hostaddr_ = "127.0.0.1"; 00166 } else { 00167 // iterate through all interfaces 00168 for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next) { 00169 if (ifa->ifa_addr->sa_family == AF_INET) { 00170 // a valid IPv4 addres 00171 tmpAddrPtr = &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; 00172 char addressBuffer[INET_ADDRSTRLEN]; 00173 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); 00174 hostaddr_ = addressBuffer; 00175 } 00176 00177 else if (ifa->ifa_addr->sa_family == AF_INET6) { 00178 // a valid IPv6 address 00179 tmpAddrPtr = &((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; 00180 char addressBuffer[INET6_ADDRSTRLEN]; 00181 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); 00182 hostaddr_ = addressBuffer; 00183 } 00184 00185 // find first non-local address 00186 if (!hostaddr_.empty() && hostaddr_.compare("127.0.0.1") && hostaddr_.compare("::1")) break; 00187 } 00188 00189 if (hostaddr_.empty()) // failed to find anything 00190 hostaddr_ = "127.0.0.1"; 00191 } 00192 } 00193 00194 #if 0 00195 // get process name from '/proc/pid/exe' 00196 std::string exe; 00197 std::ostringstream pid_ostr; 00198 pid_ostr << "/proc/" << pid_ << "/exe"; 00199 exe = realpath(pid_ostr.str().c_str(), NULL); 00200 00201 size_t end = exe.find('\0'); 00202 size_t start = exe.find_last_of('/', end); 00203 00204 app_ = exe.substr(start + 1, end - start - 1); 00205 #else 00206 // get process name from '/proc/pid/cmdline' 00207 std::stringstream ss; 00208 ss << "//proc//" << pid_ << "//cmdline"; 00209 std::ifstream procfile{ss.str().c_str()}; 00210 00211 std::string procinfo; 00212 00213 if (procfile.is_open()) { 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_() { 00226 message_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 00227 if (message_socket_ < 0) { 00228 TLOG(TLVL_ERROR) << "I failed to create the socket for sending Data messages! err=" << strerror(errno); 00229 exit(1); 00230 } 00231 int sts = ResolveHost(host_.c_str(), port_, message_addr_); 00232 if (sts == -1) { 00233 TLOG(TLVL_ERROR) << "Unable to resolve Data message address, err=" << strerror(errno); 00234 exit(1); 00235 } 00236 00237 if (multicast_out_addr_ == "0.0.0.0") { 00238 multicast_out_addr_.reserve(HOST_NAME_MAX); 00239 sts = gethostname(&multicast_out_addr_[0], HOST_NAME_MAX); 00240 if (sts < 0) { 00241 TLOG(TLVL_ERROR) << "Could not get current hostname, err=" << strerror(errno); 00242 exit(1); 00243 } 00244 } 00245 00246 if (multicast_out_addr_ != "localhost") { 00247 struct in_addr addr; 00248 sts = GetInterfaceForNetwork(multicast_out_addr_.c_str(), addr); 00249 // sts = ResolveHost(multicast_out_addr_.c_str(), addr); 00250 if (sts == -1) { 00251 TLOG(TLVL_ERROR) << "Unable to resolve multicast interface address, err=" << strerror(errno); 00252 exit(1); 00253 } 00254 00255 if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) == -1) { 00256 TLOG(TLVL_ERROR) << "Cannot set outgoing interface, err=" << strerror(errno); 00257 exit(1); 00258 } 00259 } 00260 int yes = 1; 00261 if (setsockopt(message_socket_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { 00262 TLOG(TLVL_ERROR) << "Unable to enable port reuse on message socket, err=" << strerror(errno); 00263 exit(1); 00264 } 00265 if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { 00266 TLOG(TLVL_ERROR) << "Unable to enable multicast loopback on message socket, err=" << strerror(errno); 00267 exit(1); 00268 } 00269 if (setsockopt(message_socket_, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(int)) == -1) { 00270 TLOG(TLVL_ERROR) << "Cannot set message socket to broadcast, err=" << strerror(errno); 00271 exit(1); 00272 } 00273 } 00274 00275 //====================================================================== 00276 // Message prefix filler ( overriddes ELdestination::fillPrefix ) 00277 //====================================================================== 00278 void ELUDP::fillPrefix(std::ostringstream& oss, const ErrorObj& msg) { 00279 const auto& xid = msg.xid(); 00280 00281 auto id = xid.id(); 00282 auto module = xid.module(); 00283 auto app = app_; 00284 std::replace(id.begin(), id.end(), '|', '!'); 00285 std::replace(app.begin(), app.end(), '|', '!'); 00286 std::replace(module.begin(), module.end(), '|', '!'); 00287 00288 oss << format_.timestamp(msg.timestamp()) << "|"; // timestamp 00289 oss << std::to_string(++seqNum_) << "|"; // sequence number 00290 oss << hostname_ << "|"; // host name 00291 oss << hostaddr_ << "|"; // host address 00292 oss << xid.severity().getName() << "|"; // severity 00293 oss << id << "|"; // category 00294 oss << app << "|"; // application 00295 #if MESSAGEFACILITY_HEX_VERSION >= 0x20201 // an indication of s67 00296 oss << pid_ << "|"; 00297 oss << mf::GetIteration() << "|"; // run/event no 00298 #else 00299 oss << pid_ << "|"; // process id 00300 oss << mf::MessageDrop::instance()->iteration << "|"; // run/event no 00301 #endif 00302 oss << module << "|"; // module name 00303 #if MESSAGEFACILITY_HEX_VERSION >= 0x20201 00304 oss << msg.filename() << "|" << std::to_string(msg.lineNumber()) << "|"; 00305 #endif 00306 } 00307 00308 //====================================================================== 00309 // Message filler ( overriddes ELdestination::fillUsrMsg ) 00310 //====================================================================== 00311 void ELUDP::fillUsrMsg(std::ostringstream& oss, const ErrorObj& msg) { 00312 std::ostringstream tmposs; 00313 // Print the contents. 00314 for (auto const& val : msg.items()) { 00315 tmposs << val; 00316 } 00317 00318 // remove leading "\n" if present 00319 const std::string& usrMsg = !tmposs.str().compare(0, 1, "\n") ? tmposs.str().erase(0, 1) : tmposs.str(); 00320 00321 oss << usrMsg; 00322 } 00323 00324 //====================================================================== 00325 // Message router ( overriddes ELdestination::routePayload ) 00326 //====================================================================== 00327 void ELUDP::routePayload(const std::ostringstream& oss, const ErrorObj&) { 00328 if (message_socket_ == -1) reconnect_(); 00329 if (error_count_ < error_max_ || error_max_ == 0) { 00330 char str[INET_ADDRSTRLEN]; 00331 inet_ntop(AF_INET, &(message_addr_.sin_addr), str, INET_ADDRSTRLEN); 00332 00333 auto string = "UDPMFMESSAGE" + std::to_string(pid_) + "|" + oss.str(); 00334 auto sts = sendto(message_socket_, string.c_str(), string.size(), 0, (struct sockaddr*)&message_addr_, 00335 sizeof(message_addr_)); 00336 00337 if (sts < 0) { 00338 consecutive_success_count_ = 0; 00339 ++error_count_; 00340 if (error_count_ == next_error_report_) { 00341 TLOG(TLVL_ERROR) << "Error sending message " << seqNum_ << " to " << host_ << ", errno=" << errno << " (" 00342 << strerror(errno) << ")"; 00343 next_error_report_ *= error_report_backoff_factor_; 00344 } 00345 } else { 00346 ++consecutive_success_count_; 00347 if (consecutive_success_count_ >= 5) { 00348 error_count_ = 0; 00349 next_error_report_ = 1; 00350 } 00351 } 00352 } 00353 } 00354 } // end namespace mfplugins 00355 00356 //====================================================================== 00357 // 00358 // makePlugin function 00359 // 00360 //====================================================================== 00361 00362 #ifndef EXTERN_C_FUNC_DECLARE_START 00363 #define EXTERN_C_FUNC_DECLARE_START extern "C" { 00364 #endif 00365 00366 EXTERN_C_FUNC_DECLARE_START 00367 auto makePlugin(const std::string&, const fhicl::ParameterSet& pset) { 00368 return std::make_unique<mfplugins::ELUDP>(pset); 00369 } 00370 } 00371 00372 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)