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