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