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