systemd-journal 0.1.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![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)
|
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:
|