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