artdaq_mfextensions  v1_05_00
UDP_receiver.cc
1 #define TRACE_NAME "UDP_Receiver"
2 
3 #include "mfextensions/Receivers/UDP_receiver.hh"
4 #include <sys/poll.h>
5 #include <sstream>
6 #include "messagefacility/Utilities/ELseverityLevel.h"
7 #include "mfextensions/Receivers/ReceiverMacros.hh"
9 
10 mfviewer::UDPReceiver::UDPReceiver(fhicl::ParameterSet const& pset)
11  : MVReceiver(pset)
12  , message_port_(pset.get<int>("port", 5140))
13  , message_addr_(pset.get<std::string>("message_address", "227.128.12.27"))
14  , multicast_enable_(pset.get<bool>("multicast_enable", false))
15  , multicast_out_addr_(pset.get<std::string>("multicast_interface_ip", "0.0.0.0"))
16  , message_socket_(-1)
17 {
18  TLOG(TLVL_TRACE) << "UDPReceiver Constructor";
19 }
20 
21 void mfviewer::UDPReceiver::setupMessageListener_()
22 {
23  TLOG(TLVL_INFO) << "Setting up message listen socket, address=" << message_addr_ << ":" << message_port_;
24  message_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
25  if (message_socket_ < 0)
26  {
27  TLOG(TLVL_ERROR) << "Error creating socket for receiving messages! err=" << strerror(errno);
28  exit(1);
29  }
30 
31  struct sockaddr_in si_me_request;
32 
33  int yes = 1;
34  if (setsockopt(message_socket_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
35  {
36  TLOG(TLVL_ERROR) << "Unable to enable port reuse on message socket, err=" << strerror(errno);
37  exit(1);
38  }
39  memset(&si_me_request, 0, sizeof(si_me_request));
40  si_me_request.sin_family = AF_INET;
41  si_me_request.sin_port = htons(message_port_);
42  si_me_request.sin_addr.s_addr = htonl(INADDR_ANY);
43  if (bind(message_socket_, reinterpret_cast<struct sockaddr*>(&si_me_request), sizeof(si_me_request)) == -1) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
44  {
45  TLOG(TLVL_ERROR) << "Cannot bind message socket to port " << message_port_ << ", err=" << strerror(errno);
46  exit(1);
47  }
48 
49  if (message_addr_ != "localhost" && multicast_enable_)
50  {
51  struct ip_mreq mreq;
52  int sts = ResolveHost(message_addr_.c_str(), mreq.imr_multiaddr);
53  if (sts == -1)
54  {
55  TLOG(TLVL_ERROR) << "Unable to resolve multicast message address, err=" << strerror(errno);
56  exit(1);
57  }
58  sts = GetInterfaceForNetwork(multicast_out_addr_.c_str(), mreq.imr_interface);
59  if (sts == -1)
60  {
61  TLOG(TLVL_ERROR) << "Unable to resolve hostname for " << multicast_out_addr_;
62  exit(1);
63  }
64  if (setsockopt(message_socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
65  {
66  TLOG(TLVL_ERROR) << "Unable to join multicast group, err=" << strerror(errno);
67  exit(1);
68  }
69  }
70  TLOG(TLVL_INFO) << "Done setting up message receive socket";
71 }
72 
74 {
75  TLOG(TLVL_DEBUG) << "Closing message receive socket";
76  close(message_socket_);
77  message_socket_ = -1;
78 }
79 
81 {
82  while (!stopRequested_)
83  {
84  if (message_socket_ == -1) setupMessageListener_();
85 
86  int ms_to_wait = 10;
87  struct pollfd ufds[1];
88  ufds[0].fd = message_socket_;
89  ufds[0].events = POLLIN | POLLPRI | POLLERR;
90  int rv = poll(ufds, 1, ms_to_wait);
91 
92  // Continue loop if no message received or message does not have correct event ID
93  if (rv <= 0 || (ufds[0].revents != POLLIN && ufds[0].revents != POLLPRI))
94  {
95  if (rv == 1 && (ufds[0].revents & (POLLNVAL | POLLERR | POLLHUP)))
96  {
97  close(message_socket_);
98  message_socket_ = -1;
99  }
100  if (stopRequested_)
101  {
102  break;
103  }
104  continue;
105  }
106 
107  char buffer[TRACE_STREAMER_MSGMAX + 1];
108  auto packetSize = read(message_socket_, &buffer, TRACE_STREAMER_MSGMAX);
109  if (packetSize < 0)
110  {
111  TLOG(TLVL_ERROR) << "Error receiving message, errno=" << errno << " (" << strerror(errno) << ")";
112  }
113  else
114  {
115  TLOG(TLVL_TRACE) << "Recieved message; validating...(packetSize=" << packetSize << ")";
116  std::string message(buffer, buffer + packetSize); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
117  if (validate_packet(message))
118  {
119  TLOG(TLVL_TRACE) << "Valid UDP Message received! Sending to GUI!";
120  emit NewMessage(read_msg(message));
121  }
122  }
123  }
124  TLOG(TLVL_INFO) << "UDPReceiver shutting down!";
125 }
126 
127 std::list<std::string> mfviewer::UDPReceiver::tokenize_(std::string const& input)
128 {
129  size_t pos = 0;
130  std::list<std::string> output;
131 
132  while (pos != std::string::npos && pos < input.size())
133  {
134  auto newpos = input.find('|', pos);
135  if (newpos != std::string::npos)
136  {
137  output.emplace_back(input, pos, newpos - pos);
138  //TLOG(TLVL_TRACE) << "tokenize_: " << output.back();
139  pos = newpos + 1;
140  }
141  else
142  {
143  output.emplace_back(input, pos);
144  //TLOG(TLVL_TRACE) << "tokenize_: " << output.back();
145  pos = newpos;
146  }
147  }
148  return output;
149 }
150 
151 msg_ptr_t mfviewer::UDPReceiver::read_msg(std::string const& input)
152 {
153  std::string hostname, category, application, message, hostaddr, file, line, module, eventID;
154  mf::ELseverityLevel sev;
155  timeval tv = {0, 0};
156  int pid = 0;
157  int seqNum = 0;
158 
159  TLOG(TLVL_TRACE) << "Recieved MF/Syslog message with contents: " << input;
160 
161  auto tokens = tokenize_(input);
162  auto it = tokens.begin();
163 
164  if (it != tokens.end())
165  {
166  bool timestamp_found = false;
167  struct tm tm;
168  time_t t;
169  while (it != tokens.end() && !timestamp_found)
170  {
171  std::string thisString = *it;
172  while (!thisString.empty() && !timestamp_found)
173  {
174  auto pos = thisString.find_first_of("0123456789");
175  if (pos != std::string::npos)
176  {
177  thisString = thisString.erase(0, pos);
178  //TLOG(TLVL_TRACE) << "thisString: " << thisString;
179 
180  if (strptime(thisString.c_str(), "%d-%b-%Y %H:%M:%S", &tm) != nullptr)
181  {
182  timestamp_found = true;
183  break;
184  }
185 
186  if (!thisString.empty() )
187  thisString = thisString.erase(0, 1);
188  }
189  }
190  ++it;
191  }
192 
193  tm.tm_isdst = -1;
194  t = mktime(&tm);
195  tv.tv_sec = t;
196  tv.tv_usec = 0;
197 
198  auto prevIt = it;
199  try
200  {
201  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
202  {
203  seqNum = std::stoi(*it);
204  }
205  }
206  catch (const std::invalid_argument& e)
207  {
208  it = prevIt;
209  }
210  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
211  {
212  hostname = *it;
213  }
214  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
215  {
216  hostaddr = *it;
217  }
218  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
219  {
220  sev = mf::ELseverityLevel(*it);
221  }
222  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
223  {
224  category = *it;
225  }
226  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
227  {
228  application = *it;
229  }
230  prevIt = it;
231  try
232  {
233  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
234  {
235  pid = std::stol(*it);
236  }
237  }
238  catch (const std::invalid_argument& e)
239  {
240  it = prevIt;
241  }
242  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
243  {
244  eventID = *it;
245  }
246  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
247  {
248  module = *it;
249  }
250  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
251  {
252  file = *it;
253  }
254  if (it != tokens.end() && ++it != tokens.end() /* Advances it */)
255  {
256  line = *it;
257  }
258  std::ostringstream oss;
259  bool first = true;
260  while (it != tokens.end() && ++it != tokens.end() /* Advances it */)
261  {
262  if (!first)
263  {
264  oss << "|";
265  }
266  else
267  {
268  first = false;
269  }
270  oss << *it;
271  }
272  TLOG(TLVL_TRACE) << "Message content: " << oss.str();
273  message = oss.str();
274  }
275 
276  auto msg = std::make_shared<qt_mf_msg>(hostname, category, application, pid, tv);
277  msg->setSeverity(sev);
278  msg->setMessage("UDPMessage", seqNum, message);
279  msg->setHostAddr(hostaddr);
280  msg->setFileName(file);
281  msg->setLineNumber(line);
282  msg->setModule(module);
283  msg->setEventID(eventID);
284  msg->updateText();
285 
286  return msg;
287 }
288 
289 bool mfviewer::UDPReceiver::validate_packet(std::string const& input)
290 {
291  // Run some checks on the input packet
292  if (input.find("MF") == std::string::npos)
293  {
294  TLOG(TLVL_WARNING) << "Failed to find \"MF\" in message: " << input;
295  return false;
296  }
297  if (input.find("|") == std::string::npos)
298  {
299  TLOG(TLVL_WARNING) << "Failed to find | separator character in message: " << input;
300  return false;
301  }
302  return true;
303 }
304 
305 #include "moc_UDP_receiver.cpp"
306 
307 DEFINE_MFVIEWER_RECEIVER(mfviewer::UDPReceiver)
void run() override
Receiver method. Receive messages and emit NewMessage signal
Definition: UDP_receiver.cc:80
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
Receive messages through a UDP socket. Expects the syslog format provided by UDP_mfPlugin (ELUDP) ...
Definition: UDP_receiver.hh:12
UDPReceiver(fhicl::ParameterSet const &pset)
UDPReceiver Constructor
Definition: UDP_receiver.cc:10
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:90
static bool validate_packet(std::string const &input)
Run simple validation tests on message
msg_ptr_t read_msg(std::string const &input)
Parse incoming message
virtual ~UDPReceiver()
Destructor – Close socket
Definition: UDP_receiver.cc:73
A MVReceiver class listens for messages and raises a signal when one arrives
Definition: MVReceiver.hh:17