aix-errlog 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aef41605d9a740c337e3b81e0a368a7b96dcdfd5e73dc3764673da0faa2a81ab
4
- data.tar.gz: 06ad2a5c388cb8e98d95e007a867d374575daa51333982c11e244d9c81fc5f43
3
+ metadata.gz: a6e98415b5db1bba3a83301429dbc6fb7d8e5f860a35c75225596c3f3493f9df
4
+ data.tar.gz: 77e593f0c156f3dd12209eed4806d7d87e0cf15710ef42dbb3eb96f0804dc043
5
5
  SHA512:
6
- metadata.gz: edd6fa23e192513af9eeb0e1349708e25fcb6f1e64ba39142def2bdcf91ccf25a1d4907a070770e4b0cc2d374d71f9a47aec1f3eb849346fe47c57ad7ea17b49
7
- data.tar.gz: 4b5f8bf6c6c36dc5c17cccd611a86aeb69ae4cb6e66f261627f46a24034b0dd11a4230e44aa3aa625f3d55c04d101ca626d170d13e3086ad417520c3123d41d4
6
+ metadata.gz: 7f20031f90faa552c4f99795b9fd2395b9658cd3b2aa3a02590b10bc68d3615ffab59fc21aeb84fb9ae46512f4644539a899ea97a79067325a065082845ecdc3
7
+ data.tar.gz: 6d8753b599485fdc819c80e646d5b318794ed49ce89ad5112af38efa804fcb8982344f7b8891a84f6df8befc34a511c66e00e98799596c094a2732729fb2094a
@@ -0,0 +1 @@
1
+ require 'aix/errlog/errlog'
@@ -0,0 +1,93 @@
1
+ module AIX
2
+ module Errlog
3
+ module Constants
4
+ ERR_REC_MAX = 4096
5
+ O_RDONLY = 0x00
6
+ O_WRONLY = 0x01
7
+ O_RDWR = 0x02
8
+ O_ACCMODE = 0x03
9
+ O_NONBLOCK = 0x04
10
+ O_APPEND = 0x08
11
+ O_CREAT = 0x100
12
+ O_TRUNC = 0x200
13
+ O_EXCL = 0x400
14
+ O_NOCTTY = 0x800
15
+ O_DIRECTORY = 0x80000
16
+
17
+ LE_MAGIC540 = 0x0C4DF540
18
+ LE_MAGIC = LE_MAGIC540
19
+
20
+ LE_LABEL_MAX = 20
21
+ LE_MACHINE_ID_MAX = 32
22
+ LE_NODE_ID_MAX = 32
23
+ LE_CLASS_MAX = 2
24
+ LE_TYPE_MAX = 5
25
+ LE_RESOURCE_MAX = 16
26
+ LE_RCLASS_MAX = 16
27
+ LE_RTYPE_MAX = 16
28
+ LE_VPD_MAX = 512
29
+ LE_IN_MAX = 256
30
+ LE_CONN_MAX = 20
31
+ LE_DETAIL_MAX = ERR_REC_MAX
32
+ LE_SYMPTOM_MAX = 312
33
+ LE_ERRDUP_MAX = 16
34
+ LE_WPAR_ID_MAX = 28
35
+
36
+ LE_FLAG_ERR64 = 0x01
37
+ LE_FLAG_ERRDUP = 0x100
38
+ LE_FLAG_ERRWPAR = 0x200
39
+
40
+ LE_OP_EQUAL = 0x01
41
+ LE_OP_NE = 0x02
42
+ LE_OP_SUBSTR = 0x03
43
+ LE_OP_LT = 0x04
44
+ LE_OP_LE = 0x05
45
+ LE_OP_GT = 0x06
46
+ LE_OP_GE = 0x07
47
+ LE_OP_LEAF = 0x100
48
+ LE_OP_NOT = 0x101
49
+ LE_OP_AND = 0x201
50
+ LE_OP_OR = 0x202
51
+ LE_OP_XOR = 0x203
52
+
53
+ LE_TYPE = 0xff00
54
+ LE_TYPE_INT = 0x0100
55
+ LE_TYPE_STRING = 0x0200
56
+ LE_TYPE_BOOLEAN = 0x0300
57
+
58
+ LE_MATCH_FIELD = 0xff
59
+ LE_MATCH_SEQUENCE = (0x01|LE_TYPE_INT)
60
+ LE_MATCH_LABEL = (0x02|LE_TYPE_STRING)
61
+ LE_MATCH_TIMESTAMP = (0x03|LE_TYPE_INT)
62
+ LE_MATCH_CRCID = (0x04|LE_TYPE_INT)
63
+ LE_MATCH_MACHINEID = (0x05|LE_TYPE_STRING)
64
+ LE_MATCH_NODEID = (0x06|LE_TYPE_STRING)
65
+ LE_MATCH_CLASS = (0x07|LE_TYPE_STRING)
66
+ LE_MATCH_TYPE = (0x08|LE_TYPE_STRING)
67
+ LE_MATCH_RESOURCE = (0x09|LE_TYPE_STRING)
68
+ LE_MATCH_RCLASS = (0x0a|LE_TYPE_STRING)
69
+ LE_MATCH_RTYPE = (0x0b|LE_TYPE_STRING)
70
+ LE_MATCH_VPD_IBM = (0x0c|LE_TYPE_STRING)
71
+ LE_MATCH_VPD_USER = (0x0d|LE_TYPE_STRING)
72
+ LE_MATCH_IN = (0x0e|LE_TYPE_STRING)
73
+ LE_MATCH_CONNWHERE = (0x0f|LE_TYPE_STRING)
74
+ LE_MATCH_FLAG_ERR64 = (0x10|LE_TYPE_BOOLEAN)
75
+ LE_MATCH_FLAG_ERRDUP = (0x11|LE_TYPE_BOOLEAN)
76
+ LE_MATCH_DETAIL_DATA = (0x12|LE_TYPE_STRING)
77
+ LE_MATCH_SYMPTOM_DATA = (0x13|LE_TYPE_STRING)
78
+ LE_MATCH_ERRDIAG = (0x14|LE_TYPE_INT)
79
+ LE_MATCH_WPARID = (0x15|LE_TYPE_STRING)
80
+
81
+ LE_FORWARD = 0x01
82
+ LE_REVERSE = 0x02
83
+
84
+ LE_ERR_INVARG = 0x01
85
+ LE_ERR_NOFILE = 0x02
86
+ LE_ERR_INVFILE = 0x03
87
+ LE_ERR_NOMEM = 0x04
88
+ LE_ERR_NOWRITE = 0x05
89
+ LE_ERR_IO = 0x06
90
+ LE_ERR_DONE = 0x07
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,46 @@
1
+ require 'ffi'
2
+
3
+ require 'aix/errlog/constants'
4
+
5
+ module AIX
6
+ module Errlog
7
+ ##
8
+ # An errlog entry class. Used to parse the raw errlog_entry_t struct into a
9
+ # more useful Ruby object.
10
+ #
11
+ # You shouldn't need to invoke this class directly; it is generated by the
12
+ # Errlog#forward_each and Errlog#reverse_each.
13
+ class Entry
14
+ attr_reader :magic, :sequence, :label, :timestamp, :crcid, :errdiag, :machineid, :nodeid, :class, :type, :resource, :rclass, :rtype, :vpd_ibm, :vpd_user, :in, :connwhere, :flags, :detail, :symptom, :dup_count, :dup_time1, :dup_time2, :wparid
15
+
16
+ def initialize(raw)
17
+ @magic = raw[:el_magic]
18
+ @sequence = raw[:el_sequence]
19
+ @label = raw[:el_label].to_s.freeze
20
+ @timestamp = Time.at(raw[:el_timestamp]).freeze
21
+ @crcid = raw[:el_crcid]
22
+ @errdiag = raw[:el_errdiag]
23
+ @machineid = raw[:el_machineid].to_s.freeze
24
+ @nodeid = raw[:el_nodeid].to_s.freeze
25
+ @class = raw[:el_class].to_s.freeze
26
+ @type = raw[:el_type].to_s.freeze
27
+ @resource = raw[:el_resource].to_s.freeze
28
+ @rclass = raw[:el_rclass].to_s.freeze
29
+ @rtype = raw[:el_rtype].to_s.freeze
30
+ @vpd_ibm = raw[:el_vpd_ibm].to_s.freeze
31
+ @vpd_user = raw[:el_vpd_user].to_s.freeze
32
+ @in = raw[:el_in].to_s
33
+ @connwhere = raw[:el_connwhere].to_s.freeze
34
+ @flags = raw[:el_flags]
35
+ length = raw[:el_detail_length]
36
+ @detail = raw[:el_detail_data].to_ptr.get_bytes(0, length).freeze
37
+ length = raw[:el_symptom_length]
38
+ @symptom = raw[:el_symptom_data].to_ptr.get_bytes(0, length).freeze
39
+ @dup_count = raw[:el_errdup][:ed_dupcount]
40
+ @dup_time1 = Time.at(raw[:el_errdup][:ed_time1]).freeze
41
+ @dup_time2 = Time.at(raw[:el_errdup][:ed_time2]).freeze
42
+ @wparid = raw[:el_wparid].to_s.freeze
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,436 @@
1
+ require 'aix/errlog/constants'
2
+ require 'aix/errlog/lib'
3
+ require 'aix/errlog/entry'
4
+ require 'aix/errlog/errors'
5
+ require 'aix/errlog/match'
6
+
7
+ module AIX
8
+ module Errlog
9
+ ##
10
+ # Simple convenince shortcut to access AIX::Errlog::Errlog.open
11
+ def self.open(*args, &block)
12
+ ::AIX::Errlog::Errlog.open(*args, &block)
13
+ end
14
+
15
+ ##
16
+ # The core errlog class. Used to open an errlog file.
17
+ #
18
+ # The main method that should be used here is ::open (more likely
19
+ # AIX::Errlog.open for convenience), and the block form is strongly
20
+ # recommended wherever possible. If you do not use the block form, make
21
+ # sure you call #close when you are done with it (enforce it with an ensure
22
+ # block if possible). The garbage collector will not do this automatically,
23
+ # and you can leak.
24
+ #
25
+ # #forward_each and #reverse_each should do everything you need to do. If
26
+ # you use the enumerator form, or even the block form of these, make sure
27
+ # you consume the entire enumerator before opening a new one. If you need
28
+ # to enumerate the errlog in a nested loop or something like that, you'll
29
+ # need multuple instances of this open, otherwise it will fail (the handle
30
+ # operates as a cursor, so if you try to re-invoke one before it is
31
+ # finished, the cursor will get reset, and you'll get jumbled results).
32
+ # While one of these enumerators as active, trying to re-invoke one will
33
+ # raise an EnumeratorError.
34
+ #
35
+ # If you need to do complex matching, use the #match_* methods in here to
36
+ # create Match objects to work with. You can see Match for more details as
37
+ # to how to create those.
38
+ #
39
+ # A simple example, showing how to get a list of all labels of all log
40
+ # entries in forward order which contain the string 'KILL' in their label
41
+ # and happen in the month of January 2017, and which have a sequence ID
42
+ # above 1000, or that happen in December 2016 and have a sequence ID below
43
+ # 500, might look something like this:
44
+ #
45
+ # require 'date'
46
+ #
47
+ # require 'aix/errlog'
48
+ #
49
+ # AIX::Errlog.open do |log|
50
+ # log.forward_each(match: (
51
+ # log.match_label.include?('KILL') & (
52
+ # (
53
+ # (log.match_sequence > 1000) &
54
+ # (log.match_timestamp >= DateTime.new(2017, 1, 1)) &
55
+ # (log.match_timestamp < DateTime.new(2017, 2, 1))
56
+ # ) | (
57
+ # (log.match_sequence < 500) &
58
+ # (log.match_timestamp >= DateTime.new(2016, 12, 1)) &
59
+ # (log.match_timestamp < DateTime.new(2017, 1, 1))
60
+ # )
61
+ # )
62
+ # )).map(&:label)
63
+ # end
64
+ #
65
+ # Certainly, that looks a little complex, but it is a bit more efficient
66
+ # than iterating all log entries as a whole and then filtering after the
67
+ # fact, and it's a lot more pleasant than building the Match tree from
68
+ # scratch in C.
69
+ class Errlog
70
+ ##
71
+ # path is the string path to the file.
72
+ # mode matches as closely as possible to the semantics of the fopen mode.
73
+ def initialize(path='/var/adm/ras/errlog'.freeze, mode='r'.freeze)
74
+ mode_r = mode.include? 'r'
75
+ mode_w = mode.include? 'w'
76
+ mode_a = mode.include? 'a'
77
+ mode_x = mode.include? 'x'
78
+ mode_p = mode.include? '+'
79
+
80
+ mode_flags =
81
+ if mode_p
82
+ Constants::O_RDRW
83
+ elsif mode_r
84
+ Constants::O_RDONLY
85
+ else
86
+ Constants::O_WRONLY
87
+ end
88
+ mode_flags |= Constants::O_CREAT unless mode_r
89
+ mode_flags |= Constants::O_TRUNC if mode_w
90
+ mode_flags |= Constants::O_APPEND if mode_a
91
+ mode_flags |= Constants::O_EXCL if mode_x
92
+
93
+ handle_p = FFI::MemoryPointer.new(:pointer)
94
+
95
+ status = Lib.errlog_open(
96
+ path,
97
+ mode_flags,
98
+ Constants::LE_MAGIC,
99
+ handle_p,
100
+ )
101
+
102
+ Errors.throw(status, "path: #{path}, mode: #{mode}") unless status == :ok
103
+
104
+ @enum_active = false
105
+
106
+ # Just hold the handle directly
107
+ @handle = handle_p.get_pointer
108
+ end
109
+
110
+ ##
111
+ # Opens the given error log. Arguments are passed directly into ::new
112
+ #
113
+ # If a block is given, #close will be automatically called when the block
114
+ # exits, and the return value of the block will be the return value of
115
+ # this.
116
+ def self.open(*args)
117
+ errlog = new(*args)
118
+ if block_given?
119
+ begin
120
+ return yield errlog
121
+ ensure
122
+ errlog.close
123
+ end
124
+ else
125
+ log
126
+ end
127
+ end
128
+
129
+ ##
130
+ # Closes the handle. This must be called, either directly or indirectly
131
+ # through ::open with a block. This may be called multiple times, but
132
+ # after this is called, no other functions that try to use the errlog
133
+ # handle may be called.
134
+ def close
135
+ unless @handle.nil?
136
+ status = Lib.errlog_close(@handle)
137
+ Errors.throw(status, "handle: #{@handle}") unless status == :ok
138
+ @handle = nil
139
+ end
140
+ end
141
+
142
+ ##
143
+ # Enumerate log Entry objects in forward order (default is reverse). If
144
+ # no block is given, returns an enumerator.
145
+ #
146
+ # sequence specifies the sequence ID to start with. It will be included
147
+ # in the results if specifed
148
+ #
149
+ # match takes a Match object, which specifies which entries to match.
150
+ #
151
+ # match and sequence must not be both specified. If neither are
152
+ # specified, this simply iterates from the beginning (or from the previous
153
+ # stopped position, if #find_sequence or #find_first have already been
154
+ # called).
155
+ #
156
+ # An active enumerator can not be nested within another active enumerator,
157
+ # including the block form of this. If you invoke any of the #each_
158
+ # methods while another has not finished and exited, you'll raise an
159
+ # EnumeratorError. You can create an enumerator of one within the other,
160
+ # as long as you don't activate it until the first one has exited.
161
+ #
162
+ # Warning: if the sequence does not exist (which is common when error logs
163
+ # are cleaned), no entries will be returned, even if they follow the
164
+ # sequence ID. If you want all entries based on their sequence number,
165
+ # use match instead. You're usually better off using the timestamp
166
+ # instead of sequence number, because the sequence number is 32 bits and
167
+ # might wrap.
168
+ def forward_each(match: nil, sequence: nil, &block)
169
+ raise 'match and sequence must not be both specified' if match && sequence
170
+
171
+ return to_enum(:forward_each, match: match, sequence: sequence) unless block_given?
172
+ set_direction :forward
173
+ each(match: match, sequence: sequence, &block)
174
+ end
175
+
176
+ ##
177
+ # Enumerate log Entry objects in reverse order (default is reverse). If
178
+ # no block is given, returns an enumerator.
179
+ #
180
+ # sequence specifies the sequence ID to start with. It will be included
181
+ # in the results if specifed
182
+ #
183
+ # match takes a Match object, which specifies which entries to match.
184
+ #
185
+ # match and sequence must not be both specified. If neither are
186
+ # specified, this simply iterates from the beginning (or from the previous
187
+ # stopped position, if #find_sequence or #find_first have already been
188
+ # called).
189
+ #
190
+ # An active enumerator can not be nested within another active enumerator,
191
+ # including the block form of this. If you invoke any of the #each_
192
+ # methods while another has not finished and exited, you'll raise an
193
+ # EnumeratorError. You can create an enumerator of one within the other,
194
+ # as long as you don't activate it until the first one has exited.
195
+ #
196
+ # Warning: if the sequence does not exist (which is common when error logs
197
+ # are cleaned), no entries will be returned, even if they follow the
198
+ # sequence ID. If you want all entries based on their sequence number,
199
+ # use match instead. You're usually better off using the timestamp
200
+ # instead of sequence number, because the sequence number is 32 bits and
201
+ # might wrap.
202
+ def reverse_each(match: nil, sequence: nil, &block)
203
+ raise 'match and sequence must not be both specified' if match && sequence
204
+
205
+ return to_enum(:reverse_each, match: match, sequence: sequence) unless block_given?
206
+ set_direction :reverse
207
+ each(match: match, sequence: sequence, &block)
208
+ end
209
+
210
+ ##
211
+ # Match convenience function. Gets a Leaf match for comparing against
212
+ # sequence.
213
+ def match_sequence
214
+ Match.new(left: :sequence)
215
+ end
216
+ ##
217
+ # Match convenience function. Gets a Leaf match for comparing against
218
+ # label.
219
+ def match_label
220
+ Match.new(left: :label)
221
+ end
222
+ ##
223
+ # Match convenience function. Gets a Leaf match for comparing against
224
+ # timestamp.
225
+ def match_timestamp
226
+ Match.new(left: :timestamp)
227
+ end
228
+ ##
229
+ # Match convenience function. Gets a Leaf match for comparing against
230
+ # crcid.
231
+ def match_crcid
232
+ Match.new(left: :crcid)
233
+ end
234
+ ##
235
+ # Match convenience function. Gets a Leaf match for comparing against
236
+ # machineid.
237
+ def match_machineid
238
+ Match.new(left: :machineid)
239
+ end
240
+ ##
241
+ # Match convenience function. Gets a Leaf match for comparing against
242
+ # nodeid.
243
+ def match_nodeid
244
+ Match.new(left: :nodeid)
245
+ end
246
+ ##
247
+ # Match convenience function. Gets a Leaf match for comparing against
248
+ # class.
249
+ def match_class
250
+ Match.new(left: :class)
251
+ end
252
+ ##
253
+ # Match convenience function. Gets a Leaf match for comparing against
254
+ # type.
255
+ def match_type
256
+ Match.new(left: :type)
257
+ end
258
+ ##
259
+ # Match convenience function. Gets a Leaf match for comparing against
260
+ # resource.
261
+ def match_resource
262
+ Match.new(left: :resource)
263
+ end
264
+ ##
265
+ # Match convenience function. Gets a Leaf match for comparing against
266
+ # rclass.
267
+ def match_rclass
268
+ Match.new(left: :rclass)
269
+ end
270
+ ##
271
+ # Match convenience function. Gets a Leaf match for comparing against
272
+ # rtype.
273
+ def match_rtype
274
+ Match.new(left: :rtype)
275
+ end
276
+ ##
277
+ # Match convenience function. Gets a Leaf match for comparing against
278
+ # vpd_ibm.
279
+ def match_vpd_ibm
280
+ Match.new(left: :vpd_ibm)
281
+ end
282
+ ##
283
+ # Match convenience function. Gets a Leaf match for comparing against
284
+ # vpd_user.
285
+ def match_vpd_user
286
+ Match.new(left: :vpd_user)
287
+ end
288
+ ##
289
+ # Match convenience function. Gets a Leaf match for comparing against in.
290
+ def match_in
291
+ Match.new(left: :in)
292
+ end
293
+ ##
294
+ # Match convenience function. Gets a Leaf match for comparing against
295
+ # connwhere.
296
+ def match_connwhere
297
+ Match.new(left: :connwhere)
298
+ end
299
+ ##
300
+ # Match convenience function. Gets a Leaf match for comparing against
301
+ # flag_err64.
302
+ def match_flag_err64
303
+ Match.new(left: :flag_err64)
304
+ end
305
+ ##
306
+ # Match convenience function. Gets a Leaf match for comparing against
307
+ # flag_errdup.
308
+ def match_flag_errdup
309
+ Match.new(left: :flag_errdup)
310
+ end
311
+ ##
312
+ # Match convenience function. Gets a Leaf match for comparing against
313
+ # detail_data.
314
+ def match_detail_data
315
+ Match.new(left: :detail_data)
316
+ end
317
+ ##
318
+ # Match convenience function. Gets a Leaf match for comparing against
319
+ # symptom_data.
320
+ def match_symptom_data
321
+ Match.new(left: :symptom_data)
322
+ end
323
+ ##
324
+ # Match convenience function. Gets a Leaf match for comparing against
325
+ # errdiag.
326
+ def match_errdiag
327
+ Match.new(left: :errdiag)
328
+ end
329
+ ##
330
+ # Match convenience function. Gets a Leaf match for comparing against
331
+ # wparid.
332
+ def match_wparid
333
+ Match.new(left: :wparid)
334
+ end
335
+
336
+ private
337
+
338
+ ##
339
+ # Get an entry for the specified sequence, if there is any, and nil
340
+ # otherwise.
341
+ def find_sequence(id)
342
+ entry = Lib::ErrlogEntry.new
343
+ status = Lib.errlog_find_sequence(@handle, id, entry)
344
+ return if status == :done
345
+ Errors.throw(status, "handle: #{@handle}, sequence: #{id}") unless status == :ok
346
+
347
+ Entry.new(entry).freeze
348
+ end
349
+
350
+ ##
351
+ # Get an entry matching the passed-in Match, if there is any, and nil
352
+ # otherwise.
353
+ def find_first(match)
354
+ entry = Lib::ErrlogEntry.new
355
+ status = Lib.errlog_find_first(@handle, match.to_struct, entry)
356
+ return if status == :done
357
+ Errors.throw(status, "handle: #{@handle}, match: #{match}") unless status == :ok
358
+
359
+ Entry.new(entry).freeze
360
+ end
361
+
362
+ ##
363
+ # Get the next entry.
364
+ def find_next
365
+ entry = Lib::ErrlogEntry.new
366
+ status = Lib.errlog_find_next(@handle, entry)
367
+ return if status == :done
368
+ Errors.throw(status, "handle: #{@handle}") unless status == :ok
369
+ Entry.new(entry).freeze
370
+ end
371
+
372
+ ##
373
+ # Sets the search direction for iteration.
374
+ def set_direction(direction)
375
+ status = Lib.errlog_set_direction(@handle, direction)
376
+ Errors.throw(status, "handle: #{@handle}, direction: #{direction}") unless status == :ok
377
+ end
378
+
379
+ ##
380
+ # Enumerate log entries in the order set in #set_direction (default is
381
+ # reverse).
382
+ #
383
+ # sequence specifies the sequence ID to start with. It will be included
384
+ # in the results if specifed
385
+ #
386
+ # match takes a Match object, which specifies which entries to match.
387
+ #
388
+ # match and sequence must not be both specified. If neither are
389
+ # specified, this simply iterates from the beginning (or from the previous
390
+ # stopped position, if #find_sequence or #find_first have already been
391
+ # called).
392
+ #
393
+ # An active enumerator can not be nested within another active enumerator,
394
+ # including the block form of this. If you invoke any of the #each_
395
+ # methods while another has not finished and exited, you'll raise an
396
+ # EnumeratorError. You can create an enumerator of one within the other,
397
+ # as long as you don't activate it until the first one has exited.
398
+ #
399
+ # Warning: if the sequence does not exist (which is common when error logs
400
+ # are cleaned), no entries will be returned, even if they follow the
401
+ # sequence ID. If you want all entries based on their sequence number,
402
+ # use match instead. You're usually better off using the timestamp
403
+ # instead of sequence number, because the sequence number is 32 bits and
404
+ # might wrap.
405
+ #
406
+ # The user-facing entry points to this are #forward_each and #reverse_each
407
+ def each(match: nil, sequence: nil)
408
+ # Does not return an enumerator, because this will always be called with
409
+ # an active block
410
+
411
+ begin
412
+ raise Errors::EnumeratorError if @enum_active
413
+ @enum_active = true
414
+
415
+ if sequence
416
+ entry = find_sequence(sequence)
417
+ return if entry.nil?
418
+ yield entry
419
+ end
420
+
421
+ if match
422
+ entry = find_first(match)
423
+ return if entry.nil?
424
+ yield entry
425
+ end
426
+
427
+ while entry = find_next
428
+ yield entry
429
+ end
430
+ ensure
431
+ @enum_active = false
432
+ end
433
+ end
434
+ end
435
+ end
436
+ end
@@ -0,0 +1,54 @@
1
+ module AIX
2
+ module Errlog
3
+ module Errors
4
+ class EnumeratorError < StandardError
5
+ def initialize
6
+ super 'Do not nest enumerators, or invoke another one while the first is still active.'
7
+ end
8
+ end
9
+ class ErrlogError < StandardError
10
+ end
11
+ class InvalidArgument < ErrlogError
12
+ def initialize(message)
13
+ super "A parameter error was detected. Detail: #{message}"
14
+ end
15
+ end
16
+ class NoFile < ErrlogError
17
+ def initialize(message)
18
+ super "The log file does not exist. Detail: #{message}"
19
+ end
20
+ end
21
+ class NoMem < ErrlogError
22
+ def initialize(message)
23
+ super "Memory could not be allocated. Detail: #{message}"
24
+ end
25
+ end
26
+ class IO < ErrlogError
27
+ def initialize(message)
28
+ super "An i/o error occurred. Detail: #{message}"
29
+ end
30
+ end
31
+ class InvalidFile < ErrlogError
32
+ def initialize(message)
33
+ super "The file is not a valid error log. Detail: #{message}"
34
+ end
35
+ end
36
+ class UnknownError < ErrlogError
37
+ def initialize(message)
38
+ super "An error occured that could not be diagnosed. Detail: #{message}"
39
+ end
40
+ end
41
+ LOOKUP = {
42
+ invarg: InvalidArgument,
43
+ nofile: NoFile,
44
+ nomem: NoMem,
45
+ io: IO,
46
+ invfile: InvalidFile,
47
+ }
48
+ def self.throw(status, detail)
49
+ errorClass = LOOKUP[status] || UnknownError
50
+ raise errorClass, detail
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,167 @@
1
+ require 'ffi'
2
+
3
+ require 'aix/errlog/constants'
4
+
5
+ module AIX
6
+ module Errlog
7
+ ##
8
+ # The ffi interface to the errlog library.
9
+ #
10
+ # You should never need to access this directly. All necessary functionality
11
+ # should be accessible via Errlog.
12
+ module Lib
13
+ extend FFI::Library
14
+ ffi_lib 'liberrlog.a(shr_64.o)'
15
+
16
+ typedef :pointer, :errlog_handle_t
17
+ typedef :pointer, :errlog_handle_t_ptr
18
+
19
+ Err = enum(
20
+ :ok, 0,
21
+ :invarg, Constants::LE_ERR_INVARG,
22
+ :nofile, Constants::LE_ERR_NOFILE,
23
+ :invfile, Constants::LE_ERR_INVFILE,
24
+ :nomem, Constants::LE_ERR_NOMEM,
25
+ :nowrite, Constants::LE_ERR_NOWRITE,
26
+ :io, Constants::LE_ERR_IO,
27
+ :done, Constants::LE_ERR_DONE,
28
+ )
29
+
30
+ Direction = enum(
31
+ :forward, Constants::LE_FORWARD,
32
+ :reverse, Constants::LE_REVERSE,
33
+ )
34
+
35
+ Operator = enum(
36
+ :equal, Constants::LE_OP_EQUAL,
37
+ :ne, Constants::LE_OP_NE,
38
+ :substr, Constants::LE_OP_SUBSTR,
39
+ :lt, Constants::LE_OP_LT,
40
+ :le, Constants::LE_OP_LE,
41
+ :gt, Constants::LE_OP_GT,
42
+ :ge, Constants::LE_OP_GE,
43
+ :not, Constants::LE_OP_NOT,
44
+ :and, Constants::LE_OP_AND,
45
+ :or, Constants::LE_OP_OR,
46
+ :xor, Constants::LE_OP_XOR,
47
+ )
48
+
49
+ Match = enum(
50
+ :sequence, Constants::LE_MATCH_SEQUENCE,
51
+ :label, Constants::LE_MATCH_LABEL,
52
+ :timestamp, Constants::LE_MATCH_TIMESTAMP,
53
+ :crcid, Constants::LE_MATCH_CRCID,
54
+ :machineid, Constants::LE_MATCH_MACHINEID,
55
+ :nodeid, Constants::LE_MATCH_NODEID,
56
+ :class, Constants::LE_MATCH_CLASS,
57
+ :type, Constants::LE_MATCH_TYPE,
58
+ :resource, Constants::LE_MATCH_RESOURCE,
59
+ :rclass, Constants::LE_MATCH_RCLASS,
60
+ :rtype, Constants::LE_MATCH_RTYPE,
61
+ :vpd_ibm, Constants::LE_MATCH_VPD_IBM,
62
+ :vpd_user, Constants::LE_MATCH_VPD_USER,
63
+ :in, Constants::LE_MATCH_IN,
64
+ :connwhere, Constants::LE_MATCH_CONNWHERE,
65
+ :flag_err64, Constants::LE_MATCH_FLAG_ERR64,
66
+ :flag_errdup, Constants::LE_MATCH_FLAG_ERRDUP,
67
+ :detail_data, Constants::LE_MATCH_DETAIL_DATA,
68
+ :symptom_data, Constants::LE_MATCH_SYMPTOM_DATA,
69
+ :errdiag, Constants::LE_MATCH_ERRDIAG,
70
+ :wparid, Constants::LE_MATCH_WPARID,
71
+ )
72
+
73
+ class Errdup < FFI::Struct
74
+ layout(
75
+ :ed_dupcount, :uint,
76
+ :ed_time1, :uint32,
77
+ :ed_time2, :uint32,
78
+ )
79
+ end
80
+
81
+ class ErrlogEntry < FFI::Struct
82
+ layout(
83
+ :el_magic, :uint,
84
+ :el_sequence, :uint,
85
+ :el_label, [:char, Constants::LE_LABEL_MAX],
86
+ :el_timestamp, :uint,
87
+ :el_crcid, :uint,
88
+ :el_errdiag, :uint,
89
+ :el_machineid, [:char, Constants::LE_MACHINE_ID_MAX],
90
+ :el_nodeid, [:char, Constants::LE_NODE_ID_MAX],
91
+ :el_class, [:char, Constants::LE_CLASS_MAX],
92
+ :el_type, [:char, Constants::LE_TYPE_MAX],
93
+ :el_resource, [:char, Constants::LE_RESOURCE_MAX],
94
+ :el_rclass, [:char, Constants::LE_RCLASS_MAX],
95
+ :el_rtype, [:char, Constants::LE_RTYPE_MAX],
96
+ :el_vpd_ibm, [:char, Constants::LE_VPD_MAX],
97
+ :el_vpd_user, [:char, Constants::LE_VPD_MAX],
98
+ :el_in, [:char, Constants::LE_IN_MAX],
99
+ :el_connwhere, [:char, Constants::LE_CONN_MAX],
100
+ :el_flags, :ushort,
101
+ :el_detail_length, :ushort,
102
+ :el_detail_data, [:char, Constants::LE_DETAIL_MAX],
103
+ :el_symptom_length, :uint,
104
+ :el_symptom_data, [:char, Constants::LE_SYMPTOM_MAX],
105
+ :el_errdup, Errdup,
106
+ :el_wparid, [:char, Constants::LE_WPAR_ID_MAX],
107
+ )
108
+ end
109
+
110
+ class ErrlogMatch1U < FFI::Union
111
+ layout(
112
+ :emu_left, :pointer,
113
+ :emu_field, Match,
114
+ )
115
+ end
116
+
117
+ class ErrlogMatch2U < FFI::Union
118
+ layout(
119
+ :emu_right, :pointer,
120
+ :emu_intvalue, :uint,
121
+ :emu_strvalue, :string,
122
+ )
123
+ end
124
+
125
+ class ErrlogMatch < FFI::Struct
126
+ layout(
127
+ :em_op, Operator,
128
+ :emu1, ErrlogMatch1U,
129
+ :emu2, ErrlogMatch2U,
130
+ )
131
+ end
132
+
133
+ attach_function :errlog_open, [
134
+ :string, # path
135
+ :int, # mode
136
+ :uint, # magic
137
+ :errlog_handle_t_ptr, # handle
138
+ ], Err
139
+
140
+ attach_function :errlog_close, [
141
+ :errlog_handle_t, # handle
142
+ ], Err
143
+
144
+ attach_function :errlog_find_first, [
145
+ :errlog_handle_t, # handle
146
+ ErrlogMatch, # filter
147
+ ErrlogEntry, # result
148
+ ], Err
149
+
150
+ attach_function :errlog_find_next, [
151
+ :errlog_handle_t, # handle
152
+ ErrlogEntry, # result
153
+ ], Err
154
+
155
+ attach_function :errlog_find_sequence, [
156
+ :errlog_handle_t, # handle
157
+ :int, # sequence
158
+ ErrlogEntry, # result
159
+ ], Err
160
+
161
+ attach_function :errlog_set_direction, [
162
+ :errlog_handle_t, # handle
163
+ Direction, # direction
164
+ ], Err
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,156 @@
1
+ require 'date'
2
+
3
+ require 'aix/errlog/constants'
4
+ require 'aix/errlog/lib'
5
+
6
+ module AIX
7
+ module Errlog
8
+ ##
9
+ # A class that is useful for building errlog matchers.
10
+ #
11
+ # You usually won't need to access this class directly; you'll be able to
12
+ # create instances of it indirectly through the field accessors that are
13
+ # available in Errlog.
14
+ #
15
+ # You can build field matchers using the methods on the errlog and standard
16
+ # logical operators, and there are standard conversions for certain things
17
+ # like Time and DateTime operations. For instance, if you wanted errlog
18
+ # entries only in January 2018 with +FOO+ in the label somewhere, you cold
19
+ # use a match like this with an Errlog instance.
20
+ #
21
+ # (
22
+ # (errlog.match_timestamp >= DateTime.new(2018, 1, 1)) &
23
+ # (errlog.match_timestamp < DateTime.new(2018, 2, 1)) &
24
+ # errlog.match_label.include?('FOO')
25
+ # )
26
+ #
27
+ # It really is that easy. The rest of the magic is done for you behind the
28
+ # scenes, as long as you follow the rules and know how to make these matches
29
+ # in the C equivalent. Note that there must always be a Match on the left
30
+ # side of all comparisons, so something like
31
+ #
32
+ # DateTime.new(2018, 1, 1) <= errlog.match_timestamp
33
+ #
34
+ # is not possible.
35
+ #
36
+ # This class should allow you to build matches for errlog_find_first in a
37
+ # simple and natural way. Note that & is the +LE_OP_AND+ operator, and | is
38
+ # the +LE_OP_OR+ operator, not &&, +and+, ||, or +or+, because those can't
39
+ # be overridden.
40
+ class Match
41
+ attr_accessor :left, :operator, :right
42
+
43
+ def initialize(left:, operator: nil, right: nil)
44
+ @left = left
45
+ @operator = operator
46
+ @right = right
47
+ end
48
+
49
+ # Uses the structure of this object to build a errlog_match_t structure.
50
+ # Does not check whether operators only work on leaves or any other
51
+ # specifics as that (for instance, an and operator needs two Match leaves,
52
+ # and won't work between a field and a Match or anything like that). The
53
+ # function itself might check some of the operators to make sure that
54
+ # they're set and throw an error for you, but something like a segfault is
55
+ # more likely if you screw up the structure. Make sure you know what
56
+ # you're doing.
57
+ def to_struct
58
+ raise "operator must be a symbol, but is #{@operator}" unless @operator.is_a? Symbol
59
+
60
+ # We want to be sure the struct is not garbage collected before it's
61
+ # used, so we need to retain a reference to it that ensures that it will
62
+ # live as long as this object
63
+ @struct = Lib::ErrlogMatch.new
64
+
65
+ @struct[:em_op] = @operator
66
+
67
+ case @left
68
+ when Match
69
+ @struct[:emu1][:emu_left] = @left.to_struct
70
+ when Symbol
71
+ @struct[:emu1][:emu_field] = @left
72
+ else
73
+ raise "left should be either a Match or Symbol object, but is #{@left}"
74
+ end
75
+
76
+ case @right
77
+ when Match
78
+ @struct[:emu2][:emu_right] = @right.to_struct
79
+ when String
80
+ @struct[:emu2][:emu_strvalue] = @right
81
+ when Numeric, Time
82
+ @struct[:emu2][:emu_intvalue] = @right.to_i
83
+ when DateTime
84
+ @struct[:emu2][:emu_intvalue] = @right.to_time.to_i
85
+ else
86
+ raise "left should be either a Match or Symbol object, but is #{@left}"
87
+ end
88
+
89
+ @struct
90
+ end
91
+
92
+ def ==(other)
93
+ @operator = :equal
94
+ @right = other
95
+ self
96
+ end
97
+ def !=(other)
98
+ @operator = :ne
99
+ @right = other
100
+ self
101
+ end
102
+ def include?(other)
103
+ @operator = :substr
104
+ @right = other
105
+ self
106
+ end
107
+ def <(other)
108
+ @operator = :lt
109
+ @right = other
110
+ self
111
+ end
112
+ def <=(other)
113
+ @operator = :le
114
+ @right = other
115
+ self
116
+ end
117
+ def >(other)
118
+ @operator = :gt
119
+ @right = other
120
+ self
121
+ end
122
+ def >=(other)
123
+ @operator = :ge
124
+ @right = other
125
+ self
126
+ end
127
+ def &(other)
128
+ Match.new(
129
+ left: self,
130
+ operator: :and,
131
+ right: other,
132
+ )
133
+ end
134
+ def |(other)
135
+ Match.new(
136
+ left: self,
137
+ operator: :or,
138
+ right: other,
139
+ )
140
+ end
141
+ def ^(other)
142
+ Match.new(
143
+ left: self,
144
+ operator: :xor,
145
+ right: other,
146
+ )
147
+ end
148
+ def !
149
+ Match.new(
150
+ left: self,
151
+ operator: :not,
152
+ )
153
+ end
154
+ end
155
+ end
156
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aix-errlog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taylor C. Richberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-22 00:00:00.000000000 Z
11
+ date: 2018-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -32,7 +32,11 @@ extra_rdoc_files: []
32
32
  files:
33
33
  - lib/aix/errlog.rb
34
34
  - lib/aix/errlog/constants.rb
35
- - lib/aix/errlog/ffi.rb
35
+ - lib/aix/errlog/entry.rb
36
+ - lib/aix/errlog/errlog.rb
37
+ - lib/aix/errlog/errors.rb
38
+ - lib/aix/errlog/lib.rb
39
+ - lib/aix/errlog/match.rb
36
40
  homepage: https://github.com/absperf/aix-errlog
37
41
  licenses:
38
42
  - MIT
File without changes