aix-errlog 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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