Line data Source code
1 : #include "cetlib/PluginTypeDeducer.h"
2 : #include "fhiclcpp/ParameterSet.h"
3 : #include "fhiclcpp/types/ConfigurationTable.h"
4 :
5 : #include "cetlib/compiler_macros.h"
6 : #include "messagefacility/MessageLogger/MessageLogger.h"
7 : #include "messagefacility/MessageService/ELdestination.h"
8 : #include "messagefacility/Utilities/ELseverityLevel.h"
9 : #include "messagefacility/Utilities/exception.h"
10 :
11 : // C/C++ includes
12 : #include <arpa/inet.h>
13 : #include <ifaddrs.h>
14 : #include <netdb.h>
15 : #include <netinet/in.h>
16 : #include <algorithm>
17 : #include <fstream>
18 : #include <iostream>
19 : #include <memory>
20 : #include "mfextensions/Receivers/detail/TCPConnect.hh"
21 :
22 : #define TRACE_NAME "OTS_mfPlugin"
23 : #include "trace.h"
24 :
25 : // Boost includes
26 : #include <boost/algorithm/string.hpp>
27 :
28 : namespace mfplugins {
29 : using mf::ErrorObj;
30 : using mf::service::ELdestination;
31 :
32 : /// <summary>
33 : /// Message Facility OTS Console Destination
34 : /// Formats messages into Ryan's favorite format for OTS
35 : /// </summary>
36 : class ELOTS : public ELdestination
37 : {
38 : public:
39 : /**
40 : * \brief Configuration Parameters for ELOTS
41 : */
42 : struct Config
43 : {
44 : /// ELDestination common config parameters
45 : fhicl::TableFragment<ELdestination::Config> elDestConfig;
46 : /// format_string (Default: "%L:%N:%f [%u] %m"): Format specifier for printing to console. %% => '%' ...
47 : fhicl::Atom<std::string> format_string = fhicl::Atom<std::string>{
48 : fhicl::Name{"format_string"}, fhicl::Comment{"Format specifier for printing to console. %% => '%' ... "},
49 : "%L:%N:%f [%u] %m"};
50 : /// filename_delimit (Default: "/"): Grab path after this. "/srcs/" /x/srcs/y/z.cc => y/z.cc
51 : fhicl::Atom<std::string> filename_delimit =
52 : fhicl::Atom<std::string>{fhicl::Name{"filename_delimit"},
53 : fhicl::Comment{"Grab path after this. \"/srcs/\" /x/srcs/y/z.cc => y/z.cc"}, "/"};
54 : };
55 : /// Used for ParameterSet validation
56 : using Parameters = fhicl::WrappedTable<Config>;
57 :
58 : public:
59 : /// <summary>
60 : /// ELOTS Constructor
61 : /// </summary>
62 : /// <param name="pset">ParameterSet used to configure ELOTS</param>
63 : ELOTS(Parameters const& pset);
64 :
65 : /**
66 : * \brief Fill the "Prefix" portion of the message
67 : * \param o Output stringstream
68 : * \param msg MessageFacility object containing header information
69 : */
70 : void fillPrefix(std::ostringstream& o, const ErrorObj& msg) override;
71 :
72 : /**
73 : * \brief Fill the "User Message" portion of the message
74 : * \param o Output stringstream
75 : * \param msg MessageFacility object containing header information
76 : */
77 : void fillUsrMsg(std::ostringstream& o, const ErrorObj& msg) override;
78 :
79 : /**
80 : * \brief Fill the "Suffix" portion of the message (Unused)
81 : */
82 0 : void fillSuffix(std::ostringstream& /*unused*/, const ErrorObj& /*msg*/) override {}
83 :
84 : /**
85 : * \brief Serialize a MessageFacility message to the output
86 : * \param o Stringstream object containing message data
87 : * \param e MessageFacility object containing header information
88 : */
89 : void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
90 :
91 : private:
92 : // Other stuff
93 : int64_t pid_;
94 : std::string hostname_;
95 : std::string hostaddr_;
96 : std::string app_;
97 : std::string format_string_;
98 : std::string filename_delimit_;
99 : };
100 :
101 : // END DECLARATION
102 : //======================================================================
103 : // BEGIN IMPLEMENTATION
104 :
105 : //======================================================================
106 : // ELOTS c'tor
107 : //======================================================================
108 :
109 0 : ELOTS::ELOTS(Parameters const& pset)
110 0 : : ELdestination(pset().elDestConfig()), pid_(static_cast<int64_t>(getpid())), format_string_(pset().format_string()), filename_delimit_(pset().filename_delimit())
111 : {
112 : // hostname
113 : char hostname_c[1024];
114 0 : hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
115 :
116 : // host ip address
117 0 : hostent* host = nullptr;
118 0 : host = gethostbyname(hostname_c);
119 :
120 0 : if (host != nullptr)
121 : {
122 : // ip address from hostname if the entry exists in /etc/hosts
123 0 : char* ip = inet_ntoa(*reinterpret_cast<struct in_addr*>(host->h_addr)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
124 0 : hostaddr_ = ip;
125 : }
126 : else
127 : {
128 : // enumerate all network interfaces
129 0 : struct ifaddrs* ifAddrStruct = nullptr;
130 0 : struct ifaddrs* ifa = nullptr;
131 0 : void* tmpAddrPtr = nullptr;
132 :
133 0 : if (getifaddrs(&ifAddrStruct) != 0)
134 : {
135 : // failed to get addr struct
136 0 : hostaddr_ = "127.0.0.1";
137 : }
138 : else
139 : {
140 : // iterate through all interfaces
141 0 : for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
142 : {
143 0 : if (ifa->ifa_addr->sa_family == AF_INET)
144 : {
145 : // a valid IPv4 addres
146 0 : tmpAddrPtr = &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr))->sin_addr; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
147 : char addressBuffer[INET_ADDRSTRLEN];
148 0 : inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
149 0 : hostaddr_ = addressBuffer;
150 : }
151 :
152 0 : else if (ifa->ifa_addr->sa_family == AF_INET6)
153 : {
154 : // a valid IPv6 address
155 0 : tmpAddrPtr = &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr))->sin6_addr; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
156 : char addressBuffer[INET6_ADDRSTRLEN];
157 0 : inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
158 0 : hostaddr_ = addressBuffer;
159 : }
160 :
161 : // find first non-local address
162 0 : if (!hostaddr_.empty() && (hostaddr_ != "127.0.0.1") && (hostaddr_ != "::1"))
163 : {
164 0 : break;
165 : }
166 : }
167 :
168 0 : if (hostaddr_.empty())
169 : { // failed to find anything
170 0 : hostaddr_ = "127.0.0.1";
171 : }
172 : }
173 : }
174 :
175 : // get process name from '/proc/pid/cmdline'
176 0 : std::stringstream ss;
177 0 : ss << "//proc//" << pid_ << "//cmdline";
178 0 : std::ifstream procfile{ss.str().c_str()};
179 :
180 0 : std::string procinfo;
181 :
182 0 : if (procfile.is_open())
183 : {
184 0 : procfile >> procinfo;
185 0 : procfile.close();
186 : }
187 :
188 0 : size_t end = procinfo.find('\0');
189 0 : size_t start = procinfo.find_last_of('/', end);
190 :
191 0 : app_ = procinfo.substr(start + 1, end - start - 1);
192 0 : }
193 :
194 : //======================================================================
195 : // Message prefix filler ( overriddes ELdestination::fillPrefix )
196 : //======================================================================
197 0 : void ELOTS::fillPrefix(std::ostringstream& oss, const ErrorObj& msg)
198 : {
199 0 : const auto& xid = msg.xid();
200 :
201 0 : const auto& id = xid.id();
202 0 : const auto& module = xid.module();
203 0 : auto app = app_;
204 0 : char* cp = &format_string_[0]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
205 : char sev;
206 0 : bool msg_printed = false;
207 0 : std::string ossstr;
208 : // ossstr.reserve(100);
209 :
210 0 : for (; *cp != 0; ++cp) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
211 : {
212 0 : if (*cp != '%')
213 : {
214 0 : oss << *cp;
215 0 : continue;
216 : }
217 0 : if (*++cp == '\0') // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
218 : { // inc pas '%' and check if end
219 : // ending '%' gets printed
220 0 : oss << *cp;
221 0 : break; // done
222 : }
223 0 : switch (*cp)
224 : {
225 0 : case 'A':
226 0 : oss << app;
227 0 : break; // application
228 0 : case 'a':
229 0 : oss << hostaddr_;
230 0 : break; // host address
231 0 : case 'd':
232 0 : oss << module;
233 0 : break; // module name # Early
234 0 : case 'f': // filename
235 0 : if (filename_delimit_.empty())
236 : {
237 0 : oss << msg.filename();
238 : }
239 0 : else if (filename_delimit_.size() == 1)
240 : {
241 0 : oss << (strrchr(&msg.filename()[0], filename_delimit_[0]) != nullptr // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
242 0 : ? strrchr(&msg.filename()[0], filename_delimit_[0]) + 1 // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
243 0 : : msg.filename());
244 : }
245 : else
246 : {
247 0 : const char* cp = strstr(&msg.filename()[0], &filename_delimit_[0]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
248 0 : if (cp != nullptr)
249 : {
250 : // make sure to remove a part that ends with '/'
251 0 : cp += filename_delimit_.size() - 1; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
252 0 : while (*cp && *cp != '/') ++cp;
253 0 : ++cp; // increment past '/'
254 0 : oss << cp;
255 : }
256 : else
257 0 : oss << msg.filename();
258 : }
259 0 : break;
260 0 : case 'h':
261 0 : oss << hostname_;
262 0 : break; // host name
263 0 : case 'L':
264 0 : oss << xid.severity().getName();
265 0 : break; // severity
266 0 : case 'm': // message
267 : // ossstr.clear(); // incase message is repeated
268 0 : for (auto const& val : msg.items())
269 : {
270 0 : ossstr += val; // Print the contents.
271 : }
272 0 : if (!ossstr.empty())
273 : { // allow/check for "no message"
274 0 : if (ossstr.compare(0, 1, "\n") == 0)
275 : {
276 0 : ossstr.erase(0, 1); // remove leading "\n" if present
277 : }
278 0 : if (ossstr.compare(ossstr.size() - 1, 1, "\n") == 0)
279 : {
280 0 : ossstr.erase(ossstr.size() - 1, 1); // remove trailing "\n" if present
281 : }
282 0 : oss << ossstr;
283 : }
284 0 : msg_printed = true;
285 0 : break;
286 0 : case 'N':
287 0 : oss << id;
288 0 : break; // category
289 0 : case 'P':
290 0 : oss << pid_;
291 0 : break; // processID
292 0 : case 'r':
293 0 : oss << mf::GetIteration();
294 0 : break; // run/iteration/event no #pre-events
295 0 : case 's':
296 0 : sev = xid.severity().getName()[0] | 0x20; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
297 0 : oss << sev;
298 0 : break; // severity lower case
299 0 : case 'T':
300 0 : oss << format_.timestamp(msg.timestamp());
301 0 : break; // timestamp
302 0 : case 'u':
303 0 : oss << std::to_string(msg.lineNumber());
304 0 : break; // linenumber
305 0 : case '%':
306 0 : oss << '%';
307 0 : break; // a '%' character
308 0 : default:
309 0 : oss << '%' << *cp;
310 0 : break; // unknown - just print it w/ it's '%'
311 : }
312 : }
313 0 : if (!msg_printed)
314 : {
315 0 : for (auto const& val : msg.items())
316 : {
317 0 : ossstr += val; // Print the contents.
318 : }
319 0 : if (ossstr.compare(0, 1, "\n") == 0)
320 : {
321 0 : ossstr.erase(0, 1); // remove leading "\n" if present
322 : }
323 0 : if (ossstr.compare(ossstr.size() - 1, 1, "\n") == 0)
324 : {
325 0 : ossstr.erase(ossstr.size() - 1, 1); // remove trailing "\n" if present
326 : }
327 0 : oss << ossstr;
328 : }
329 0 : }
330 :
331 : //======================================================================
332 : // Message filler ( overriddes ELdestination::fillUsrMsg )
333 : //======================================================================
334 0 : void ELOTS::fillUsrMsg(std::ostringstream& oss __attribute__((__unused__)),
335 : const ErrorObj& msg __attribute__((__unused__)))
336 : {
337 : // UsrMsg filled above
338 0 : }
339 :
340 : //======================================================================
341 : // Message router ( overriddes ELdestination::routePayload )
342 : //======================================================================
343 0 : void ELOTS::routePayload(const std::ostringstream& oss, const ErrorObj& /*msg*/) { std::cout << oss.str() << std::endl; }
344 : } // end namespace mfplugins
345 : //======================================================================
346 : //
347 : // makePlugin function
348 : //
349 : //======================================================================
350 :
351 : #ifndef EXTERN_C_FUNC_DECLARE_START
352 : #define EXTERN_C_FUNC_DECLARE_START extern "C" {
353 : #endif
354 :
355 0 : EXTERN_C_FUNC_DECLARE_START auto makePlugin(const std::string& /*unused*/, const fhicl::ParameterSet& pset)
356 : {
357 0 : return std::make_unique<mfplugins::ELOTS>(pset);
358 : }
359 : } // namespace mfplugins
360 :
361 0 : DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
|