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