artdaq_mfextensions  v1_03_02
UDP_mfPlugin.cc
1 #include "cetlib/PluginTypeDeducer.h"
2 #include "fhiclcpp/ParameterSet.h"
3 
4 #include "messagefacility/MessageService/ELdestination.h"
5 #include "messagefacility/Utilities/ELseverityLevel.h"
6 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // v2_02_01 is s67
7 # include "messagefacility/MessageService/MessageDrop.h"
8 #else
9 # include "messagefacility/MessageLogger/MessageLogger.h"
10 #endif
11 #include "messagefacility/Utilities/exception.h"
12 #include "cetlib/compiler_macros.h"
13 
14 // C/C++ includes
15 #include <iostream>
16 #include <fstream>
17 #include <memory>
18 #include <algorithm>
19 #include <arpa/inet.h>
20 #include <ifaddrs.h>
21 #include <netdb.h>
22 #include <netinet/in.h>
24 
25 #define TRACE_NAME "UDP_mfPlugin"
26 #include "trace.h"
27 
28 // Boost includes
29 #include <boost/algorithm/string.hpp>
30 
31 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // format changed to format_ for s67
32 #define format_ format
33 #endif
34 
35 namespace mfplugins
36 {
37  using mf::service::ELdestination;
38  using mf::ELseverityLevel;
39  using mf::ErrorObj;
40 
45  class ELUDP : public ELdestination
46  {
47 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
48  struct Config
49  {
50  fhicl::TableFragment<ELdestination::Config> elDestConfig;
51  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 };
52  fhicl::Atom<int> error_report{ fhicl::Name{ "error_report_backoff_factor" },fhicl::Comment{ "Print an error message every N errors" },100 };
53  fhicl::Atom<std::string> host{ fhicl::Name{ "host" },fhicl::Comment{ "Address to send messages to" }, "227.128.12.27" };
54  fhicl::Atom<int> port{ fhicl::Name{ "port" },fhicl::Comment{ "Port to send messages to" },5140 };
55  fhicl::Atom<bool> multicast_enabled{ fhicl::Name{"multicast_enabled"},fhicl::Comment{"Whether messages should be sent via multicast"}, false };
57  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" };
58  };
59  using Parameters = fhicl::WrappedTable<Config>;
60 #endif
61  public:
66 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
67  ELUDP(const fhicl::ParameterSet& pset);
68 #else
69  ELUDP(Parameters const& pset);
70 #endif
71 
77  virtual void fillPrefix(std::ostringstream& o, const ErrorObj& e) override;
78 
84  virtual void fillUsrMsg(std::ostringstream& o, const ErrorObj& e) override;
85 
89  virtual void fillSuffix(std::ostringstream&, const ErrorObj&) override {}
90 
96  virtual void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
97 
98  private:
99  void reconnect_();
100 
101  // Parameters
102  int error_report_backoff_factor_;
103  int error_max_;
104  std::string host_;
105  int port_;
106  bool multicast_enabled_;
107  std::string multicast_out_addr_;
108 
109  int message_socket_;
110  struct sockaddr_in message_addr_;
111 
112  // Other stuff
113  int consecutive_success_count_;
114  int error_count_;
115  int next_error_report_;
116  int seqNum_;
117 
118 
119  long pid_;
120  std::string hostname_;
121  std::string hostaddr_;
122  std::string app_;
123  };
124 
125  // END DECLARATION
126  //======================================================================
127  // BEGIN IMPLEMENTATION
128 
129 
130  //======================================================================
131  // ELUDP c'tor
132  //======================================================================
133 
134 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
135  ELUDP::ELUDP(const fhicl::ParameterSet& pset)
136  : ELdestination(pset)
137  , error_report_backoff_factor_(pset.get<int>("error_report_backoff_factor", 100))
138  , error_max_(pset.get<int>("error_turnoff_threshold", 0))
139  , host_(pset.get<std::string>("host", "227.128.12.27"))
140  , port_(pset.get<int>("port", 5140))
141  , multicast_enabled_(pset.get<bool>("multicast_enabled", false))
142  , multicast_out_addr_(pset.get<std::string>("multicast_interface_ip", pset.get<std::string>("output_address", "0.0.0.0")))
143 #else
144  ELUDP::ELUDP(Parameters const& pset)
145  : ELdestination(pset().elDestConfig())
146  , error_report_backoff_factor_(pset().error_report())
147  , error_max_(pset().error_max())
148  , host_(pset().host())
149  , port_(pset().port())
150  , multicast_enabled_(pset().multicast_enabled())
151  , multicast_out_addr_(pset().output_address())
152 #endif
153  , message_socket_(-1)
154  , consecutive_success_count_(0)
155  , error_count_(0)
156  , next_error_report_(1)
157  , seqNum_(0)
158  , pid_(static_cast<long>(getpid()))
159  {
160  // hostname
161  char hostname_c[1024];
162  hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
163 
164  // host ip address
165  hostent* host = nullptr;
166  host = gethostbyname(hostname_c);
167 
168  if (host != nullptr)
169  {
170  // ip address from hostname if the entry exists in /etc/hosts
171  char* ip = inet_ntoa(*(struct in_addr *)host->h_addr);
172  hostaddr_ = ip;
173  }
174  else
175  {
176  // enumerate all network interfaces
177  struct ifaddrs* ifAddrStruct = nullptr;
178  struct ifaddrs* ifa = nullptr;
179  void* tmpAddrPtr = nullptr;
180 
181  if (getifaddrs(&ifAddrStruct))
182  {
183  // failed to get addr struct
184  hostaddr_ = "127.0.0.1";
185  }
186  else
187  {
188  // iterate through all interfaces
189  for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
190  {
191  if (ifa->ifa_addr->sa_family == AF_INET)
192  {
193  // a valid IPv4 addres
194  tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
195  char addressBuffer[INET_ADDRSTRLEN];
196  inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
197  hostaddr_ = addressBuffer;
198  }
199 
200  else if (ifa->ifa_addr->sa_family == AF_INET6)
201  {
202  // a valid IPv6 address
203  tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
204  char addressBuffer[INET6_ADDRSTRLEN];
205  inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
206  hostaddr_ = addressBuffer;
207  }
208 
209  // find first non-local address
210  if (!hostaddr_.empty()
211  && hostaddr_.compare("127.0.0.1")
212  && hostaddr_.compare("::1"))
213  break;
214  }
215 
216  if (hostaddr_.empty()) // failed to find anything
217  hostaddr_ = "127.0.0.1";
218  }
219  }
220 
221 #if 0
222  // get process name from '/proc/pid/exe'
223  std::string exe;
224  std::ostringstream pid_ostr;
225  pid_ostr << "/proc/" << pid_ << "/exe";
226  exe = realpath(pid_ostr.str().c_str(), NULL);
227 
228  size_t end = exe.find('\0');
229  size_t start = exe.find_last_of('/', end);
230 
231  app_ = exe.substr(start + 1, end - start - 1);
232 #else
233  // get process name from '/proc/pid/cmdline'
234  std::stringstream ss;
235  ss << "//proc//" << pid_ << "//cmdline";
236  std::ifstream procfile{ ss.str().c_str() };
237 
238  std::string procinfo;
239 
240  if (procfile.is_open())
241  {
242  procfile >> procinfo;
243  procfile.close();
244  }
245 
246  size_t end = procinfo.find('\0');
247  size_t start = procinfo.find_last_of('/', end);
248 
249  app_ = procinfo.substr(start + 1, end - start - 1);
250 #endif
251  }
252 
253  void ELUDP::reconnect_()
254  {
255  message_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
256  if (message_socket_ < 0)
257  {
258  TLOG(TLVL_ERROR) << "I failed to create the socket for sending Data messages! err=" << strerror(errno);
259  exit(1);
260  }
261  int sts = ResolveHost(host_.c_str(), port_, message_addr_);
262  if (sts == -1)
263  {
264  TLOG(TLVL_ERROR) << "Unable to resolve Data message address, err=" << strerror(errno);
265  exit(1);
266  }
267 
268  if (multicast_out_addr_ == "0.0.0.0")
269  {
270  multicast_out_addr_.reserve(HOST_NAME_MAX);
271  sts = gethostname(&multicast_out_addr_[0], HOST_NAME_MAX);
272  if (sts < 0)
273  {
274  TLOG(TLVL_ERROR) << "Could not get current hostname, err=" << strerror(errno);
275  exit(1);
276  }
277  }
278 
279  if (multicast_out_addr_ != "localhost")
280  {
281  struct in_addr addr;
282  sts = GetInterfaceForNetwork(multicast_out_addr_.c_str(), addr);
283  //sts = ResolveHost(multicast_out_addr_.c_str(), addr);
284  if (sts == -1)
285  {
286  TLOG(TLVL_ERROR) << "Unable to resolve multicast interface address, err=" << strerror(errno);
287  exit(1);
288  }
289 
290  if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) == -1)
291  {
292  TLOG(TLVL_ERROR) << "Cannot set outgoing interface, err=" << strerror(errno);
293  exit(1);
294  }
295  }
296  int yes = 1;
297  if (setsockopt(message_socket_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
298  {
299  TLOG(TLVL_ERROR) << "Unable to enable port reuse on message socket, err=" << strerror(errno);
300  exit(1);
301  }
302  if (setsockopt(message_socket_, IPPROTO_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0)
303  {
304  TLOG(TLVL_ERROR) << "Unable to enable multicast loopback on message socket, err=" << strerror(errno);
305  exit(1);
306  }
307  if (setsockopt(message_socket_, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(int)) == -1)
308  {
309  TLOG(TLVL_ERROR) << "Cannot set message socket to broadcast, err=" << strerror(errno);
310  exit(1);
311  }
312  }
313 
314  //======================================================================
315  // Message prefix filler ( overriddes ELdestination::fillPrefix )
316  //======================================================================
317  void ELUDP::fillPrefix(std::ostringstream& oss, const ErrorObj& msg)
318  {
319  const auto& xid = msg.xid();
320 
321  auto id = xid.id();
322  auto module = xid.module();
323  auto app = app_;
324  std::replace(id.begin(), id.end(), '|', '!');
325  std::replace(app.begin(), app.end(), '|', '!');
326  std::replace(module.begin(), module.end(), '|', '!');
327 
328  oss << format_.timestamp(msg.timestamp()) << "|"; // timestamp
329  oss << std::to_string(++seqNum_) << "|"; // sequence number
330  oss << hostname_ << "|"; // host name
331  oss << hostaddr_ << "|"; // host address
332  oss << xid.severity().getName() << "|"; // severity
333  oss << id << "|"; // category
334  oss << app << "|"; // application
335 # if MESSAGEFACILITY_HEX_VERSION >= 0x20201 // an indication of s67
336  oss << pid_ << "|";
337  oss << mf::GetIteration() << "|"; // run/event no
338 #else
339  oss << pid_ << "|"; // process id
340  oss << mf::MessageDrop::instance()->iteration << "|"; // run/event no
341 #endif
342  oss << module << "|"; // module name
343 #if MESSAGEFACILITY_HEX_VERSION >= 0x20201
344  oss << msg.filename() << "|" << std::to_string(msg.lineNumber()) << "|";
345 #endif
346  }
347 
348  //======================================================================
349  // Message filler ( overriddes ELdestination::fillUsrMsg )
350  //======================================================================
351  void ELUDP::fillUsrMsg(std::ostringstream& oss, const ErrorObj& msg)
352  {
353  std::ostringstream tmposs;
354  // Print the contents.
355  for (auto const& val : msg.items())
356  {
357  tmposs << val;
358  }
359 
360  // remove leading "\n" if present
361  const std::string& usrMsg = !tmposs.str().compare(0, 1, "\n") ? tmposs.str().erase(0, 1) : tmposs.str();
362 
363  oss << usrMsg;
364  }
365 
366  //======================================================================
367  // Message router ( overriddes ELdestination::routePayload )
368  //======================================================================
369  void ELUDP::routePayload(const std::ostringstream& oss, const ErrorObj&)
370  {
371  if (message_socket_ == -1) reconnect_();
372  if (error_count_ < error_max_ || error_max_ == 0)
373  {
374  char str[INET_ADDRSTRLEN];
375  inet_ntop(AF_INET, &(message_addr_.sin_addr), str, INET_ADDRSTRLEN);
376 
377  auto string = "UDPMFMESSAGE" + std::to_string(pid_) + "|" + oss.str();
378  auto sts = sendto(message_socket_, string.c_str(), string.size(), 0, (struct sockaddr *)&message_addr_, sizeof(message_addr_));
379 
380  if (sts < 0)
381  {
382  consecutive_success_count_ = 0;
383  ++error_count_;
384  if (error_count_ == next_error_report_)
385  {
386  TLOG(TLVL_ERROR) << "Error sending message " << seqNum_ << " to " << host_ << ", errno=" << errno << " (" << strerror(errno) << ")";
387  next_error_report_ *= error_report_backoff_factor_;
388  }
389  }
390  else
391  {
392  ++consecutive_success_count_;
393  if (consecutive_success_count_ >= 5)
394  {
395  error_count_ = 0;
396  next_error_report_ = 1;
397  }
398  }
399  }
400  }
401 } // end namespace mfplugins
402 
403 //======================================================================
404 //
405 // makePlugin function
406 //
407 //======================================================================
408 
409 #ifndef EXTERN_C_FUNC_DECLARE_START
410 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
411 #endif
412 
413 EXTERN_C_FUNC_DECLARE_START
414 auto makePlugin(const std::string&,
415  const fhicl::ParameterSet& pset)
416 {
417  return std::make_unique<mfplugins::ELUDP>(pset);
418 }
419 }
420 
421 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
int ResolveHost(char const *host_in, in_addr &addr)
Convert a string hostname to a in_addr suitable for socket communication.
Definition: TCPConnect.hh:42
Message Facility UDP Streamer Destination Formats messages into a delimited string and sends via UDP ...
Definition: UDP_mfPlugin.cc:45
int GetInterfaceForNetwork(char const *host_in, in_addr &addr)
Convert an IP address to the network address of the interface sharing the subnet mask.
Definition: TCPConnect.hh:90
ELUDP(const fhicl::ParameterSet &pset)
ELUDP Constructor
virtual void routePayload(const std::ostringstream &o, const ErrorObj &e) override
Serialize a MessageFacility message to the output.
virtual void fillUsrMsg(std::ostringstream &o, const ErrorObj &e) override
Fill the &quot;User Message&quot; portion of the message.
virtual void fillPrefix(std::ostringstream &o, const ErrorObj &e) override
Fill the &quot;Prefix&quot; portion of the message.
virtual void fillSuffix(std::ostringstream &, const ErrorObj &) override
Fill the &quot;Suffix&quot; portion of the message (Unused)
Definition: UDP_mfPlugin.cc:89