1 #include "cetlib/PluginTypeDeducer.h"
2 #include "fhiclcpp/ParameterSet.h"
3 #include "fhiclcpp/types/ConfigurationTable.h"
5 #include "cetlib/compiler_macros.h"
6 #include "messagefacility/MessageLogger/MessageLogger.h"
7 #include "messagefacility/MessageService/ELdestination.h"
8 #include "messagefacility/Utilities/ELseverityLevel.h"
9 #include "messagefacility/Utilities/exception.h"
12 #include <arpa/inet.h>
15 #include <netinet/in.h>
23 #define TRACE_NAME "UDP_mfPlugin"
27 #include <boost/algorithm/string.hpp>
31 using mf::service::ELdestination;
37 class ELUDP :
public ELdestination
50 fhicl::Name{
"error_turnoff_threshold"},
51 fhicl::Comment{
"Number of errors before turning off destination (default: 0, don't turn off)"}, 0};
53 fhicl::Atom<int>
error_report = fhicl::Atom<int>{fhicl::Name{
"error_report_backoff_factor"},
54 fhicl::Comment{
"Print an error message every N errors"}, 100};
56 fhicl::Atom<std::string>
host =
57 fhicl::Atom<std::string>{fhicl::Name{
"host"}, fhicl::Comment{
"Address to send messages to"},
"227.128.12.27"};
59 fhicl::Atom<int>
port = fhicl::Atom<int>{fhicl::Name{
"port"}, fhicl::Comment{
"Port to send messages to"}, 5140};
62 fhicl::Name{
"multicast_enabled"}, fhicl::Comment{
"Whether messages should be sent via multicast"},
false};
66 fhicl::Name{
"multicast_interface_ip"},
67 fhicl::Comment{
"Use this hostname for multicast output(to assign to the proper NIC)"},
"0.0.0.0"};
70 fhicl::Atom<std::string>{fhicl::Name{
"filename_delimit"},
71 fhicl::Comment{
"Grab path after this. \"/srcs/\" /x/srcs/y/z.cc => y/z.cc. NOTE: only works if full filename is given to this plugin (based on which mf::<method> is used)."},
"/"};
88 void fillPrefix(std::ostringstream& o,
const ErrorObj& msg)
override;
95 void fillUsrMsg(std::ostringstream& o,
const ErrorObj& msg)
override;
100 void fillSuffix(std::ostringstream& ,
const ErrorObj& )
override {}
107 void routePayload(
const std::ostringstream& o,
const ErrorObj& e)
override;
113 int error_report_backoff_factor_;
117 bool multicast_enabled_;
118 std::string multicast_out_addr_;
121 struct sockaddr_in message_addr_;
124 int consecutive_success_count_;
126 int next_error_report_;
130 std::string hostname_;
131 std::string hostaddr_;
133 std::string filename_delimit_;
145 : ELdestination(pset().elDestConfig()), error_report_backoff_factor_(pset().error_report()), error_max_(pset().error_max()), host_(pset().host()), port_(pset().port()), multicast_enabled_(pset().multicast_enabled()), multicast_out_addr_(pset().output_address()), message_socket_(-1), consecutive_success_count_(0), error_count_(0), next_error_report_(1), seqNum_(0), pid_(static_cast<int64_t>(getpid())), filename_delimit_(pset().filename_delimit())
148 char hostname_c[1024];
149 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c :
"Unkonwn Host";
152 hostent* host =
nullptr;
153 host = gethostbyname(hostname_c);
158 char* ip = inet_ntoa(*reinterpret_cast<struct in_addr*>(host->h_addr));
164 struct ifaddrs* ifAddrStruct =
nullptr;
165 struct ifaddrs* ifa =
nullptr;
166 void* tmpAddrPtr =
nullptr;
168 if (getifaddrs(&ifAddrStruct) != 0)
171 hostaddr_ =
"127.0.0.1";
176 for (ifa = ifAddrStruct; ifa !=
nullptr; ifa = ifa->ifa_next)
178 if (ifa->ifa_addr->sa_family == AF_INET)
181 tmpAddrPtr = &(
reinterpret_cast<struct sockaddr_in*
>(ifa->ifa_addr)->sin_addr);
182 char addressBuffer[INET_ADDRSTRLEN];
183 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
184 hostaddr_ = addressBuffer;
187 else if (ifa->ifa_addr->sa_family == AF_INET6)
190 tmpAddrPtr = &(
reinterpret_cast<struct sockaddr_in6*
>(ifa->ifa_addr)->sin6_addr);
191 char addressBuffer[INET6_ADDRSTRLEN];
192 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
193 hostaddr_ = addressBuffer;
197 if (!hostaddr_.empty() && (hostaddr_ !=
"127.0.0.1") && (hostaddr_ !=
"::1"))
203 if (hostaddr_.empty())
205 hostaddr_ =
"127.0.0.1";
213 std::ostringstream pid_ostr;
214 pid_ostr <<
"/proc/" << pid_ <<
"/exe";
215 exe = realpath(pid_ostr.str().c_str(), NULL);
217 size_t end = exe.find(
'\0');
218 size_t start = exe.find_last_of(
'/', end);
220 app_ = exe.substr(start + 1, end - start - 1);
223 std::stringstream ss;
224 ss <<
"//proc//" << pid_ <<
"//cmdline";
225 std::ifstream procfile{ss.str().c_str()};
227 std::string procinfo;
229 if (procfile.is_open())
231 procfile >> procinfo;
235 size_t end = procinfo.find(
'\0');
236 size_t start = procinfo.find_last_of(
'/', end);
238 app_ = procinfo.substr(start + 1, end - start - 1);
242 void ELUDP::reconnect_()
244 static std::mutex mutex;
245 std::lock_guard<std::mutex> lk(mutex);
246 if (message_socket_ == -1)
248 message_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
249 if (message_socket_ < 0)
251 TLOG(TLVL_ERROR) <<
"I failed to create the socket for sending Data messages! err=" << strerror(errno);
254 int sts =
ResolveHost(host_.c_str(), port_, message_addr_);
257 TLOG(TLVL_ERROR) <<
"Unable to resolve Data message address, err=" << strerror(errno);
261 if (multicast_out_addr_ ==
"0.0.0.0")
263 multicast_out_addr_.reserve(HOST_NAME_MAX);
264 sts = gethostname(&multicast_out_addr_[0], HOST_NAME_MAX);
267 TLOG(TLVL_ERROR) <<
"Could not get current hostname, err=" << strerror(errno);
272 if (multicast_out_addr_ !=
"localhost")
279 TLOG(TLVL_ERROR) <<
"Unable to resolve multicast interface address, err=" << strerror(errno);
283 if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_IF, &addr,
sizeof(addr)) == -1)
285 TLOG(TLVL_ERROR) <<
"Cannot set outgoing interface, err=" << strerror(errno);
290 if (setsockopt(message_socket_, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(yes)) < 0)
292 TLOG(TLVL_ERROR) <<
"Unable to enable port reuse on message socket, err=" << strerror(errno);
295 if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_LOOP, &yes,
sizeof(yes)) < 0)
297 TLOG(TLVL_ERROR) <<
"Unable to enable multicast loopback on message socket, err=" << strerror(errno);
300 if (setsockopt(message_socket_, SOL_SOCKET, SO_BROADCAST, &yes,
sizeof(yes)) == -1)
302 TLOG(TLVL_ERROR) <<
"Cannot set message socket to broadcast, err=" << strerror(errno);
313 const auto& xid = msg.xid();
316 auto module = xid.module();
318 std::replace(
id.begin(),
id.end(),
'|',
'!');
319 std::replace(app.begin(), app.end(),
'|',
'!');
320 std::replace(module.begin(), module.end(),
'|',
'!');
322 oss << format_.timestamp(msg.timestamp()) <<
"|";
323 oss << std::to_string(++seqNum_) <<
"|";
324 oss << hostname_ <<
"|";
325 oss << hostaddr_ <<
"|";
326 oss << xid.severity().getName() <<
"|";
330 oss << mf::GetIteration() <<
"|";
332 oss << module <<
"|";
333 if (filename_delimit_.empty())
335 oss << msg.filename();
337 else if (filename_delimit_.size() == 1)
339 oss << (strrchr(&msg.filename()[0], filename_delimit_[0]) !=
nullptr
340 ? strrchr(&msg.filename()[0], filename_delimit_[0]) + 1
345 const char* cp = strstr(&msg.filename()[0], &filename_delimit_[0]);
349 cp += filename_delimit_.size() - 1;
350 while (*cp && *cp !=
'/') ++cp;
355 oss << msg.filename();
357 oss <<
"|" << std::to_string(msg.lineNumber()) <<
"|";
365 std::ostringstream tmposs;
367 for (
auto const& val : msg.items())
373 const std::string& usrMsg = tmposs.str().compare(0, 1,
"\n") == 0 ? tmposs.str().erase(0, 1) : tmposs.str();
383 if (message_socket_ == -1)
387 if (error_count_ < error_max_ || error_max_ == 0)
389 char str[INET_ADDRSTRLEN];
390 inet_ntop(AF_INET, &(message_addr_.sin_addr), str, INET_ADDRSTRLEN);
392 auto string =
"UDPMFMESSAGE" + std::to_string(pid_) +
"|" + oss.str();
393 auto sts = sendto(message_socket_,
string.c_str(),
string.size(), 0, reinterpret_cast<struct sockaddr*>(&message_addr_),
394 sizeof(message_addr_));
398 consecutive_success_count_ = 0;
400 if (error_count_ == next_error_report_)
402 TLOG(TLVL_ERROR) <<
"Error sending message " << seqNum_ <<
" to " << host_ <<
", errno=" << errno <<
" ("
403 << strerror(errno) <<
")";
404 next_error_report_ *= error_report_backoff_factor_;
409 ++consecutive_success_count_;
410 if (consecutive_success_count_ >= 5)
413 next_error_report_ = 1;
426 #ifndef EXTERN_C_FUNC_DECLARE_START
427 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
430 EXTERN_C_FUNC_DECLARE_START
431 auto makePlugin(
const std::string& ,
const fhicl::ParameterSet& pset)
433 return std::make_unique<mfplugins::ELUDP>(pset);
437 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
ELUDP(Parameters const &pset)
ELUDP Constructor
int ResolveHost(char const *host_in, in_addr &addr)
Convert a string hostname to a in_addr suitable for socket communication.
Message Facility UDP Streamer Destination Formats messages into a delimited string and sends via UDP ...
void fillPrefix(std::ostringstream &o, const ErrorObj &msg) override
Fill the "Prefix" portion of the message.
int GetInterfaceForNetwork(char const *host_in, in_addr &addr)
Convert an IP address to the network address of the interface sharing the subnet mask.
fhicl::Atom< int > error_report
"error_report_backoff_factor" (Default: 100): Print an error message every N errors ...
fhicl::Atom< int > port
"port" (Default: 5140): Port to send messages to
fhicl::Atom< std::string > host
"host" (Default: "227.128.12.27"): Address to send messages to
fhicl::Atom< std::string > output_address
Configuration Parameters for ELUDP.
void routePayload(const std::ostringstream &o, const ErrorObj &e) override
Serialize a MessageFacility message to the output.
fhicl::WrappedTable< Config > Parameters
Used for ParameterSet validation.
fhicl::Atom< int > error_max
fhicl::TableFragment< ELdestination::Config > elDestConfig
ELDestination common config parameters.
void fillUsrMsg(std::ostringstream &o, const ErrorObj &msg) override
Fill the "User Message" portion of the message.
fhicl::Atom< std::string > filename_delimit
filename_delimit (Default: "/"): Grab path after this. "/srcs/" /x/srcs/y/z.cc => y/z...
void fillSuffix(std::ostringstream &, const ErrorObj &) override
Fill the "Suffix" portion of the message (Unused)
fhicl::Atom< bool > multicast_enabled
"multicast_enabled" (Default: false): Whether messages should be sent via multicast ...