systemd-journal 0.1.4 → 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 +4 -4
- data/README.md +19 -15
- data/Rakefile +5 -1
- data/examples/journal_directory.rb +2 -4
- data/examples/ssh_watcher.rb +4 -9
- data/lib/systemd/ffi_size_t.rb +19 -0
- data/lib/systemd/id128.rb +9 -6
- data/lib/systemd/journal/compat.rb +8 -0
- data/lib/systemd/journal/flags.rb +3 -2
- data/lib/systemd/journal/native.rb +12 -5
- data/lib/systemd/journal/version.rb +1 -1
- data/lib/systemd/journal.rb +157 -41
- data/lib/systemd/journal_entry.rb +28 -0
- data/lib/systemd/journal_error.rb +2 -0
- data/spec/no_ffi.rb +4 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/systemd/journal_entry_spec.rb +43 -0
- data/spec/systemd/journal_spec.rb +325 -23
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 871a098e73eadfe6cd53fc8066537c37e1a1681b
|
4
|
+
data.tar.gz: cb944399a1624c724b469ca41b13a3ccb4bddcb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a399b0ddb5e9eb60bb305b631bb531588352f0015a1ac0b64880fa289877a716c44eaa34d887461a17144af21f5c520a6d759c8030c315538c117160ead48724
|
7
|
+
data.tar.gz: af798df9d31d0cc9e742104de505964bf1e07f94dfb74918407cdce0996cde716a96b440482614c57050afabc56c62aa82d218edc53fb20f3685e29ec5c1d719
|
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
# Systemd::Journal
|
1
|
+
# Systemd::Journal [](http://badge.fury.io/rb/systemd-journal) [](https://travis-ci.org/ledbettj/systemd-journal)
|
2
2
|
|
3
3
|
Ruby bindings for reading from the systemd journal.
|
4
4
|
|
5
|
-
* [documentation](http://rubydoc.info/
|
5
|
+
* [documentation](http://rubydoc.info/gems/systemd-journal)
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
|
-
gem 'systemd-journal', '~>
|
11
|
+
gem 'systemd-journal', '~> 1.0.0'
|
12
12
|
|
13
13
|
And then execute:
|
14
14
|
|
@@ -16,29 +16,33 @@ And then execute:
|
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
|
-
|
19
|
+
Print all messages as they occur:
|
20
20
|
|
21
21
|
require 'systemd/journal'
|
22
22
|
|
23
23
|
j = Systemd::Journal.new
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
j.seek(:tail)
|
25
|
+
|
26
|
+
j.watch do |entry|
|
27
|
+
puts entry.message
|
27
28
|
end
|
28
|
-
|
29
|
-
|
29
|
+
|
30
|
+
Filter messages included in the journal:
|
30
31
|
|
31
32
|
require 'systemd/journal'
|
32
|
-
|
33
|
+
|
33
34
|
j = Systemd::Journal.new
|
34
|
-
|
35
|
+
|
36
|
+
# only display entries from SSHD with priority 6.
|
37
|
+
j.add_match(:priority, 6)
|
38
|
+
j.add_match(:_exe, '/usr/bin/sshd')
|
39
|
+
|
35
40
|
while j.move_next
|
36
|
-
j.current_entry
|
37
|
-
puts "#{key}: #{value}"
|
38
|
-
end
|
39
|
-
puts "\n"
|
41
|
+
puts j.current_entry.message
|
40
42
|
end
|
41
43
|
|
44
|
+
See the documentation for more examples.
|
45
|
+
|
42
46
|
## Contributing
|
43
47
|
|
44
48
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -13,5 +13,9 @@ YARD::Rake::YardocTask.new do |t|
|
|
13
13
|
end
|
14
14
|
|
15
15
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
16
|
-
|
16
|
+
opts = ['--color']
|
17
|
+
opts << '--require ./spec/no_ffi.rb' if ENV['TRAVIS']
|
18
|
+
t.rspec_opts = opts.join(' ')
|
17
19
|
end
|
20
|
+
|
21
|
+
task default: :spec
|
data/examples/ssh_watcher.rb
CHANGED
@@ -8,16 +8,11 @@ class SSHWatcher
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def run
|
11
|
-
@journal.add_match(
|
11
|
+
@journal.add_match(:_exe, '/usr/bin/sshd')
|
12
12
|
# skip all existing entries -- sd_journal_seek_tail() is currently broken.
|
13
13
|
while @journal.move_next ; end
|
14
14
|
|
15
|
-
|
16
|
-
if @journal.wait(1_000_000 * 5) != :nop
|
17
|
-
process_event(@journal.current_entry) while @journal.move_next
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
15
|
+
@journal.watch{ |entry| process_event(entry) }
|
21
16
|
end
|
22
17
|
|
23
18
|
private
|
@@ -25,9 +20,9 @@ class SSHWatcher
|
|
25
20
|
LOGIN_REGEXP = /Accepted\s+(?<auth_method>[^\s]+)\s+for\s+(?<user>[^\s]+)\s+from\s+(?<address>[^\s]+)/
|
26
21
|
|
27
22
|
def process_event(entry)
|
28
|
-
if (m = entry
|
23
|
+
if (m = entry.message.match(LOGIN_REGEXP))
|
29
24
|
timestamp = DateTime.strptime(
|
30
|
-
(entry
|
25
|
+
(entry._source_realtime_timestamp.to_i / 1_000_000).to_s,
|
31
26
|
"%s"
|
32
27
|
)
|
33
28
|
puts "login via #{m[:auth_method]} for #{m[:user]} from #{m[:address]} at #{timestamp.ctime}"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
# @private
|
4
|
+
class FFI::MemoryPointer
|
5
|
+
|
6
|
+
# monkey patch a read_size_t and write_size_t method onto
|
7
|
+
# FFI::MemoryPointer if necessary.
|
8
|
+
case (p = FFI::MemoryPointer.new(:size_t, 1)).size
|
9
|
+
when 4
|
10
|
+
alias_method(:read_size_t, :read_uint32) unless p.respond_to?(:read_size_t)
|
11
|
+
alias_method(:write_size_t, :write_uint32) unless p.respond_to?(:write_size_t)
|
12
|
+
when 8
|
13
|
+
alias_method(:read_size_t, :read_uint64) unless p.respond_to?(:read_size_t)
|
14
|
+
alias_method(:write_size_t, :write_uint64) unless p.respond_to?(:write_size_t)
|
15
|
+
else
|
16
|
+
raise RuntimeError.new("unsupported size_t width: #{p.size}")
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/lib/systemd/id128.rb
CHANGED
@@ -46,9 +46,15 @@ module Systemd
|
|
46
46
|
|
47
47
|
# providing bindings to the systemd-id128 library.
|
48
48
|
module Native
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
unless $NO_FFI_SPEC
|
50
|
+
require 'ffi'
|
51
|
+
extend FFI::Library
|
52
|
+
ffi_lib %w[libsystemd-id128.so libsystemd-id128.so.0]
|
53
|
+
|
54
|
+
attach_function :sd_id128_get_machine, [:pointer], :int
|
55
|
+
attach_function :sd_id128_get_boot, [:pointer], :int
|
56
|
+
attach_function :sd_id128_randomize, [:pointer], :int
|
57
|
+
end
|
52
58
|
|
53
59
|
class Id128 < FFI::Union
|
54
60
|
layout :bytes, [:uint8, 16],
|
@@ -59,9 +65,6 @@ module Systemd
|
|
59
65
|
("%02x" * 16) % self[:bytes].to_a
|
60
66
|
end
|
61
67
|
end
|
62
|
-
attach_function :sd_id128_get_machine, [:pointer], :int
|
63
|
-
attach_function :sd_id128_get_boot, [:pointer], :int
|
64
|
-
attach_function :sd_id128_randomize, [:pointer], :int
|
65
68
|
end
|
66
69
|
end
|
67
70
|
end
|
@@ -33,6 +33,14 @@ module Systemd
|
|
33
33
|
# {Systemd::Journal}
|
34
34
|
module ClassMethods
|
35
35
|
|
36
|
+
# write the value of the c errno constant to the systemd journal in the
|
37
|
+
# style of the perror() function.
|
38
|
+
# @param [String] message the text to prefix the error message with.
|
39
|
+
def perror(message)
|
40
|
+
rc = Native::sd_journal_perror(message)
|
41
|
+
raise JournalError.new(rc) if rc < 0
|
42
|
+
end
|
43
|
+
|
36
44
|
# write a simple message to the systemd journal.
|
37
45
|
# @param [Integer] level one of the LOG_* constants defining the
|
38
46
|
# severity of the event.
|
@@ -1,8 +1,9 @@
|
|
1
1
|
module Systemd
|
2
2
|
class Journal
|
3
3
|
# contains a set of constants which maybe bitwise OR-ed together and passed
|
4
|
-
# to the Journal constructor
|
5
|
-
#
|
4
|
+
# to the Journal constructor.
|
5
|
+
# @example
|
6
|
+
# Systemd::Journal.new(flags: Systemd::Journal::Flags::LOCAL_ONLY)
|
6
7
|
module Flags
|
7
8
|
# Only open journal files generated on the local machine.
|
8
9
|
LOCAL_ONLY = 1
|
@@ -22,11 +22,18 @@ module Systemd
|
|
22
22
|
attach_function :sd_journal_seek_tail, [:pointer], :int
|
23
23
|
attach_function :sd_journal_seek_realtime_usec, [:pointer, :uint64], :int
|
24
24
|
|
25
|
+
attach_function :sd_journal_get_cursor, [:pointer, :pointer], :int
|
26
|
+
attach_function :sd_journal_seek_cursor, [:pointer, :string], :int
|
27
|
+
attach_function :sd_journal_test_cursor, [:pointer, :string], :int
|
28
|
+
|
25
29
|
# data reading
|
26
30
|
attach_function :sd_journal_get_data, [:pointer, :string, :pointer, :pointer], :int
|
27
31
|
attach_function :sd_journal_restart_data, [:pointer], :void
|
28
32
|
attach_function :sd_journal_enumerate_data, [:pointer, :pointer, :pointer], :int
|
29
33
|
|
34
|
+
attach_function :sd_journal_get_data_threshold, [:pointer, :pointer], :int
|
35
|
+
attach_function :sd_journal_set_data_threshold, [:pointer, :size_t], :int
|
36
|
+
|
30
37
|
# querying
|
31
38
|
attach_function :sd_journal_query_unique, [:pointer, :string], :int
|
32
39
|
attach_function :sd_journal_enumerate_unique, [:pointer, :pointer, :pointer], :int
|
@@ -38,7 +45,7 @@ module Systemd
|
|
38
45
|
:append,
|
39
46
|
:invalidate
|
40
47
|
]
|
41
|
-
attach_function :sd_journal_wait, [:pointer, :uint64], :wake_reason
|
48
|
+
attach_function :sd_journal_wait, [:pointer, :uint64], :wake_reason, blocking: true
|
42
49
|
|
43
50
|
# filtering
|
44
51
|
attach_function :sd_journal_add_match, [:pointer, :string, :size_t], :int
|
@@ -47,12 +54,12 @@ module Systemd
|
|
47
54
|
attach_function :sd_journal_add_conjunction, [:pointer], :int
|
48
55
|
|
49
56
|
# writing
|
50
|
-
attach_function :sd_journal_print,
|
51
|
-
attach_function :sd_journal_send,
|
52
|
-
|
57
|
+
attach_function :sd_journal_print, [:int, :string], :int
|
58
|
+
attach_function :sd_journal_send, [:varargs], :int
|
59
|
+
attach_function :sd_journal_perror, [:string], :int
|
53
60
|
# misc
|
54
61
|
attach_function :sd_journal_get_usage, [:pointer, :pointer], :int
|
55
62
|
end
|
56
63
|
|
57
|
-
end
|
64
|
+
end unless $NO_FFI_SPEC
|
58
65
|
end
|
data/lib/systemd/journal.rb
CHANGED
@@ -3,8 +3,11 @@ require 'systemd/journal/flags'
|
|
3
3
|
require 'systemd/journal/compat'
|
4
4
|
require 'systemd/journal/fields'
|
5
5
|
require 'systemd/journal_error'
|
6
|
+
require 'systemd/journal_entry'
|
6
7
|
require 'systemd/id128'
|
7
8
|
|
9
|
+
require 'systemd/ffi_size_t'
|
10
|
+
|
8
11
|
module Systemd
|
9
12
|
# Class to allow interacting with the systemd journal.
|
10
13
|
# To read from the journal, instantiate a new {Systemd::Journal}; to write to
|
@@ -12,6 +15,7 @@ module Systemd
|
|
12
15
|
# {Systemd::Journal::Compat::ClassMethods#message Journal.message} or
|
13
16
|
# {Systemd::Journal::Compat::ClassMethods#print Journal.print}.
|
14
17
|
class Journal
|
18
|
+
include Enumerable
|
15
19
|
include Systemd::Journal::Compat
|
16
20
|
|
17
21
|
# Returns a new instance of a Journal, opened with the provided options.
|
@@ -45,17 +49,62 @@ module Systemd
|
|
45
49
|
ObjectSpace.define_finalizer(self, self.class.finalize(@ptr))
|
46
50
|
end
|
47
51
|
|
52
|
+
# Iterate over each entry in the journal, respecting the applied
|
53
|
+
# conjunctions/disjunctions.
|
54
|
+
# If a block is given, it is called with each entry until no more
|
55
|
+
# entries remain. Otherwise, returns an enumerator which can be chained.
|
56
|
+
def each
|
57
|
+
return to_enum(:each) unless block_given?
|
58
|
+
|
59
|
+
seek(:head)
|
60
|
+
yield current_entry while move_next
|
61
|
+
end
|
62
|
+
|
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
|
+
|
48
99
|
# Move the read pointer to the next entry in the journal.
|
49
100
|
# @return [Boolean] True if moving to the next entry was successful.
|
50
101
|
# @return [Boolean] False if unable to move to the next entry, indicating
|
51
102
|
# that the pointer has reached the end of the journal.
|
52
103
|
def move_next
|
53
|
-
|
54
|
-
when 0 then false
|
55
|
-
when 1 then true
|
56
|
-
else
|
104
|
+
if (rc = Native::sd_journal_next(@ptr)) < 0
|
57
105
|
raise JournalError.new(rc) if rc < 0
|
58
106
|
end
|
107
|
+
rc > 0
|
59
108
|
end
|
60
109
|
|
61
110
|
# Move the read pointer forward by `amount` entries.
|
@@ -73,12 +122,10 @@ module Systemd
|
|
73
122
|
# @return [Boolean] False if unable to move to the previous entry,
|
74
123
|
# indicating that the pointer has reached the beginning of the journal.
|
75
124
|
def move_previous
|
76
|
-
|
77
|
-
when 0 then false # EOF
|
78
|
-
when 1 then true
|
79
|
-
else
|
125
|
+
if (rc = Native::sd_journal_previous(@ptr)) < 0
|
80
126
|
raise JournalError.new(rc) if rc < 0
|
81
127
|
end
|
128
|
+
rc > 0
|
82
129
|
end
|
83
130
|
|
84
131
|
# Move the read pointer backwards by `amount` entries.
|
@@ -98,7 +145,9 @@ module Systemd
|
|
98
145
|
# @param [Symbol, Time] whence one of :head, :tail, or a Time instance.
|
99
146
|
# `:head` (or `:start`) will seek to the beginning of the journal.
|
100
147
|
# `:tail` (or `:end`) will seek to the end of the journal. When a `Time`
|
101
|
-
# is provided, seek to the journal entry logged closest to that time.
|
148
|
+
# is provided, seek to the journal entry logged closest to that time. When
|
149
|
+
# a String is provided, assume it is a cursor from {#cursor} and seek to
|
150
|
+
# that entry.
|
102
151
|
# @return [True]
|
103
152
|
def seek(whence)
|
104
153
|
rc = case whence
|
@@ -110,6 +159,8 @@ module Systemd
|
|
110
159
|
if whence.is_a?(Time)
|
111
160
|
# TODO: is this right? who knows.
|
112
161
|
Native::sd_journal_seek_realtime_usec(@ptr, whence.to_i * 1_000_000)
|
162
|
+
elsif whence.is_a?(String)
|
163
|
+
Native::sd_journal_seek_cursor(@ptr, whence)
|
113
164
|
else
|
114
165
|
raise ArgumentError.new("Unknown seek type: #{whence.class}")
|
115
166
|
end
|
@@ -133,11 +184,11 @@ module Systemd
|
|
133
184
|
len_ptr = FFI::MemoryPointer.new(:size_t, 1)
|
134
185
|
out_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
135
186
|
|
136
|
-
rc = Native::sd_journal_get_data(@ptr, field, out_ptr, len_ptr)
|
187
|
+
rc = Native::sd_journal_get_data(@ptr, field.to_s.upcase, out_ptr, len_ptr)
|
137
188
|
|
138
189
|
raise JournalError.new(rc) if rc < 0
|
139
190
|
|
140
|
-
len = read_size_t
|
191
|
+
len = len_ptr.read_size_t
|
141
192
|
out_ptr.read_pointer.read_string_length(len).split('=', 2).last
|
142
193
|
end
|
143
194
|
|
@@ -161,7 +212,7 @@ module Systemd
|
|
161
212
|
results = {}
|
162
213
|
|
163
214
|
while (rc = Native::sd_journal_enumerate_data(@ptr, out_ptr, len_ptr)) > 0
|
164
|
-
len = read_size_t
|
215
|
+
len = len_ptr.read_size_t
|
165
216
|
key, value = out_ptr.read_pointer.read_string_length(len).split('=', 2)
|
166
217
|
results[key] = value
|
167
218
|
|
@@ -170,7 +221,7 @@ module Systemd
|
|
170
221
|
|
171
222
|
raise JournalError.new(rc) if rc < 0
|
172
223
|
|
173
|
-
results
|
224
|
+
JournalEntry.new(results)
|
174
225
|
end
|
175
226
|
|
176
227
|
# Get the list of unique values stored in the journal for the given field.
|
@@ -186,18 +237,17 @@ module Systemd
|
|
186
237
|
# end
|
187
238
|
def query_unique(field)
|
188
239
|
results = []
|
189
|
-
field = field.to_s.upcase
|
190
240
|
out_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
191
241
|
len_ptr = FFI::MemoryPointer.new(:size_t, 1)
|
192
242
|
|
193
243
|
Native::sd_journal_restart_unique(@ptr)
|
194
244
|
|
195
|
-
if (rc = Native::sd_journal_query_unique(@ptr, field)) < 0
|
245
|
+
if (rc = Native::sd_journal_query_unique(@ptr, field.to_s.upcase)) < 0
|
196
246
|
raise JournalError.new(rc)
|
197
247
|
end
|
198
248
|
|
199
249
|
while (rc = Native::sd_journal_enumerate_unique(@ptr, out_ptr, len_ptr)) > 0
|
200
|
-
len = read_size_t
|
250
|
+
len = len_ptr.read_size_t
|
201
251
|
results << out_ptr.read_pointer.read_string_length(len).split('=', 2).last
|
202
252
|
|
203
253
|
yield results.last if block_given?
|
@@ -214,41 +264,73 @@ module Systemd
|
|
214
264
|
# @example Wait for an event for a maximum of 3 seconds
|
215
265
|
# j = Systemd::Journal.new
|
216
266
|
# j.seek(:tail)
|
217
|
-
# if j.wait(3 * 1_000_000)
|
267
|
+
# if j.wait(3 * 1_000_000)
|
218
268
|
# # event occurred
|
219
269
|
# end
|
220
|
-
# @return [
|
270
|
+
# @return [Nil] if the wait time was reached (no events occured).
|
221
271
|
# @return [Symbol] :append if new entries were appened to the journal.
|
222
272
|
# @return [Symbol] :invalidate if journal files were added/removed/rotated.
|
223
273
|
def wait(timeout_usec = -1)
|
224
274
|
rc = Native::sd_journal_wait(@ptr, timeout_usec)
|
225
275
|
raise JournalError.new(rc) if rc.is_a?(Fixnum) && rc < 0
|
226
|
-
rc
|
276
|
+
rc == :nop ? nil : rc
|
277
|
+
end
|
278
|
+
|
279
|
+
# Blocks and waits for new entries to be appended to the journal. When new
|
280
|
+
# entries are written, yields them in turn. Note that this function does
|
281
|
+
# not automatically seek to the end of the journal prior to waiting.
|
282
|
+
# This method Does not return.
|
283
|
+
# @example Print out events as they happen
|
284
|
+
# j = Systemd::Journal.new
|
285
|
+
# j.seek(:tail)
|
286
|
+
# j.watch do |event|
|
287
|
+
# puts event.message
|
288
|
+
# end
|
289
|
+
def watch
|
290
|
+
while true
|
291
|
+
if wait
|
292
|
+
yield current_entry while move_next
|
293
|
+
end
|
294
|
+
end
|
227
295
|
end
|
228
296
|
|
229
297
|
# Add a filter to journal, such that only entries where the given filter
|
230
298
|
# matches are returned.
|
231
|
-
# {#move_next} or {#move_previous} must be invoked after adding a
|
299
|
+
# {#move_next} or {#move_previous} must be invoked after adding a filter
|
232
300
|
# before attempting to read from the journal.
|
233
301
|
# @param [String] field the column to filter on, e.g. _PID, _EXE.
|
234
302
|
# @param [String] value the match to search for, e.g. '/usr/bin/sshd'
|
235
303
|
# @return [nil]
|
236
|
-
def
|
304
|
+
def add_filter(field, value)
|
237
305
|
match = "#{field.to_s.upcase}=#{value}"
|
238
306
|
rc = Native::sd_journal_add_match(@ptr, match, match.length)
|
239
307
|
raise JournalError.new(rc) if rc < 0
|
240
308
|
end
|
241
309
|
|
310
|
+
# Add a set of filters to the journal, such that only entries where the
|
311
|
+
# given filters match are returned.
|
312
|
+
# @param [Hash] filters a set of field/filter value pairs.
|
313
|
+
# If the filter value is an array, each value in the array is added
|
314
|
+
# and entries where the specified field matches any of the values is
|
315
|
+
# returned.
|
316
|
+
# @example Filter by PID and EXE
|
317
|
+
# j.add_filters(_pid: 6700, _exe: '/usr/bin/sshd')
|
318
|
+
def add_filters(filters)
|
319
|
+
filters.each do |field, value|
|
320
|
+
Array(value).each{ |v| add_filter(field, v) }
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
242
324
|
# Add an OR condition to the filter. All previously added matches
|
243
|
-
#
|
325
|
+
# will be ORed with the terms following the disjunction.
|
244
326
|
# {#move_next} or {#move_previous} must be invoked after adding a match
|
245
327
|
# before attempting to read from the journal.
|
246
328
|
# @return [nil]
|
247
329
|
# @example Filter entries returned using an OR condition
|
248
330
|
# j = Systemd::Journal.new
|
249
|
-
# j.
|
250
|
-
# j.add_match('_EXE', '/usr/bin/sshd')
|
331
|
+
# j.add_filter('PRIORITY', 5)
|
251
332
|
# j.add_disjunction
|
333
|
+
# j.add_filter('_EXE', '/usr/bin/sshd')
|
252
334
|
# while j.move_next
|
253
335
|
# # current_entry is either an sshd event or
|
254
336
|
# # has priority 5
|
@@ -258,16 +340,16 @@ module Systemd
|
|
258
340
|
raise JournalError.new(rc) if rc < 0
|
259
341
|
end
|
260
342
|
|
261
|
-
# Add an AND condition to the filter. All previously added
|
262
|
-
#
|
343
|
+
# Add an AND condition to the filter. All previously added terms will be
|
344
|
+
# ANDed together with terms following the conjunction.
|
263
345
|
# {#move_next} or {#move_previous} must be invoked after adding a match
|
264
346
|
# before attempting to read from the journal.
|
265
347
|
# @return [nil]
|
266
348
|
# @example Filter entries returned using an AND condition
|
267
349
|
# j = Systemd::Journal.new
|
268
|
-
# j.
|
269
|
-
# j.add_match('_EXE', '/usr/bin/sshd')
|
350
|
+
# j.add_filter('PRIORITY', 5)
|
270
351
|
# j.add_conjunction
|
352
|
+
# j.add_filter('_EXE', '/usr/bin/sshd')
|
271
353
|
# while j.move_next
|
272
354
|
# # current_entry is an sshd event with priority 5
|
273
355
|
# end
|
@@ -276,9 +358,9 @@ module Systemd
|
|
276
358
|
raise JournalError.new(rc) if rc < 0
|
277
359
|
end
|
278
360
|
|
279
|
-
# Remove all
|
361
|
+
# Remove all filters and conjunctions/disjunctions.
|
280
362
|
# @return [nil]
|
281
|
-
def
|
363
|
+
def clear_filters
|
282
364
|
Native::sd_journal_flush_matches(@ptr)
|
283
365
|
end
|
284
366
|
|
@@ -295,21 +377,55 @@ module Systemd
|
|
295
377
|
size_ptr.read_uint64
|
296
378
|
end
|
297
379
|
|
298
|
-
|
380
|
+
# Get the maximum length of a data field that will be returned.
|
381
|
+
# Fields longer than this will be truncated. Default is 64K.
|
382
|
+
# @return [Integer] size in bytes.
|
383
|
+
def data_threshold
|
384
|
+
size_ptr = FFI::MemoryPointer.new(:size_t, 1)
|
385
|
+
if (rc = Native::sd_journal_get_data_threshold(@ptr, size_ptr)) < 0
|
386
|
+
raise JournalError.new(rc)
|
387
|
+
end
|
299
388
|
|
300
|
-
|
301
|
-
|
389
|
+
size_ptr.read_size_t
|
390
|
+
end
|
391
|
+
|
392
|
+
# Set the maximum length of a data field that will be returned.
|
393
|
+
# Fields longer than this will be truncated.
|
394
|
+
def data_threshold=(threshold)
|
395
|
+
if (rc = Native::sd_journal_set_data_threshold(@ptr, threshold)) < 0
|
396
|
+
raise JournalError.new(rc)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
# returns a string representing the current read position.
|
401
|
+
# This string can be passed to {#seek} or {#cursor?}.
|
402
|
+
# @return [String] a cursor token.
|
403
|
+
def cursor
|
404
|
+
out_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
405
|
+
if (rc = Native.sd_journal_get_cursor(@ptr, out_ptr)) < 0
|
406
|
+
raise JournalError.new(rc)
|
407
|
+
end
|
408
|
+
|
409
|
+
out_ptr.read_pointer.read_string
|
302
410
|
end
|
303
411
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
raise
|
412
|
+
# Check if the read position is currently at the entry represented by the
|
413
|
+
# provided cursor value.
|
414
|
+
# @param c [String] a cursor token returned from {#cursor}.
|
415
|
+
# @return [Boolean] True if the current entry is the one represented by the
|
416
|
+
# provided cursor, False otherwise.
|
417
|
+
def cursor?(c)
|
418
|
+
if (rc = Native.sd_journal_test_cursor(@ptr, c)) < 0
|
419
|
+
raise JournalError.new(rc)
|
312
420
|
end
|
421
|
+
|
422
|
+
rc > 0
|
423
|
+
end
|
424
|
+
|
425
|
+
private
|
426
|
+
|
427
|
+
def self.finalize(ptr)
|
428
|
+
proc{ Native::sd_journal_close(ptr) unless ptr.nil? }
|
313
429
|
end
|
314
430
|
|
315
431
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Systemd
|
2
|
+
class JournalEntry
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :fields
|
6
|
+
|
7
|
+
def initialize(entry)
|
8
|
+
@entry = entry
|
9
|
+
@fields = entry.map do |key, value|
|
10
|
+
name = key.downcase.to_sym
|
11
|
+
define_singleton_method(name){ value } unless respond_to?(name)
|
12
|
+
name
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
@entry[key] || @entry[key.to_s.upcase]
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
return to_enum(:each) unless block_given?
|
23
|
+
|
24
|
+
@entry.each{ |key, value| yield [key, value] }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/spec/no_ffi.rb
ADDED
data/spec/spec_helper.rb
CHANGED
@@ -3,3 +3,37 @@ require 'simplecov'
|
|
3
3
|
|
4
4
|
SimpleCov.start
|
5
5
|
require 'systemd/journal'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.before(:each) do
|
9
|
+
|
10
|
+
# Stub open and close calls
|
11
|
+
dummy_open = ->(ptr, flags, path=nil) do
|
12
|
+
ptr.write_pointer(nil)
|
13
|
+
0
|
14
|
+
end
|
15
|
+
|
16
|
+
Systemd::Journal::Native.stub(:sd_journal_open, &dummy_open)
|
17
|
+
Systemd::Journal::Native.stub(:sd_journal_open_directory, &dummy_open)
|
18
|
+
Systemd::Journal::Native.stub(:sd_journal_close).and_return(0)
|
19
|
+
|
20
|
+
# Raise an exception if any native calls are actually called
|
21
|
+
native_calls = Systemd::Journal::Native.methods.select do |m|
|
22
|
+
m.to_s.start_with?("sd_")
|
23
|
+
end
|
24
|
+
|
25
|
+
native_calls -= [
|
26
|
+
:sd_journal_open, :sd_journal_open_directory, :sd_journal_close
|
27
|
+
]
|
28
|
+
|
29
|
+
build_err_proc = ->(method_name) do
|
30
|
+
return ->(*params) do
|
31
|
+
raise RuntimeError.new("#{method_name} called without being stubbed.")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
native_calls.each do |meth|
|
36
|
+
Systemd::Journal::Native.stub(meth, &build_err_proc.call(meth))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Systemd::JournalEntry do
|
4
|
+
subject do
|
5
|
+
Systemd::JournalEntry.new(
|
6
|
+
'_PID' => '125',
|
7
|
+
'_EXE' => '/usr/bin/sshd',
|
8
|
+
'PRIORITY' => '4',
|
9
|
+
'OBJECT_ID'=> ':)'
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'allows enumerating entries' do
|
14
|
+
expect{ |b| subject.each(&b) }.to yield_successive_args(
|
15
|
+
['_PID', '125'],
|
16
|
+
['_EXE', '/usr/bin/sshd'],
|
17
|
+
['PRIORITY', '4'],
|
18
|
+
['OBJECT_ID', ':)']
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'responds to field names as methods' do
|
23
|
+
subject._pid.should eq('125')
|
24
|
+
subject.priority.should eq('4')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'doesnt overwrite existing methods' do
|
28
|
+
subject.object_id.should_not eq(':)')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'allows accessing via [string]' do
|
32
|
+
subject['OBJECT_ID'].should eq(':)')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'allows accessing via [symbol]' do
|
36
|
+
subject[:object_id].should eq(':)')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'lists all fields it contains' do
|
40
|
+
subject.fields.should eq([:_pid, :_exe, :priority, :object_id])
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -1,18 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Systemd::Journal do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
# don't actually make native API calls.
|
7
|
-
dummy_open = ->(ptr, flags, path=nil) do
|
8
|
-
ptr.write_pointer(nil)
|
9
|
-
0
|
10
|
-
end
|
11
|
-
|
12
|
-
Systemd::Journal::Native.stub(:sd_journal_open, &dummy_open)
|
13
|
-
Systemd::Journal::Native.stub(:sd_journal_open_directory, &dummy_open)
|
14
|
-
Systemd::Journal::Native.stub(:sd_journal_close).and_return(0)
|
15
|
-
end
|
16
4
|
|
17
5
|
describe '#initialize' do
|
18
6
|
it 'opens a directory if a path is passed' do
|
@@ -34,6 +22,20 @@ describe Systemd::Journal do
|
|
34
22
|
end
|
35
23
|
end
|
36
24
|
|
25
|
+
describe '#move' do
|
26
|
+
it 'calls move_next_skip if the value is positive' do
|
27
|
+
j = Systemd::Journal.new
|
28
|
+
j.should_receive(:move_next_skip).with(5)
|
29
|
+
j.move(5)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'calls move_next_previous otherwise' do
|
33
|
+
j = Systemd::Journal.new
|
34
|
+
j.should_receive(:move_previous_skip).with(5)
|
35
|
+
j.move(-5)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
37
39
|
['next', 'previous'].each do |direction|
|
38
40
|
describe "#move_#{direction}" do
|
39
41
|
it 'returns true on a successful move' do
|
@@ -76,6 +78,33 @@ describe Systemd::Journal do
|
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
81
|
+
describe '#each' do
|
82
|
+
it 'should reposition to the head of the journal' do
|
83
|
+
j = Systemd::Journal.new
|
84
|
+
j.should_receive(:seek).with(:head).and_return(0)
|
85
|
+
j.stub(:move_next).and_return(nil)
|
86
|
+
j.each{|e| nil }
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should return an enumerator if no block is given' do
|
90
|
+
j = Systemd::Journal.new
|
91
|
+
j.each.class.should eq(Enumerator)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should return each entry in the journal' do
|
95
|
+
entries = [{'_PID' => 1}, {'_PID' => 2}]
|
96
|
+
entry = nil
|
97
|
+
|
98
|
+
j = Systemd::Journal.new
|
99
|
+
j.stub(:seek).and_return(0)
|
100
|
+
j.stub(:current_entry) { entry }
|
101
|
+
j.stub(:move_next) { entry = entries.shift }
|
102
|
+
|
103
|
+
j.map{|e| e['_PID'] }.should eq([1, 2])
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
79
108
|
describe '#seek' do
|
80
109
|
it 'moves to the first entry of the file' do
|
81
110
|
j = Systemd::Journal.new
|
@@ -94,44 +123,226 @@ describe Systemd::Journal do
|
|
94
123
|
Systemd::Journal::Native.should_receive(:sd_journal_seek_realtime_usec).and_return(0)
|
95
124
|
j.seek(Time.now).should eq(true)
|
96
125
|
end
|
126
|
+
|
127
|
+
it 'seeks based on a cursor when a string is provided' do
|
128
|
+
j = Systemd::Journal.new
|
129
|
+
|
130
|
+
Systemd::Journal::Native.should_receive(:sd_journal_seek_cursor).
|
131
|
+
with(anything, "123").and_return(0)
|
132
|
+
|
133
|
+
j.seek("123")
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'throws an exception if it doesnt understand the type' do
|
137
|
+
j = Systemd::Journal.new
|
138
|
+
expect { j.seek(Object.new) }.to raise_error(ArgumentError)
|
139
|
+
end
|
97
140
|
end
|
98
141
|
|
99
142
|
describe '#read_field' do
|
100
|
-
|
143
|
+
it 'raises an exception if the call fails' do
|
144
|
+
Systemd::Journal::Native.should_receive(:sd_journal_get_data).and_return(-1)
|
145
|
+
|
146
|
+
j = Systemd::Journal.new
|
147
|
+
expect{ j.read_field(:message) }.to raise_error(Systemd::JournalError)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'parses the returned value correctly.' do
|
151
|
+
j = Systemd::Journal.new
|
152
|
+
|
153
|
+
Systemd::Journal::Native.should_receive(:sd_journal_get_data) do |ptr, field, out_ptr, len_ptr|
|
154
|
+
dummy = "MESSAGE=hello world"
|
155
|
+
out_ptr.write_pointer(FFI::MemoryPointer.from_string(dummy))
|
156
|
+
len_ptr.write_size_t(dummy.size)
|
157
|
+
0
|
158
|
+
end
|
159
|
+
|
160
|
+
j.read_field(:message).should eq("hello world")
|
161
|
+
end
|
101
162
|
end
|
102
163
|
|
103
164
|
describe '#current_entry' do
|
104
|
-
|
165
|
+
before(:each) do
|
166
|
+
Systemd::Journal::Native.should_receive(:sd_journal_restart_data).and_return(nil)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'raises an exception if the call fails' do
|
170
|
+
j = Systemd::Journal.new
|
171
|
+
Systemd::Journal::Native.should_receive(:sd_journal_enumerate_data).and_return(-1)
|
172
|
+
expect { j.current_entry }.to raise_error(Systemd::JournalError)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'returns the correct data' do
|
176
|
+
j = Systemd::Journal.new
|
177
|
+
results = ['_PID=100', 'MESSAGE=hello world']
|
178
|
+
|
179
|
+
Systemd::Journal::Native.should_receive(:sd_journal_enumerate_data).exactly(3).times do |ptr, out_ptr, len_ptr|
|
180
|
+
if results.any?
|
181
|
+
x = results.shift
|
182
|
+
out_ptr.write_pointer(FFI::MemoryPointer.from_string(x))
|
183
|
+
len_ptr.write_size_t(x.length)
|
184
|
+
1
|
185
|
+
else
|
186
|
+
0
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
entry = j.current_entry
|
191
|
+
|
192
|
+
entry._pid.should eq('100')
|
193
|
+
entry.message.should eq('hello world')
|
194
|
+
|
195
|
+
end
|
105
196
|
end
|
106
197
|
|
107
198
|
describe '#query_unique' do
|
108
|
-
|
199
|
+
before(:each) do
|
200
|
+
Systemd::Journal::Native.should_receive(:sd_journal_restart_unique).and_return(nil)
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'raises an exception if the call fails' do
|
204
|
+
j = Systemd::Journal.new
|
205
|
+
Systemd::Journal::Native.should_receive(:sd_journal_query_unique).and_return(-1)
|
206
|
+
expect { j.query_unique(:_pid) }.to raise_error(Systemd::JournalError)
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'raises an exception if the call fails (2)' do
|
210
|
+
j = Systemd::Journal.new
|
211
|
+
Systemd::Journal::Native.should_receive(:sd_journal_query_unique).and_return(0)
|
212
|
+
Systemd::Journal::Native.should_receive(:sd_journal_enumerate_unique).and_return(-1)
|
213
|
+
expect { j.query_unique(:_pid) }.to raise_error(Systemd::JournalError)
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'returns the correct data' do
|
217
|
+
j = Systemd::Journal.new
|
218
|
+
results = ['_PID=100', '_PID=200', '_PID=300']
|
219
|
+
|
220
|
+
Systemd::Journal::Native.should_receive(:sd_journal_query_unique).and_return(0)
|
221
|
+
|
222
|
+
Systemd::Journal::Native.should_receive(:sd_journal_enumerate_unique).exactly(4).times do |ptr, out_ptr, len_ptr|
|
223
|
+
if results.any?
|
224
|
+
x = results.shift
|
225
|
+
out_ptr.write_pointer(FFI::MemoryPointer.from_string(x))
|
226
|
+
len_ptr.write_size_t(x.length)
|
227
|
+
1
|
228
|
+
else
|
229
|
+
0
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
j.query_unique(:_pid).should eq(['100', '200', '300'])
|
234
|
+
end
|
235
|
+
|
109
236
|
end
|
110
237
|
|
111
238
|
describe '#wait' do
|
112
|
-
|
239
|
+
it 'raises an exception if the call fails' do
|
240
|
+
Systemd::Journal::Native.should_receive(:sd_journal_wait).and_return(-1)
|
241
|
+
|
242
|
+
j = Systemd::Journal.new
|
243
|
+
expect{ j.wait(100) }.to raise_error(Systemd::JournalError)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'returns the reason we were woken up' do
|
247
|
+
j = Systemd::Journal.new
|
248
|
+
Systemd::Journal::Native.should_receive(:sd_journal_wait).and_return(:append)
|
249
|
+
j.wait(100).should eq(:append)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'returns nil if we reached the timeout.' do
|
253
|
+
j = Systemd::Journal.new
|
254
|
+
Systemd::Journal::Native.should_receive(:sd_journal_wait).and_return(:nop)
|
255
|
+
j.wait(100).should eq(nil)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe '#add_filter' do
|
260
|
+
it 'raises an exception if the call fails' do
|
261
|
+
Systemd::Journal::Native.should_receive(:sd_journal_add_match).and_return(-1)
|
262
|
+
|
263
|
+
j = Systemd::Journal.new
|
264
|
+
expect{ j.add_filter(:message, "test") }.to raise_error(Systemd::JournalError)
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'formats the arguments appropriately' do
|
268
|
+
Systemd::Journal::Native.should_receive(:sd_journal_add_match).
|
269
|
+
with(anything, "MESSAGE=test", "MESSAGE=test".length).
|
270
|
+
and_return(0)
|
271
|
+
|
272
|
+
Systemd::Journal.new.add_filter(:message, "test")
|
273
|
+
end
|
113
274
|
end
|
114
275
|
|
115
|
-
describe '#
|
116
|
-
|
276
|
+
describe '#add_filters' do
|
277
|
+
it 'calls add_filter for each parameter' do
|
278
|
+
j = Systemd::Journal.new
|
279
|
+
j.should_receive(:add_filter).with(:priority, 1)
|
280
|
+
j.should_receive(:add_filter).with(:_exe, '/usr/bin/sshd')
|
281
|
+
|
282
|
+
j.add_filters(priority: 1, _exe: '/usr/bin/sshd')
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'expands array arguments to multiple add_filter calls' do
|
286
|
+
j = Systemd::Journal.new
|
287
|
+
j.should_receive(:add_filter).with(:priority, 1)
|
288
|
+
j.should_receive(:add_filter).with(:priority, 2)
|
289
|
+
j.should_receive(:add_filter).with(:priority, 3)
|
290
|
+
|
291
|
+
j.add_filters(priority: [1,2,3])
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe '#filter' do
|
296
|
+
it 'clears the existing filters' do
|
297
|
+
j = Systemd::Journal.new
|
298
|
+
j.should_receive(:clear_filters)
|
299
|
+
|
300
|
+
j.filter({})
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'adds disjunctions between terms' do
|
304
|
+
j = Systemd::Journal.new
|
305
|
+
j.stub(:clear_filters).and_return(nil)
|
306
|
+
|
307
|
+
j.should_receive(:add_filter).with(:priority, 1).ordered
|
308
|
+
j.should_receive(:add_disjunction).ordered
|
309
|
+
j.should_receive(:add_filter).with(:message, 'hello').ordered
|
310
|
+
|
311
|
+
j.filter({priority: 1}, {message: 'hello'})
|
312
|
+
|
313
|
+
end
|
117
314
|
end
|
118
315
|
|
119
316
|
describe '#add_conjunction' do
|
120
|
-
|
317
|
+
it 'raises an exception if the call fails' do
|
318
|
+
Systemd::Journal::Native.should_receive(:sd_journal_add_conjunction).and_return(-1)
|
319
|
+
|
320
|
+
j = Systemd::Journal.new
|
321
|
+
expect{ j.add_conjunction }.to raise_error(Systemd::JournalError)
|
322
|
+
end
|
121
323
|
end
|
122
324
|
|
123
325
|
describe '#add_disjunction' do
|
124
|
-
|
326
|
+
it 'raises an exception if the call fails' do
|
327
|
+
Systemd::Journal::Native.should_receive(:sd_journal_add_disjunction).and_return(-1)
|
328
|
+
|
329
|
+
j = Systemd::Journal.new
|
330
|
+
expect{ j.add_disjunction }.to raise_error(Systemd::JournalError)
|
331
|
+
end
|
125
332
|
end
|
126
333
|
|
127
|
-
describe '#
|
128
|
-
|
334
|
+
describe '#clear_filters' do
|
335
|
+
it 'flushes the matches' do
|
336
|
+
j = Systemd::Journal.new
|
337
|
+
Systemd::Journal::Native.should_receive(:sd_journal_flush_matches).and_return(nil)
|
338
|
+
j.clear_filters
|
339
|
+
end
|
129
340
|
end
|
130
341
|
|
131
342
|
describe '#disk_usage' do
|
132
343
|
it 'returns the size used on disk' do
|
133
344
|
Systemd::Journal::Native.should_receive(:sd_journal_get_usage) do |ptr, size_ptr|
|
134
|
-
size_ptr.
|
345
|
+
size_ptr.write_size_t(12)
|
135
346
|
0
|
136
347
|
end
|
137
348
|
j = Systemd::Journal.new
|
@@ -145,4 +356,95 @@ describe Systemd::Journal do
|
|
145
356
|
end
|
146
357
|
end
|
147
358
|
|
359
|
+
describe '#data_threshold=' do
|
360
|
+
it 'sets the data threshold' do
|
361
|
+
j = Systemd::Journal.new
|
362
|
+
|
363
|
+
Systemd::Journal::Native.should_receive(:sd_journal_set_data_threshold).
|
364
|
+
with(anything, 0x1234).and_return(0)
|
365
|
+
|
366
|
+
j.data_threshold = 0x1234
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'raises a JournalError on failure' do
|
370
|
+
j = Systemd::Journal.new
|
371
|
+
|
372
|
+
Systemd::Journal::Native.should_receive(:sd_journal_set_data_threshold).
|
373
|
+
with(anything, 0x1234).and_return(-1)
|
374
|
+
|
375
|
+
expect { j.data_threshold = 0x1234 }.to raise_error(Systemd::JournalError)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe '#data_threshold' do
|
380
|
+
it 'gets the data threshold' do
|
381
|
+
j = Systemd::Journal.new
|
382
|
+
|
383
|
+
Systemd::Journal::Native.should_receive(:sd_journal_get_data_threshold) do |ptr, size_ptr|
|
384
|
+
size_ptr.write_size_t(0x1234)
|
385
|
+
0
|
386
|
+
end
|
387
|
+
j.data_threshold.should eq(0x1234)
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'raises a JournalError on failure' do
|
391
|
+
j = Systemd::Journal.new
|
392
|
+
|
393
|
+
Systemd::Journal::Native.should_receive(:sd_journal_get_data_threshold).
|
394
|
+
and_return(-3)
|
395
|
+
|
396
|
+
expect{ j.data_threshold }.to raise_error(Systemd::JournalError)
|
397
|
+
end
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
describe '#cursor?' do
|
402
|
+
it 'returns true if the current cursor is the provided value' do
|
403
|
+
j = Systemd::Journal.new
|
404
|
+
Systemd::Journal::Native.should_receive(:sd_journal_test_cursor).
|
405
|
+
with(anything, "1234").and_return(1)
|
406
|
+
|
407
|
+
j.cursor?("1234").should eq(true)
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'returns false otherwise' do
|
411
|
+
j = Systemd::Journal.new
|
412
|
+
Systemd::Journal::Native.should_receive(:sd_journal_test_cursor).
|
413
|
+
with(anything, "1234").and_return(0)
|
414
|
+
|
415
|
+
j.cursor?("1234").should eq(false)
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'raises a JournalError on failure' do
|
419
|
+
j = Systemd::Journal.new
|
420
|
+
|
421
|
+
Systemd::Journal::Native.should_receive(:sd_journal_test_cursor).
|
422
|
+
and_return(-3)
|
423
|
+
|
424
|
+
expect{ j.cursor?('123') }.to raise_error(Systemd::JournalError)
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|
428
|
+
|
429
|
+
describe '#cursor' do
|
430
|
+
it 'returns the current cursor' do
|
431
|
+
j = Systemd::Journal.new
|
432
|
+
Systemd::Journal::Native.should_receive(:sd_journal_get_cursor) do |ptr, out_ptr|
|
433
|
+
out_ptr.write_pointer(FFI::MemoryPointer.from_string("5678"))
|
434
|
+
0
|
435
|
+
end
|
436
|
+
j.cursor.should eq("5678")
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'raises a JournalError on failure' do
|
440
|
+
j = Systemd::Journal.new
|
441
|
+
|
442
|
+
Systemd::Journal::Native.should_receive(:sd_journal_get_cursor).
|
443
|
+
and_return(-3)
|
444
|
+
|
445
|
+
expect{ j.cursor }.to raise_error(Systemd::JournalError)
|
446
|
+
end
|
447
|
+
|
448
|
+
end
|
449
|
+
|
148
450
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: systemd-journal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Ledbetter
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- examples/journal_directory.rb
|
70
70
|
- examples/ssh_watcher.rb
|
71
71
|
- lib/systemd-journal.rb
|
72
|
+
- lib/systemd/ffi_size_t.rb
|
72
73
|
- lib/systemd/id128.rb
|
73
74
|
- lib/systemd/journal.rb
|
74
75
|
- lib/systemd/journal/compat.rb
|
@@ -76,9 +77,12 @@ files:
|
|
76
77
|
- lib/systemd/journal/flags.rb
|
77
78
|
- lib/systemd/journal/native.rb
|
78
79
|
- lib/systemd/journal/version.rb
|
80
|
+
- lib/systemd/journal_entry.rb
|
79
81
|
- lib/systemd/journal_error.rb
|
82
|
+
- spec/no_ffi.rb
|
80
83
|
- spec/spec_helper.rb
|
81
84
|
- spec/systemd/id128_spec.rb
|
85
|
+
- spec/systemd/journal_entry_spec.rb
|
82
86
|
- spec/systemd/journal_spec.rb
|
83
87
|
- systemd-journal.gemspec
|
84
88
|
homepage: https://github.com/ledbettj/systemd-journal
|
@@ -106,7 +110,9 @@ signing_key:
|
|
106
110
|
specification_version: 4
|
107
111
|
summary: Ruby bindings to libsystemd-journal
|
108
112
|
test_files:
|
113
|
+
- spec/no_ffi.rb
|
109
114
|
- spec/spec_helper.rb
|
110
115
|
- spec/systemd/id128_spec.rb
|
116
|
+
- spec/systemd/journal_entry_spec.rb
|
111
117
|
- spec/systemd/journal_spec.rb
|
112
118
|
has_rdoc:
|