systemd-journal 1.0.2 → 1.1.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
  SHA1:
3
- metadata.gz: 6f9a1f4583f555e4c8832ce277fc87d02eeabb2f
4
- data.tar.gz: a4bc24e388cf2211574a5f138df8d05dc7f97505
3
+ metadata.gz: df7fe5c76f75b85e1c6ae332b5dc4db8268956c9
4
+ data.tar.gz: ddb32ec9f34855f7fe8ae5bfef85f5c8606b9295
5
5
  SHA512:
6
- metadata.gz: 25274b48c5eba590dd4904f10142932fc4c6458170a49f93453c4d7e0cbb9822c6d045e2d9536f0d79244528081d2725fe99ab1aafacb3ebb4ae891a2076e05c
7
- data.tar.gz: 2f863c1a90d35b999115eedd3b160b5d36db4cc273f80fdad5a91fca74dcc9f8c1189b9e1cd9c47dbc6e69df3b180d02ab15be8718ec46383539ebc1e22c14ce
6
+ metadata.gz: 1aa3dd8871ac3b44fe9e063a5fef7ed300f2580a76ebb0a122951dd1136fe86c99322f5342515fafcaf723c337b90ccc59a4a89c47cb02fe007912ba9e493b6f
7
+ data.tar.gz: 30018c03c4cc3178e2798c9f7edf60d290cdef165ee41bccc5a9256f0fe922b5f9473a6f6fb6d3fe8662f56111eef6878729874b05bc69b06729c4e521a1c6b5
data/.rubocop.yml CHANGED
@@ -6,3 +6,12 @@ RaiseArgs:
6
6
 
7
7
  SpaceAroundBlockBraces:
8
8
  EnforcedStyle: space_inside_braces
9
+ Lambda:
10
+ Enabled: false
11
+
12
+ GlobalVars:
13
+ AllowedVariables:
14
+ - $NO_FFI_SPEC
15
+
16
+ Documentation:
17
+ Enabled: false
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Systemd::Journal [![Gem Version](https://badge.fury.io/rb/systemd-journal.png)](http://badge.fury.io/rb/systemd-journal) [![Build Status](https://travis-ci.org/ledbettj/systemd-journal.png?branch=master)](https://travis-ci.org/ledbettj/systemd-journal)
1
+ # Systemd::Journal [![Gem Version](https://badge.fury.io/rb/systemd-journal.png)](http://badge.fury.io/rb/systemd-journal) [![Build Status](https://travis-ci.org/ledbettj/systemd-journal.png?branch=master)](https://travis-ci.org/ledbettj/systemd-journal) [![Code Climate](https://codeclimate.com/github/ledbettj/systemd-journal.png)](https://codeclimate.com/github/ledbettj/systemd-journal)
2
2
 
3
3
  Ruby bindings for reading from the systemd journal.
4
4
 
@@ -9,7 +9,7 @@ Ruby bindings for reading from the systemd journal.
9
9
 
10
10
  Add this line to your application's Gemfile:
11
11
 
12
- gem 'systemd-journal', '~> 1.0.0'
12
+ gem 'systemd-journal', '~> 1.1.0'
13
13
 
14
14
  And then execute:
15
15
 
@@ -62,6 +62,27 @@ Moving around the journal:
62
62
  # seek the entry that occured closest to this time
63
63
  j.seek(Time.parse("2013-10-31T12:00:00+04:00:00"))
64
64
 
65
+ Waiting for things to happen:
66
+
67
+ j = Systemd::Journal.new
68
+ j.seek(:tail)
69
+ # wait up to one second for something to happen
70
+ if j.wait(1_000_000)
71
+ puts 'something changed!'
72
+ # same as above, but can be interrupted with Control+C.
73
+ if j.wait(1_000_000, select: true)
74
+ puts 'something changed!'
75
+
76
+ Accessing the catalog:
77
+
78
+ j = Systemd::Journal.new
79
+ j.move_next
80
+ j.move_next while !j.current_entry.catalog?
81
+
82
+ puts j.current_entry.catalog
83
+ # or if you have a message id:
84
+ puts Systemd::Journal.catalog_for(j.current_entry.message_id)
85
+
65
86
 
66
87
  See the documentation for more examples.
67
88
 
@@ -78,3 +99,5 @@ If you run into problems or have questions, please open an
78
99
  4. Push to the branch (`git push origin my-new-feature`)
79
100
  5. Create new Pull Request, targeting the __develop__ branch.
80
101
  6. Wipe hands on pants, you're done.
102
+
103
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/ledbettj/systemd-journal/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
data/Rakefile CHANGED
@@ -1,15 +1,15 @@
1
- require "bundler/gem_tasks"
2
- require "yard"
3
- require "rspec/core/rake_task"
4
- require "rubocop/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'yard'
3
+ require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
5
5
 
6
- desc "open a console with systemd/journal required"
6
+ desc 'open a console with systemd/journal required'
7
7
  task :console do
8
8
  exec 'pry -I./lib -r systemd/journal'
9
9
  end
10
10
 
11
11
  Rubocop::RakeTask.new(:rubocop) do |task|
12
- task.patterns = ['lib/**/*.rb']
12
+ task.patterns = ['lib/**/*.rb', 'spec/**/*.rb']
13
13
  task.fail_on_error = false
14
14
  end
15
15
 
data/lib/systemd/id128.rb CHANGED
@@ -62,6 +62,15 @@ module Systemd
62
62
  def to_s
63
63
  ('%02x' * 16) % self[:bytes].to_a
64
64
  end
65
+
66
+ def self.from_s(str)
67
+ r = Id128.new
68
+ [str].pack('H*').bytes.each_with_index do |b, i|
69
+ r[:bytes][i] = b
70
+ end
71
+
72
+ r
73
+ end
65
74
  end
66
75
  end
67
76
  end
@@ -1,7 +1,10 @@
1
1
  require 'systemd/journal/native'
2
2
  require 'systemd/journal/flags'
3
- require 'systemd/journal/compat'
3
+ require 'systemd/journal/writable'
4
4
  require 'systemd/journal/fields'
5
+ require 'systemd/journal/navigable'
6
+ require 'systemd/journal/filterable'
7
+ require 'systemd/journal/waitable'
5
8
  require 'systemd/journal_error'
6
9
  require 'systemd/journal_entry'
7
10
  require 'systemd/id128'
@@ -12,11 +15,14 @@ module Systemd
12
15
  # Class to allow interacting with the systemd journal.
13
16
  # To read from the journal, instantiate a new {Systemd::Journal}; to write to
14
17
  # the journal, use
15
- # {Systemd::Journal::Compat::ClassMethods#message Journal.message} or
16
- # {Systemd::Journal::Compat::ClassMethods#print Journal.print}.
18
+ # {Systemd::Journal::Writable::ClassMethods#message Journal.message} or
19
+ # {Systemd::Journal::Writable::ClassMethods#print Journal.print}.
17
20
  class Journal
18
21
  include Enumerable
19
- include Systemd::Journal::Compat
22
+ include Systemd::Journal::Writable
23
+ include Systemd::Journal::Navigable
24
+ include Systemd::Journal::Filterable
25
+ include Systemd::Journal::Waitable
20
26
 
21
27
  # Returns a new instance of a Journal, opened with the provided options.
22
28
  # @param [Hash] opts optional initialization parameters.
@@ -60,115 +66,6 @@ module Systemd
60
66
  yield current_entry while move_next
61
67
  end
62
68
 
63
- # Move the read pointer by `offset` entries.
64
- # @param [Integer] offset how many entries to move the read pointer by. If
65
- # this value is positive, the read pointer moves forward. Otherwise, it
66
- # moves backwards.
67
- # @return [Integer] the number of entries the read pointer actually moved.
68
- def move(offset)
69
- offset > 0 ? move_next_skip(offset) : move_previous_skip(-offset)
70
- end
71
-
72
- # Filter the journal at a high level.
73
- # Takes any number of arguments; each argument should be a hash representing
74
- # a condition to filter based on. Fields inside the hash will be ANDed
75
- # together. Each hash will be ORed with the others. Fields in hashes with
76
- # Arrays as values are treated as an OR statement, since otherwise they
77
- # would never match.
78
- # @example
79
- # j = Systemd::Journal.filter(
80
- # {_systemd_unit: 'session-4.scope'},
81
- # {priority: [4, 6]},
82
- # {_exe: '/usr/bin/sshd', priority: 1}
83
- # )
84
- # # equivalent to
85
- # (_systemd_unit == 'session-4.scope') ||
86
- # (priority == 4 || priority == 6) ||
87
- # (_exe == '/usr/bin/sshd' && priority == 1)
88
- def filter(*conditions)
89
- clear_filters
90
-
91
- last_index = conditions.length - 1
92
-
93
- conditions.each_with_index do |condition, index|
94
- add_filters(condition)
95
- add_disjunction unless index == last_index
96
- end
97
- end
98
-
99
- # Move the read pointer to the next entry in the journal.
100
- # @return [Boolean] True if moving to the next entry was successful.
101
- # @return [Boolean] False if unable to move to the next entry, indicating
102
- # that the pointer has reached the end of the journal.
103
- def move_next
104
- rc = Native.sd_journal_next(@ptr)
105
- raise JournalError.new(rc) if rc < 0
106
- rc > 0
107
- end
108
-
109
- # Move the read pointer forward by `amount` entries.
110
- # @return [Integer] the actual number of entries by which the read pointer
111
- # moved. If this number is less than the requested amount, the read
112
- # pointer has reached the end of the journal.
113
- def move_next_skip(amount)
114
- rc = Native.sd_journal_next_skip(@ptr, amount)
115
- raise JournalError.new(rc) if rc < 0
116
- rc
117
- end
118
-
119
- # Move the read pointer to the previous entry in the journal.
120
- # @return [Boolean] True if moving to the previous entry was successful.
121
- # @return [Boolean] False if unable to move to the previous entry,
122
- # indicating that the pointer has reached the beginning of the journal.
123
- def move_previous
124
- rc = Native.sd_journal_previous(@ptr)
125
- raise JournalError.new(rc) if rc < 0
126
- rc > 0
127
- end
128
-
129
- # Move the read pointer backwards by `amount` entries.
130
- # @return [Integer] the actual number of entries by which the read pointer
131
- # was moved. If this number is less than the requested amount, the read
132
- # pointer has reached the beginning of the journal.
133
- def move_previous_skip(amount)
134
- rc = Native.sd_journal_previous_skip(@ptr, amount)
135
- raise JournalError.new(rc) if rc < 0
136
- rc
137
- end
138
-
139
- # Seek to a position in the journal.
140
- # Note: after seeking, you must call {#move_next} or {#move_previous}
141
- # before you can call {#read_field} or {#current_entry}.
142
- #
143
- # @param [Symbol, Time] whence one of :head, :tail, or a Time instance.
144
- # `:head` (or `:start`) will seek to the beginning of the journal.
145
- # `:tail` (or `:end`) will seek to the end of the journal. When a `Time`
146
- # is provided, seek to the journal entry logged closest to that time. When
147
- # a String is provided, assume it is a cursor from {#cursor} and seek to
148
- # that entry.
149
- # @return [True]
150
- def seek(whence)
151
- rc = case whence
152
- when :head, :start
153
- Native.sd_journal_seek_head(@ptr)
154
- when :tail, :end
155
- Native.sd_journal_seek_tail(@ptr)
156
- else
157
- if whence.is_a?(Time)
158
- # TODO: is this right? who knows.
159
- Native.sd_journal_seek_realtime_usec(@ptr, whence.to_i * 1_000_000)
160
- elsif whence.is_a?(String)
161
- Native.sd_journal_seek_cursor(@ptr, whence)
162
- else
163
- raise ArgumentError.new("Unknown seek type: #{whence.class}")
164
- end
165
- end
166
-
167
- raise JournalErrornew(rc) if rc < 0
168
-
169
- true
170
- end
171
-
172
69
  # Read the contents of the provided field from the current journal entry.
173
70
  # {#move_next} or {#move_previous} must be called at least once after
174
71
  # initialization or seeking prior to attempting to read data.
@@ -215,6 +112,27 @@ module Systemd
215
112
  JournalEntry.new(results)
216
113
  end
217
114
 
115
+ def current_catalog
116
+ out_ptr = FFI::MemoryPointer.new(:pointer, 1)
117
+
118
+ rc = Native.sd_journal_get_catalog(@ptr, out_ptr)
119
+ raise JournalError.new(rc) if rc < 0
120
+
121
+ Journal.read_and_free_outstr(out_ptr.read_pointer)
122
+ end
123
+
124
+ def self.catalog_for(message_id)
125
+ out_ptr = FFI::MemoryPointer.new(:pointer, 1)
126
+
127
+ rc = Native.sd_journal_get_catalog_for_message_id(
128
+ Systemd::Id128::Native::Id128.from_s(message_id),
129
+ out_ptr
130
+ )
131
+ raise JournalError.new(rc) if rc < 0
132
+
133
+ read_and_free_outstr(out_ptr.read_pointer)
134
+ end
135
+
218
136
  # Get the list of unique values stored in the journal for the given field.
219
137
  # If passed a block, each possible value will be yielded.
220
138
  # @return [Array] the list of possible values.
@@ -241,112 +159,6 @@ module Systemd
241
159
  results
242
160
  end
243
161
 
244
- # Block until the journal is changed.
245
- # @param timeout_usec [Integer] the maximum number of microseconds to wait
246
- # or `-1` to wait indefinitely.
247
- # @example Wait for an event for a maximum of 3 seconds
248
- # j = Systemd::Journal.new
249
- # j.seek(:tail)
250
- # if j.wait(3 * 1_000_000)
251
- # # event occurred
252
- # end
253
- # @return [Nil] if the wait time was reached (no events occured).
254
- # @return [Symbol] :append if new entries were appened to the journal.
255
- # @return [Symbol] :invalidate if journal files were added/removed/rotated.
256
- def wait(timeout_usec = -1)
257
- rc = Native.sd_journal_wait(@ptr, timeout_usec)
258
- raise JournalError.new(rc) if rc.is_a?(Fixnum) && rc < 0
259
- rc == :nop ? nil : rc
260
- end
261
-
262
- # Blocks and waits for new entries to be appended to the journal. When new
263
- # entries are written, yields them in turn. Note that this function does
264
- # not automatically seek to the end of the journal prior to waiting.
265
- # This method Does not return.
266
- # @example Print out events as they happen
267
- # j = Systemd::Journal.new
268
- # j.seek(:tail)
269
- # j.watch do |event|
270
- # puts event.message
271
- # end
272
- def watch
273
- loop do
274
- if wait
275
- yield current_entry while move_next
276
- end
277
- end
278
- end
279
-
280
- # Add a filter to journal, such that only entries where the given filter
281
- # matches are returned.
282
- # {#move_next} or {#move_previous} must be invoked after adding a filter
283
- # before attempting to read from the journal.
284
- # @param [String] field the column to filter on, e.g. _PID, _EXE.
285
- # @param [String] value the match to search for, e.g. '/usr/bin/sshd'
286
- # @return [nil]
287
- def add_filter(field, value)
288
- match = "#{field.to_s.upcase}=#{value}"
289
- rc = Native.sd_journal_add_match(@ptr, match, match.length)
290
- raise JournalError.new(rc) if rc < 0
291
- end
292
-
293
- # Add a set of filters to the journal, such that only entries where the
294
- # given filters match are returned.
295
- # @param [Hash] filters a set of field/filter value pairs.
296
- # If the filter value is an array, each value in the array is added
297
- # and entries where the specified field matches any of the values is
298
- # returned.
299
- # @example Filter by PID and EXE
300
- # j.add_filters(_pid: 6700, _exe: '/usr/bin/sshd')
301
- def add_filters(filters)
302
- filters.each do |field, value|
303
- Array(value).each{ |v| add_filter(field, v) }
304
- end
305
- end
306
-
307
- # Add an OR condition to the filter. All previously added matches
308
- # will be ORed with the terms following the disjunction.
309
- # {#move_next} or {#move_previous} must be invoked after adding a match
310
- # before attempting to read from the journal.
311
- # @return [nil]
312
- # @example Filter entries returned using an OR condition
313
- # j = Systemd::Journal.new
314
- # j.add_filter('PRIORITY', 5)
315
- # j.add_disjunction
316
- # j.add_filter('_EXE', '/usr/bin/sshd')
317
- # while j.move_next
318
- # # current_entry is either an sshd event or
319
- # # has priority 5
320
- # end
321
- def add_disjunction
322
- rc = Native.sd_journal_add_disjunction(@ptr)
323
- raise JournalError.new(rc) if rc < 0
324
- end
325
-
326
- # Add an AND condition to the filter. All previously added terms will be
327
- # ANDed together with terms following the conjunction.
328
- # {#move_next} or {#move_previous} must be invoked after adding a match
329
- # before attempting to read from the journal.
330
- # @return [nil]
331
- # @example Filter entries returned using an AND condition
332
- # j = Systemd::Journal.new
333
- # j.add_filter('PRIORITY', 5)
334
- # j.add_conjunction
335
- # j.add_filter('_EXE', '/usr/bin/sshd')
336
- # while j.move_next
337
- # # current_entry is an sshd event with priority 5
338
- # end
339
- def add_conjunction
340
- rc = Native.sd_journal_add_conjunction(@ptr)
341
- raise JournalError.new(rc) if rc < 0
342
- end
343
-
344
- # Remove all filters and conjunctions/disjunctions.
345
- # @return [nil]
346
- def clear_filters
347
- Native.sd_journal_flush_matches(@ptr)
348
- end
349
-
350
162
  # Get the number of bytes the Journal is currently using on disk.
351
163
  # If {Systemd::Journal::Flags::LOCAL_ONLY} was passed when opening the
352
164
  # journal, this value will only reflect the size of journal files of the
@@ -380,35 +192,10 @@ module Systemd
380
192
  end
381
193
  end
382
194
 
383
- # returns a string representing the current read position.
384
- # This string can be passed to {#seek} or {#cursor?}.
385
- # @return [String] a cursor token.
386
- def cursor
387
- out_ptr = FFI::MemoryPointer.new(:pointer, 1)
388
- if (rc = Native.sd_journal_get_cursor(@ptr, out_ptr)) < 0
389
- raise JournalError.new(rc)
390
- end
391
-
392
- read_and_free_outstr(out_ptr.read_pointer)
393
- end
394
-
395
- # Check if the read position is currently at the entry represented by the
396
- # provided cursor value.
397
- # @param c [String] a cursor token returned from {#cursor}.
398
- # @return [Boolean] True if the current entry is the one represented by the
399
- # provided cursor, False otherwise.
400
- def cursor?(c)
401
- if (rc = Native.sd_journal_test_cursor(@ptr, c)) < 0
402
- raise JournalError.new(rc)
403
- end
404
-
405
- rc > 0
406
- end
407
-
408
195
  private
409
196
 
410
197
  def self.finalize(ptr)
411
- proc{ Native.sd_journal_close(ptr) unless ptr.nil? }
198
+ proc { Native.sd_journal_close(ptr) unless ptr.nil? }
412
199
  end
413
200
 
414
201
  def enumerate_helper(enum_function)
@@ -430,7 +217,7 @@ module Systemd
430
217
  # some sd_journal_* functions return strings that we're expected to free
431
218
  # ourselves. This function copies the string from a char* to a ruby string,
432
219
  # frees the char*, and returns the ruby string.
433
- def read_and_free_outstr(ptr)
220
+ def self.read_and_free_outstr(ptr)
434
221
  str = ptr.read_string
435
222
  LibC.free(ptr)
436
223
  str
@@ -0,0 +1,102 @@
1
+ module Systemd
2
+ class Journal
3
+ module Filterable
4
+ # Filter the journal at a high level.
5
+ # Takes any number of arguments; each argument should be a hash
6
+ # representing a condition to filter based on. Fields inside the hash
7
+ # will be ANDed together. Each hash will be ORed with the others.
8
+ # Fields in hashes with Arrays as values are treated as an OR statement,
9
+ # since otherwise they would never match.
10
+ # @example
11
+ # j = Systemd::Journal.filter(
12
+ # {_systemd_unit: 'session-4.scope'},
13
+ # {priority: [4, 6]},
14
+ # {_exe: '/usr/bin/sshd', priority: 1}
15
+ # )
16
+ # # equivalent to
17
+ # (_systemd_unit == 'session-4.scope') ||
18
+ # (priority == 4 || priority == 6) ||
19
+ # (_exe == '/usr/bin/sshd' && priority == 1)
20
+ def filter(*conditions)
21
+ clear_filters
22
+
23
+ last_index = conditions.length - 1
24
+
25
+ conditions.each_with_index do |condition, index|
26
+ add_filters(condition)
27
+ add_disjunction unless index == last_index
28
+ end
29
+ end
30
+
31
+ # Add a filter to journal, such that only entries where the given filter
32
+ # matches are returned.
33
+ # {#move_next} or {#move_previous} must be invoked after adding a filter
34
+ # before attempting to read from the journal.
35
+ # @param [String] field the column to filter on, e.g. _PID, _EXE.
36
+ # @param [String] value the match to search for, e.g. '/usr/bin/sshd'
37
+ # @return [nil]
38
+ def add_filter(field, value)
39
+ match = "#{field.to_s.upcase}=#{value}"
40
+ rc = Native.sd_journal_add_match(@ptr, match, match.length)
41
+ raise JournalError.new(rc) if rc < 0
42
+ end
43
+
44
+ # Add a set of filters to the journal, such that only entries where the
45
+ # given filters match are returned.
46
+ # @param [Hash] filters a set of field/filter value pairs.
47
+ # If the filter value is an array, each value in the array is added
48
+ # and entries where the specified field matches any of the values is
49
+ # returned.
50
+ # @example Filter by PID and EXE
51
+ # j.add_filters(_pid: 6700, _exe: '/usr/bin/sshd')
52
+ def add_filters(filters)
53
+ filters.each do |field, value|
54
+ Array(value).each { |v| add_filter(field, v) }
55
+ end
56
+ end
57
+
58
+ # Add an OR condition to the filter. All previously added matches
59
+ # will be ORed with the terms following the disjunction.
60
+ # {#move_next} or {#move_previous} must be invoked after adding a match
61
+ # before attempting to read from the journal.
62
+ # @return [nil]
63
+ # @example Filter entries returned using an OR condition
64
+ # j = Systemd::Journal.new
65
+ # j.add_filter('PRIORITY', 5)
66
+ # j.add_disjunction
67
+ # j.add_filter('_EXE', '/usr/bin/sshd')
68
+ # while j.move_next
69
+ # # current_entry is either an sshd event or
70
+ # # has priority 5
71
+ # end
72
+ def add_disjunction
73
+ rc = Native.sd_journal_add_disjunction(@ptr)
74
+ raise JournalError.new(rc) if rc < 0
75
+ end
76
+
77
+ # Add an AND condition to the filter. All previously added terms will be
78
+ # ANDed together with terms following the conjunction.
79
+ # {#move_next} or {#move_previous} must be invoked after adding a match
80
+ # before attempting to read from the journal.
81
+ # @return [nil]
82
+ # @example Filter entries returned using an AND condition
83
+ # j = Systemd::Journal.new
84
+ # j.add_filter('PRIORITY', 5)
85
+ # j.add_conjunction
86
+ # j.add_filter('_EXE', '/usr/bin/sshd')
87
+ # while j.move_next
88
+ # # current_entry is an sshd event with priority 5
89
+ # end
90
+ def add_conjunction
91
+ rc = Native.sd_journal_add_conjunction(@ptr)
92
+ raise JournalError.new(rc) if rc < 0
93
+ end
94
+
95
+ # Remove all filters and conjunctions/disjunctions.
96
+ # @return [nil]
97
+ def clear_filters
98
+ Native.sd_journal_flush_matches(@ptr)
99
+ end
100
+ end
101
+ end
102
+ end