artdaq_mfextensions  v1_03_00
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 < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
7 # include "messagefacility/MessageService/ELcontextSupplier.h"
8 # include "messagefacility/MessageLogger/MessageDrop.h"
9 #elif MESSAGEFACILITY_HEX_VERSION < 0x20201 // v2_02_01 is s67
10 # include "messagefacility/MessageService/MessageDrop.h"
11 #else
12 # include "messagefacility/MessageLogger/MessageLogger.h"
13 #endif
14 #include "messagefacility/Utilities/exception.h"
15 #include "cetlib/compiler_macros.h"
16 
17 // C/C++ includes
18 #include <iostream>
19 #include <fstream>
20 #include <memory>
21 #include <algorithm>
22 #include <arpa/inet.h>
23 #include <ifaddrs.h>
24 #include <netdb.h>
25 #include <netinet/in.h>
26 
27 
28 // Boost includes
29 #include <boost/asio.hpp>
30 #include <boost/algorithm/string.hpp>
31 
32 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // format changed to format_ for s67
33 #define format_ format
34 #endif
35 
36 namespace mfplugins
37 {
38  using mf::service::ELdestination;
39  using mf::ELseverityLevel;
40  using mf::ErrorObj;
41 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
42  using mf::service::ELcontextSupplier;
43 # endif
44  using boost::asio::ip::udp;
45 
50  class ELUDP : public ELdestination
51  {
52 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
53  struct Config
54  {
55  fhicl::TableFragment<ELdestination::Config> elDestConfig;
56  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 };
57  fhicl::Atom<int> error_report{ fhicl::Name{ "error_report_backoff_factor" },fhicl::Comment{ "Print an error message every N errors" },100 };
58  fhicl::Atom<std::string> host{ fhicl::Name{ "host" },fhicl::Comment{ "Address to send messages to" }, "227.128.12.27" };
59  fhicl::Atom<int> port{ fhicl::Name{ "port" },fhicl::Comment{ "Port to send messages to" },5140 };
60  };
61  using Parameters = fhicl::WrappedTable<Config>;
62 #endif
63  public:
64 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
65  ELUDP(const fhicl::ParameterSet& pset);
66 #else
67  ELUDP(Parameters const& pset);
68 #endif
69 
70  virtual void fillPrefix(std::ostringstream&, const ErrorObj&
71 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
72  , const ELcontextSupplier&
73 # endif
74  ) override;
75 
76  virtual void fillUsrMsg(std::ostringstream&, const ErrorObj&) override;
77 
78  virtual void fillSuffix(std::ostringstream&, const ErrorObj&) override {}
79 
80  virtual void routePayload(const std::ostringstream&, const ErrorObj&
81 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
82  , const ELcontextSupplier&
83 # endif
84  ) override;
85 
86  private:
87  void reconnect_(bool quiet = false);
88 
89  // Parameters
90  int error_report_backoff_factor_;
91  int error_max_;
92  std::string host_;
93  int port_;
94 
95  // Other stuff
96  boost::asio::io_service io_service_;
97  udp::socket socket_;
98  udp::endpoint remote_endpoint_;
99  int consecutive_success_count_;
100  int error_count_;
101  int next_error_report_;
102  int seqNum_;
103 
104 
105  long pid_;
106  std::string hostname_;
107  std::string hostaddr_;
108  std::string app_;
109  };
110 
111  // END DECLARATION
112  //======================================================================
113  // BEGIN IMPLEMENTATION
114 
115 
116  //======================================================================
117  // ELUDP c'tor
118  //======================================================================
119 
120 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
121  ELUDP::ELUDP(const fhicl::ParameterSet& pset)
122  : ELdestination(pset)
123  , error_report_backoff_factor_(pset.get<int>("error_report_backoff_factor", 100))
124  , error_max_(pset.get<int>("error_turnoff_threshold", 0))
125  , host_(pset.get<std::string>("host", "227.128.12.27"))
126  , port_(pset.get<int>("port", 5140))
127 #else
128  ELUDP::ELUDP(Parameters const& pset)
129  : ELdestination(pset().elDestConfig())
130  , error_report_backoff_factor_(pset().error_report())
131  , error_max_(pset().error_max())
132  , host_(pset().host())
133  , port_(pset().port())
134 #endif
135  , io_service_()
136  , socket_(io_service_)
137  , remote_endpoint_()
138  , consecutive_success_count_(0)
139  , error_count_(0)
140  , next_error_report_(1)
141  , seqNum_(0)
142  , pid_(static_cast<long>(getpid()))
143  {
144  reconnect_();
145 
146  // hostname
147  char hostname_c[1024];
148  hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
149 
150  // host ip address
151  hostent* host = nullptr;
152  host = gethostbyname(hostname_c);
153 
154  if (host != nullptr)
155  {
156  // ip address from hostname if the entry exists in /etc/hosts
157  char* ip = inet_ntoa(*(struct in_addr *)host->h_addr);
158  hostaddr_ = ip;
159  }
160  else
161  {
162  // enumerate all network interfaces
163  struct ifaddrs* ifAddrStruct = nullptr;
164  struct ifaddrs* ifa = nullptr;
165  void* tmpAddrPtr = nullptr;
166 
167  if (getifaddrs(&ifAddrStruct))
168  {
169  // failed to get addr struct
170  hostaddr_ = "127.0.0.1";
171  }
172  else
173  {
174  // iterate through all interfaces
175  for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
176  {
177  if (ifa->ifa_addr->sa_family == AF_INET)
178  {
179  // a valid IPv4 addres
180  tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
181  char addressBuffer[INET_ADDRSTRLEN];
182  inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
183  hostaddr_ = addressBuffer;
184  }
185 
186  else if (ifa->ifa_addr->sa_family == AF_INET6)
187  {
188  // a valid IPv6 address
189  tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
190  char addressBuffer[INET6_ADDRSTRLEN];
191  inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
192  hostaddr_ = addressBuffer;
193  }
194 
195  // find first non-local address
196  if (!hostaddr_.empty()
197  && hostaddr_.compare("127.0.0.1")
198  && hostaddr_.compare("::1"))
199  break;
200  }
201 
202  if (hostaddr_.empty()) // failed to find anything
203  hostaddr_ = "127.0.0.1";
204  }
205  }
206 
207 #if 0
208  // get process name from '/proc/pid/exe'
209  std::string exe;
210  std::ostringstream pid_ostr;
211  pid_ostr << "/proc/" << pid_ << "/exe";
212  exe = realpath(pid_ostr.str().c_str(), NULL);
213 
214  size_t end = exe.find('\0');
215  size_t start = exe.find_last_of('/', end);
216 
217  app_ = exe.substr(start + 1, end - start - 1);
218 #else
219  // get process name from '/proc/pid/cmdline'
220  std::stringstream ss;
221  ss << "//proc//" << pid_ << "//cmdline";
222  std::ifstream procfile{ ss.str().c_str() };
223 
224  std::string procinfo;
225 
226  if (procfile.is_open())
227  {
228  procfile >> procinfo;
229  procfile.close();
230  }
231 
232  size_t end = procinfo.find('\0');
233  size_t start = procinfo.find_last_of('/', end);
234 
235  app_ = procinfo.substr(start + 1, end - start - 1);
236 #endif
237  }
238 
239  void ELUDP::reconnect_(bool quiet)
240  {
241  boost::system::error_code ec;
242  socket_.open(udp::v4());
243  socket_.set_option(boost::asio::socket_base::reuse_address(true), ec);
244  if (ec && !quiet)
245  {
246  std::cerr << "An error occurred setting reuse_address to true: "
247  << ec.message() << std::endl;
248  }
249 
250  if (boost::iequals(host_, "Broadcast") || host_ == "255.255.255.255")
251  {
252  socket_.set_option(boost::asio::socket_base::broadcast(true), ec);
253  remote_endpoint_ = udp::endpoint(boost::asio::ip::address_v4::broadcast(), port_);
254  }
255  else
256  {
257  udp::resolver resolver(io_service_);
258  udp::resolver::query query(udp::v4(), host_, std::to_string(port_));
259  remote_endpoint_ = *resolver.resolve(query);
260  }
261 
262  socket_.connect(remote_endpoint_, ec);
263  if (ec)
264  {
265  if (!quiet)
266  {
267  std::cerr << "An Error occurred in connect(): " << ec.message() << std::endl
268  << " endpoint = " << remote_endpoint_ << std::endl;
269  }
270  error_count_++;
271  }
272  //else {
273  // std::cout << "Successfully connected to remote endpoint = " << remote_endpoint_
274  // << std::endl;
275  //}
276  }
277 
278  //======================================================================
279  // Message prefix filler ( overriddes ELdestination::fillPrefix )
280  //======================================================================
281  void ELUDP::fillPrefix(std::ostringstream& oss, const ErrorObj& msg
282 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
283  , ELcontextSupplier const&
284 # endif
285  )
286  {
287  const auto& xid = msg.xid();
288 
289 # if MESSAGEFACILITY_HEX_VERSION >= 0x20002 // an indication of a switch from s48 to s50
290  auto id = xid.id();
291  auto module = xid.module();
292  auto app = app_;
293 # else
294  auto id = xid.id;
295  auto app = xid.application;
296  auto process = xid.process;
297  auto module = xid.module;
298 # endif
299  std::replace(id.begin(), id.end(), '|', '!');
300  std::replace(app.begin(), app.end(), '|', '!');
301 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
302  std::replace(process.begin(), process.end(), '|', '!');
303 # endif
304  std::replace(module.begin(), module.end(), '|', '!');
305 
306  oss << format_.timestamp(msg.timestamp()) << "|"; // timestamp
307  oss << std::to_string(++seqNum_) << "|"; // sequence number
308 # if MESSAGEFACILITY_HEX_VERSION >= 0x20002 // an indication of a switch from s48 to s50
309  oss << hostname_ << "|"; // host name
310  oss << hostaddr_ << "|"; // host address
311  oss << xid.severity().getName() << "|"; // severity
312 # else
313  oss << xid.hostname << "|"; // host name
314  oss << xid.hostaddr << "|"; // host address
315  oss << xid.severity.getName() << "|"; // severity
316 # endif
317  oss << id << "|"; // category
318  oss << app << "|"; // application
319 # if MESSAGEFACILITY_HEX_VERSION >= 0x20201 // an indication of s67
320  oss << pid_ << "|";
321  oss << mf::GetIteration() << "|"; // run/event no
322 # elif MESSAGEFACILITY_HEX_VERSION >= 0x20002 // an indication of a switch from s48 to s50
323  oss << pid_ << "|"; // process id
324  oss << mf::MessageDrop::instance()->iteration << "|"; // run/event no
325 # else
326  oss << process << "|";
327  oss << xid.pid << "|"; // process id
328  oss << mf::MessageDrop::instance()->runEvent << "|"; // run/event no
329 # endif
330  oss << module << "|"; // module name
331 #if MESSAGEFACILITY_HEX_VERSION >= 0x20201
332  oss << msg.filename() << "|" << std::to_string(msg.lineNumber()) << "|";
333 #endif
334  }
335 
336  //======================================================================
337  // Message filler ( overriddes ELdestination::fillUsrMsg )
338  //======================================================================
339  void ELUDP::fillUsrMsg(std::ostringstream& oss, const ErrorObj& msg)
340  {
341  std::ostringstream tmposs;
342  // Print the contents.
343  for (auto const& val : msg.items()) {
344  tmposs << val;
345  }
346 
347  // remove leading "\n" if present
348  const std::string& usrMsg = !tmposs.str().compare(0, 1, "\n") ? tmposs.str().erase(0, 1) : tmposs.str();
349 
350  oss << usrMsg;
351  }
352 
353  //======================================================================
354  // Message router ( overriddes ELdestination::routePayload )
355  //======================================================================
356  void ELUDP::routePayload(const std::ostringstream& oss
357 #if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
358  , const ErrorObj& msg, ELcontextSupplier const&
359 #else
360  , const ErrorObj&
361 #endif
362  )
363  {
364  if (error_count_ < error_max_ || error_max_ == 0)
365  {
366 # if MESSAGEFACILITY_HEX_VERSION >= 0x20002 // an indication of a switch from s48 to s50
367  auto pid = pid_;
368 # else
369  auto pid = msg.xid().pid;
370 # endif
371  //std::cout << oss.str() << std::endl;
372  auto string = "UDPMFMESSAGE" + std::to_string(pid) + "|" + oss.str();
373  //std::cout << string << std::endl;
374  auto message = boost::asio::buffer(string);
375  bool error = true;
376  try
377  {
378  socket_.send_to(message, remote_endpoint_);
379  ++consecutive_success_count_;
380  if (consecutive_success_count_ >= 5)
381  {
382  error_count_ = 0;
383  next_error_report_ = 1;
384  }
385  error = false;
386  }
387  catch (boost::system::system_error& err)
388  {
389  consecutive_success_count_ = 0;
390  ++error_count_;
391  if (error_count_ == next_error_report_)
392  {
393  std::cerr << "An exception occurred when trying to send message " << std::to_string(seqNum_) << " to "
394  << remote_endpoint_ << std::endl
395  << " message = " << oss.str() << std::endl
396  << " exception = " << err.what() << std::endl;
397  next_error_report_ *= error_report_backoff_factor_;
398  }
399  }
400  if (error)
401  {
402  try
403  {
404  reconnect_(true);
405  }
406  catch (...) {}
407  }
408  }
409  }
410 } // end namespace mfplugins
411 
412 //======================================================================
413 //
414 // makePlugin function
415 //
416 //======================================================================
417 
418 #ifndef EXTERN_C_FUNC_DECLARE_START
419 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
420 #endif
421 
422 EXTERN_C_FUNC_DECLARE_START
423  auto makePlugin(const std::string&,
424  const fhicl::ParameterSet& pset)
425  {
426  return std::make_unique<mfplugins::ELUDP>(pset);
427  }
428 }
429 
430 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
Message Facility UDP Streamer Destination Formats messages into a delimited string and sends via UDP ...
Definition: UDP_mfPlugin.cc:50