Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libdevdctl/event.cc
39475 views
1
/*-
2
* Copyright (c) 2011, 2012, 2013, 2016 Spectra Logic Corporation
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions, and the following disclaimer,
10
* without modification.
11
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
12
* substantially similar to the "NO WARRANTY" disclaimer below
13
* ("Disclaimer") and any redistribution must be conditioned upon
14
* including a substantially similar Disclaimer requirement for further
15
* binary redistribution.
16
*
17
* NO WARRANTY
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
* POSSIBILITY OF SUCH DAMAGES.
29
*
30
* Authors: Justin T. Gibbs (Spectra Logic Corporation)
31
*/
32
33
/**
34
* \file event.cc
35
*
36
* Implementation of the class hierarchy used to express events
37
* received via the devdctl API.
38
*/
39
#include <sys/cdefs.h>
40
#include <sys/disk.h>
41
#include <sys/filio.h>
42
#include <sys/param.h>
43
#include <sys/stat.h>
44
45
#include <err.h>
46
#include <fcntl.h>
47
#include <inttypes.h>
48
#include <paths.h>
49
#include <stdlib.h>
50
#include <syslog.h>
51
#include <unistd.h>
52
53
#include <cstdarg>
54
#include <cstring>
55
#include <iostream>
56
#include <list>
57
#include <map>
58
#include <sstream>
59
#include <string>
60
61
#include "guid.h"
62
#include "event.h"
63
#include "event_factory.h"
64
#include "exception.h"
65
/*================================== Macros ==================================*/
66
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
67
68
/*============================ Namespace Control =============================*/
69
using std::cout;
70
using std::endl;
71
using std::string;
72
using std::stringstream;
73
74
namespace DevdCtl
75
{
76
77
/*=========================== Class Implementations ==========================*/
78
/*----------------------------------- Event ----------------------------------*/
79
//- Event Static Protected Data ------------------------------------------------
80
const string Event::s_theEmptyString;
81
82
Event::EventTypeRecord Event::s_typeTable[] =
83
{
84
{ Event::NOTIFY, "Notify" },
85
{ Event::NOMATCH, "No Driver Match" },
86
{ Event::ATTACH, "Attach" },
87
{ Event::DETACH, "Detach" }
88
};
89
90
//- Event Static Public Methods ------------------------------------------------
91
Event *
92
Event::Builder(Event::Type type, NVPairMap &nvPairs,
93
const string &eventString)
94
{
95
return (new Event(type, nvPairs, eventString));
96
}
97
98
Event *
99
Event::CreateEvent(const EventFactory &factory, const string &eventString)
100
{
101
NVPairMap &nvpairs(*new NVPairMap);
102
Type type(static_cast<Event::Type>(eventString[0]));
103
104
try {
105
ParseEventString(type, eventString, nvpairs);
106
} catch (const ParseException &exp) {
107
if (exp.GetType() == ParseException::INVALID_FORMAT)
108
exp.Log();
109
return (NULL);
110
}
111
112
/*
113
* Allow entries in our table for events with no system specified.
114
* These entries should specify the string "none".
115
*/
116
NVPairMap::iterator system_item(nvpairs.find("system"));
117
if (system_item == nvpairs.end())
118
nvpairs["system"] = "none";
119
120
return (factory.Build(type, nvpairs, eventString));
121
}
122
123
bool
124
Event::DevName(std::string &name) const
125
{
126
return (false);
127
}
128
129
/* TODO: simplify this function with C++-11 <regex> methods */
130
bool
131
Event::IsDiskDev() const
132
{
133
const int numDrivers = 2;
134
static const char *diskDevNames[numDrivers] =
135
{
136
"da",
137
"ada"
138
};
139
const char **dName;
140
string devName;
141
142
if (! DevName(devName))
143
return false;
144
145
size_t find_start = devName.rfind('/');
146
if (find_start == string::npos) {
147
find_start = 0;
148
} else {
149
/* Just after the last '/'. */
150
find_start++;
151
}
152
153
for (dName = &diskDevNames[0];
154
dName <= &diskDevNames[numDrivers - 1]; dName++) {
155
156
size_t loc(devName.find(*dName, find_start));
157
if (loc == find_start) {
158
size_t prefixLen(strlen(*dName));
159
160
if (devName.length() - find_start >= prefixLen
161
&& isdigit(devName[find_start + prefixLen]))
162
return (true);
163
}
164
}
165
166
return (false);
167
}
168
169
const char *
170
Event::TypeToString(Event::Type type)
171
{
172
EventTypeRecord *rec(s_typeTable);
173
EventTypeRecord *lastRec(s_typeTable + NUM_ELEMENTS(s_typeTable) - 1);
174
175
for (; rec <= lastRec; rec++) {
176
if (rec->m_type == type)
177
return (rec->m_typeName);
178
}
179
return ("Unknown");
180
}
181
182
//- Event Public Methods -------------------------------------------------------
183
const string &
184
Event::Value(const string &varName) const
185
{
186
NVPairMap::const_iterator item(m_nvPairs.find(varName));
187
if (item == m_nvPairs.end())
188
return (s_theEmptyString);
189
190
return (item->second);
191
}
192
193
bool
194
Event::Contains(const string &varName) const
195
{
196
return (m_nvPairs.find(varName) != m_nvPairs.end());
197
}
198
199
string
200
Event::ToString() const
201
{
202
stringstream result;
203
204
NVPairMap::const_iterator devName(m_nvPairs.find("device-name"));
205
if (devName != m_nvPairs.end())
206
result << devName->second << ": ";
207
208
NVPairMap::const_iterator systemName(m_nvPairs.find("system"));
209
if (systemName != m_nvPairs.end()
210
&& systemName->second != "none")
211
result << systemName->second << ": ";
212
213
result << TypeToString(GetType()) << ' ';
214
215
for (NVPairMap::const_iterator curVar = m_nvPairs.begin();
216
curVar != m_nvPairs.end(); curVar++) {
217
if (curVar == devName || curVar == systemName)
218
continue;
219
220
result << ' '
221
<< curVar->first << "=" << curVar->second;
222
}
223
result << endl;
224
225
return (result.str());
226
}
227
228
void
229
Event::Print() const
230
{
231
cout << ToString() << std::flush;
232
}
233
234
void
235
Event::Log(int priority) const
236
{
237
syslog(priority, "%s", ToString().c_str());
238
}
239
240
//- Event Virtual Public Methods -----------------------------------------------
241
Event::~Event()
242
{
243
delete &m_nvPairs;
244
}
245
246
Event *
247
Event::DeepCopy() const
248
{
249
return (new Event(*this));
250
}
251
252
bool
253
Event::Process() const
254
{
255
return (false);
256
}
257
258
timeval
259
Event::GetTimestamp() const
260
{
261
timeval tv_timestamp;
262
struct tm tm_timestamp;
263
264
if (!Contains("timestamp")) {
265
throw Exception("Event contains no timestamp: %s",
266
m_eventString.c_str());
267
}
268
strptime(Value(string("timestamp")).c_str(), "%s", &tm_timestamp);
269
tv_timestamp.tv_sec = mktime(&tm_timestamp);
270
tv_timestamp.tv_usec = 0;
271
return (tv_timestamp);
272
}
273
274
bool
275
Event::DevPath(std::string &path) const
276
{
277
char buf[SPECNAMELEN + 1];
278
string devName;
279
280
if (!DevName(devName))
281
return (false);
282
283
string devPath(_PATH_DEV + devName);
284
int devFd(open(devPath.c_str(), O_RDONLY));
285
if (devFd == -1)
286
return (false);
287
288
/* Normalize the device name in case the DEVFS event is for a link. */
289
if (fdevname_r(devFd, buf, sizeof(buf)) == NULL) {
290
close(devFd);
291
return (false);
292
}
293
devName = buf;
294
path = _PATH_DEV + devName;
295
296
close(devFd);
297
298
return (true);
299
}
300
301
bool
302
Event::PhysicalPath(std::string &path) const
303
{
304
string devPath;
305
306
if (!DevPath(devPath))
307
return (false);
308
309
int devFd(open(devPath.c_str(), O_RDONLY));
310
if (devFd == -1)
311
return (false);
312
313
char physPath[MAXPATHLEN];
314
physPath[0] = '\0';
315
bool result(ioctl(devFd, DIOCGPHYSPATH, physPath) == 0);
316
close(devFd);
317
if (result)
318
path = physPath;
319
return (result);
320
}
321
322
//- Event Protected Methods ----------------------------------------------------
323
Event::Event(Type type, NVPairMap &map, const string &eventString)
324
: m_type(type),
325
m_nvPairs(map),
326
m_eventString(eventString)
327
{
328
}
329
330
Event::Event(const Event &src)
331
: m_type(src.m_type),
332
m_nvPairs(*new NVPairMap(src.m_nvPairs)),
333
m_eventString(src.m_eventString)
334
{
335
}
336
337
void
338
Event::ParseEventString(Event::Type type,
339
const string &eventString,
340
NVPairMap& nvpairs)
341
{
342
size_t start;
343
size_t end;
344
345
switch (type) {
346
case ATTACH:
347
case DETACH:
348
349
/*
350
* <type><device-name><unit> <pnpvars> \
351
* at <location vars> <pnpvars> \
352
* on <parent>
353
*
354
* Handle all data that doesn't conform to the
355
* "name=value" format, and let the generic parser
356
* below handle the rest.
357
*
358
* Type is a single char. Skip it.
359
*/
360
start = 1;
361
end = eventString.find_first_of(" \t\n", start);
362
if (end == string::npos)
363
throw ParseException(ParseException::INVALID_FORMAT,
364
eventString, start);
365
366
nvpairs["device-name"] = eventString.substr(start, end - start);
367
368
start = eventString.find(" on ", end);
369
if (end == string::npos)
370
throw ParseException(ParseException::INVALID_FORMAT,
371
eventString, start);
372
start += 4;
373
end = eventString.find_first_of(" \t\n", start);
374
nvpairs["parent"] = eventString.substr(start, end);
375
break;
376
case NOTIFY:
377
break;
378
case NOMATCH:
379
throw ParseException(ParseException::DISCARDED_EVENT_TYPE,
380
eventString);
381
default:
382
throw ParseException(ParseException::UNKNOWN_EVENT_TYPE,
383
eventString);
384
}
385
386
/* Process common "key=value" format. */
387
for (start = 1; start < eventString.length(); start = end + 1) {
388
389
/* Find the '=' in the middle of the key/value pair. */
390
end = eventString.find('=', start);
391
if (end == string::npos)
392
break;
393
394
/*
395
* Find the start of the key by backing up until
396
* we hit whitespace or '!' (event type "notice").
397
* Due to the devdctl format, all key/value pair must
398
* start with one of these two characters.
399
*/
400
start = eventString.find_last_of("! \t\n", end);
401
if (start == string::npos)
402
throw ParseException(ParseException::INVALID_FORMAT,
403
eventString, end);
404
start++;
405
string key(eventString.substr(start, end - start));
406
407
/*
408
* Walk forward from the '=' until either we exhaust
409
* the buffer or we hit whitespace.
410
*/
411
start = end + 1;
412
if (start >= eventString.length())
413
throw ParseException(ParseException::INVALID_FORMAT,
414
eventString, end);
415
end = eventString.find_first_of(" \t\n", start);
416
if (end == string::npos)
417
end = eventString.length() - 1;
418
string value(eventString.substr(start, end - start));
419
420
nvpairs[key] = value;
421
}
422
}
423
424
void
425
Event::TimestampEventString(std::string &eventString)
426
{
427
if (eventString.size() > 0) {
428
/*
429
* Add a timestamp as the final field of the event if it is
430
* not already present.
431
*/
432
if (eventString.find(" timestamp=") == string::npos) {
433
const size_t bufsize = 32; // Long enough for a 64-bit int
434
timeval now;
435
char timebuf[bufsize];
436
437
size_t eventEnd(eventString.find_last_not_of('\n') + 1);
438
if (gettimeofday(&now, NULL) != 0)
439
err(1, "gettimeofday");
440
snprintf(timebuf, bufsize, " timestamp=%" PRId64,
441
(int64_t) now.tv_sec);
442
eventString.insert(eventEnd, timebuf);
443
}
444
}
445
}
446
447
/*-------------------------------- DevfsEvent --------------------------------*/
448
//- DevfsEvent Static Public Methods -------------------------------------------
449
Event *
450
DevfsEvent::Builder(Event::Type type, NVPairMap &nvPairs,
451
const string &eventString)
452
{
453
return (new DevfsEvent(type, nvPairs, eventString));
454
}
455
456
//- DevfsEvent Static Protected Methods ----------------------------------------
457
bool
458
DevfsEvent::IsWholeDev(const string &devName)
459
{
460
string::const_iterator i(devName.begin());
461
462
size_t start = devName.rfind('/');
463
if (start == string::npos) {
464
start = 0;
465
} else {
466
/* Just after the last '/'. */
467
start++;
468
}
469
i += start;
470
471
/* alpha prefix followed only by digits. */
472
for (; i < devName.end() && !isdigit(*i); i++)
473
;
474
475
if (i == devName.end())
476
return (false);
477
478
for (; i < devName.end() && isdigit(*i); i++)
479
;
480
481
return (i == devName.end());
482
}
483
484
//- DevfsEvent Virtual Public Methods ------------------------------------------
485
Event *
486
DevfsEvent::DeepCopy() const
487
{
488
return (new DevfsEvent(*this));
489
}
490
491
bool
492
DevfsEvent::Process() const
493
{
494
return (true);
495
}
496
497
//- DevfsEvent Public Methods --------------------------------------------------
498
bool
499
DevfsEvent::IsWholeDev() const
500
{
501
string devName;
502
503
return (DevName(devName) && IsDiskDev() && IsWholeDev(devName));
504
}
505
506
bool
507
DevfsEvent::DevName(std::string &name) const
508
{
509
if (Value("subsystem") != "CDEV")
510
return (false);
511
512
name = Value("cdev");
513
return (!name.empty());
514
}
515
516
//- DevfsEvent Protected Methods -----------------------------------------------
517
DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs,
518
const string &eventString)
519
: Event(type, nvpairs, eventString)
520
{
521
}
522
523
DevfsEvent::DevfsEvent(const DevfsEvent &src)
524
: Event(src)
525
{
526
}
527
528
/*--------------------------------- GeomEvent --------------------------------*/
529
//- GeomEvent Static Public Methods --------------------------------------------
530
Event *
531
GeomEvent::Builder(Event::Type type, NVPairMap &nvpairs,
532
const string &eventString)
533
{
534
return (new GeomEvent(type, nvpairs, eventString));
535
}
536
537
//- GeomEvent Virtual Public Methods -------------------------------------------
538
Event *
539
GeomEvent::DeepCopy() const
540
{
541
return (new GeomEvent(*this));
542
}
543
544
bool
545
GeomEvent::DevName(std::string &name) const
546
{
547
if (Value("subsystem") == "disk")
548
name = Value("devname");
549
else
550
name = Value("cdev");
551
return (!name.empty());
552
}
553
554
555
//- GeomEvent Protected Methods ------------------------------------------------
556
GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs,
557
const string &eventString)
558
: Event(type, nvpairs, eventString),
559
m_devname(Value("devname"))
560
{
561
}
562
563
GeomEvent::GeomEvent(const GeomEvent &src)
564
: Event(src),
565
m_devname(src.m_devname)
566
{
567
}
568
569
/*--------------------------------- ZfsEvent ---------------------------------*/
570
//- ZfsEvent Static Public Methods ---------------------------------------------
571
Event *
572
ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs,
573
const string &eventString)
574
{
575
return (new ZfsEvent(type, nvpairs, eventString));
576
}
577
578
//- ZfsEvent Virtual Public Methods --------------------------------------------
579
Event *
580
ZfsEvent::DeepCopy() const
581
{
582
return (new ZfsEvent(*this));
583
}
584
585
bool
586
ZfsEvent::DevName(std::string &name) const
587
{
588
return (false);
589
}
590
591
//- ZfsEvent Protected Methods -------------------------------------------------
592
ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs,
593
const string &eventString)
594
: Event(type, nvpairs, eventString),
595
m_poolGUID(Guid(Value("pool_guid"))),
596
m_vdevGUID(Guid(Value("vdev_guid")))
597
{
598
}
599
600
ZfsEvent::ZfsEvent(const ZfsEvent &src)
601
: Event(src),
602
m_poolGUID(src.m_poolGUID),
603
m_vdevGUID(src.m_vdevGUID)
604
{
605
}
606
607
} // namespace DevdCtl
608
609