systemd-journal 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a059156599a0d4007da349975fbeabc806617b7
4
- data.tar.gz: 04e3a33cd7dd024fab848183ad3f36e3c3a3ecba
3
+ metadata.gz: 978a030e0e1fbf45c4d7cf8f597fb746c125d9ed
4
+ data.tar.gz: b4ead8920befcd90d27351b7552128347e165a6a
5
5
  SHA512:
6
- metadata.gz: 07b7b3dadcc849ef85dec175bec3734353ffb839e0a0ce287b0bb8954e307972684584815129017e92012f027a04518a6e839237edf52c95c9a16f4763cdbf85
7
- data.tar.gz: b05f3ba3b46d561c0596b498616fab5a9fb57cbeb0462a85c8fbf613e09060caf98dd7ba9224242845ad354cd9897aa135dcd8af9500f3af2a7f2cf8d3fcb909
6
+ metadata.gz: 16629bfb3cf7f6d58894eeec4f2e977c6eeed09eca46c09669060b6f25c1cb10237a3f2aa03551b291e974e7ae6ee10e884b11ffc4069680a30a17c76b11f51d
7
+ data.tar.gz: 45339d61c9c50b4eec0fcf36ac68d9cfe9feeacd8bb07f2d47f5c235b1d2e0f1bd64ce29a6118c6f226e1918d717a8b10285abc69816e56232f5c3227f8801ca
data/.rubocop.yml CHANGED
@@ -4,14 +4,28 @@ SignalException:
4
4
  RaiseArgs:
5
5
  Enabled: false
6
6
 
7
- SpaceAroundBlockBraces:
8
- EnforcedStyle: space_inside_braces
7
+ SpaceInsideBlockBraces:
8
+ Enabled: true
9
+ SpaceBeforeBlockBraces:
10
+ Enabled: true
11
+
9
12
  Lambda:
10
13
  Enabled: false
11
14
 
12
- GlobalVars:
13
- AllowedVariables:
14
- - $NO_FFI_SPEC
15
-
16
15
  Documentation:
17
- Enabled: false
16
+ Enabled: false
17
+
18
+ MethodLength:
19
+ Max: 20
20
+
21
+ ClassLength:
22
+ Max: 150
23
+
24
+ Style/FileName:
25
+ Enabled: false
26
+
27
+ Style/GuardClause:
28
+ Enabled: false
29
+
30
+ Style/ClassAndModuleChildren:
31
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,23 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ - 1.9.3
5
+ before_script:
6
+ - export PREV_DIR=$(pwd)
7
+ - sudo apt-get update -qq
8
+ - sudo apt-get install autotools-dev automake autoconf libtool libdbus-1-dev libcap-dev libblkid-dev libpam-dev libcryptsetup-dev libaudit-dev libacl1-dev libattr1-dev libselinux-dev liblzma-dev libgcrypt-dev libqrencode-dev libmicrohttpd-dev gtk-doc-tools gperf python2.7-dev
9
+ - git clone git://github.com/systemd/systemd.git /tmp/systemd
10
+ - >
11
+ cd /tmp/systemd/ &&
12
+ git checkout v209 &&
13
+ sh autogen.sh &&
14
+ ./configure --disable-apparmor --disable-audit --disable-selinux
15
+ --disable-machined --disable-logind --disable-timedated
16
+ --disable-timesyncd --disable-localed --disable-resolved
17
+ --disable-networkd --disable-tests --disable-coredump --disable-hostnamed
18
+ --disable-manpages --disable-libcryptsetup --disable-python-devel
19
+ --sysconfdir=/etc --localstatedir=/var --libdir=/usr/lib
20
+ --with-rootprefix= --with-rootlibdir=/lib
21
+ - cd /tmp/systemd/ && make && sudo make install
22
+ - echo "done building in $(pwd), switching back to $PREV_DIR, library path is $LD_LIBRARY_PATH"
23
+ - cd $PREV_DIR
data/lib/systemd/id128.rb CHANGED
@@ -45,8 +45,8 @@ module Systemd
45
45
  module Native
46
46
  require 'ffi'
47
47
  extend FFI::Library
48
- ffi_lib %w{ libsystemd.so.0 libsystemd.so
49
- libsystemd-id128.so.0 libsystemd-id128.so }
48
+ ffi_lib %w( libsystemd.so.0 libsystemd.so
49
+ libsystemd-id128.so.0 libsystemd-id128.so )
50
50
 
51
51
  attach_function :sd_id128_get_machine, [:pointer], :int
52
52
  attach_function :sd_id128_get_boot, [:pointer], :int
@@ -49,18 +49,9 @@ module Systemd
49
49
 
50
50
  flags = opts[:flags] || 0
51
51
  ptr = FFI::MemoryPointer.new(:pointer, 1)
52
+ opts[:files] = opts[:file] if opts[:file]
52
53
 
53
- rc = case
54
- when opts[:path]
55
- Native.sd_journal_open_directory(ptr, opts[:path], 0)
56
- when opts[:files]
57
- Native.sd_journal_open_files(ptr, array_to_ptrs(opts[:files]), 0)
58
- when opts[:container]
59
- Native.sd_journal_open_container(ptr, opts[:container], flags)
60
- else
61
- Native.sd_journal_open(ptr, flags)
62
- end
63
-
54
+ rc = open_journal(ptr, opts, flags)
64
55
  raise JournalError.new(rc) if rc < 0
65
56
 
66
57
  @ptr = ptr.read_pointer
@@ -121,7 +112,11 @@ module Systemd
121
112
  yield(key, value) if block_given?
122
113
  end
123
114
 
124
- JournalEntry.new(results)
115
+ JournalEntry.new(
116
+ results,
117
+ realtime_ts: read_realtime,
118
+ monotonic_ts: read_monotonic
119
+ )
125
120
  end
126
121
 
127
122
  def current_catalog
@@ -201,6 +196,37 @@ module Systemd
201
196
 
202
197
  private
203
198
 
199
+ def open_journal(ptr, opts, flags)
200
+ case
201
+ when opts[:path]
202
+ Native.sd_journal_open_directory(ptr, opts[:path], 0)
203
+ when opts[:files]
204
+ Native.sd_journal_open_files(ptr, array_to_ptrs(Array(opts[:files])), 0)
205
+ when opts[:container]
206
+ Native.sd_journal_open_container(ptr, opts[:container], flags)
207
+ else
208
+ Native.sd_journal_open(ptr, flags)
209
+ end
210
+ end
211
+
212
+ def read_realtime
213
+ out = FFI::MemoryPointer.new(:uint64, 1)
214
+ rc = Native.sd_journal_get_realtime_usec(@ptr, out)
215
+ raise JournalError.new(rc) if rc < 0
216
+
217
+ out.read_uint64
218
+ end
219
+
220
+ def read_monotonic
221
+ out = FFI::MemoryPointer.new(:uint64, 1)
222
+ boot = FFI::MemoryPointer.new(Systemd::Id128::Native::Id128, 1)
223
+
224
+ rc = Native.sd_journal_get_monotonic_usec(@ptr, out, boot)
225
+ raise JournalError.new(rc) if rc < 0
226
+
227
+ [out.read_uint64, Systemd::Id128::Native::Id128.new(boot).to_s]
228
+ end
229
+
204
230
  def array_to_ptrs(strings)
205
231
  ptr = FFI::MemoryPointer.new(:pointer, strings.length + 1)
206
232
  strings.each_with_index do |s, i|
@@ -211,7 +237,7 @@ module Systemd
211
237
  end
212
238
 
213
239
  def validate_options!(opts)
214
- exclusive = [:path, :files, :container]
240
+ exclusive = [:path, :files, :container, :file]
215
241
  provided = (opts.keys & exclusive)
216
242
  if provided.length > 1
217
243
  raise ArgumentError.new("#{provided} are conflicting options")
@@ -1,18 +1,18 @@
1
1
  module Systemd
2
2
  class Journal
3
3
  # Fields directly passed by client programs and stored in the journal.
4
- USER_FIELDS = %w{ MESSAGE MESSAGE_ID PRIORITY CODE_FILE CODE_LINE CODE_FUNC
5
- ERRNO SYSLOG_FACILITY SYSLOG_IDENTIFIER SYSLOG_PID }
4
+ USER_FIELDS = %w( MESSAGE MESSAGE_ID PRIORITY CODE_FILE CODE_LINE CODE_FUNC
5
+ ERRNO SYSLOG_FACILITY SYSLOG_IDENTIFIER SYSLOG_PID )
6
6
 
7
7
  # Fields generated by the journal and added to each event.
8
- TRUSTED_FIELDS = %w{ _PID _UID _GID _COMM _EXE _CMDLINE _AUDIT_SESSION
8
+ TRUSTED_FIELDS = %w( _PID _UID _GID _COMM _EXE _CMDLINE _AUDIT_SESSION
9
9
  _AUDIT_LOGINUID _SYSTEMD_CGROUP _SYSTEMD_SESSION
10
10
  _SYSTEMD_UNIT _SYSTEMD_USER_UNIT _SYSTEMD_OWNER_UID
11
11
  _SELINUX_CONTEXT _SOURCE_REALTIME_TIMESTAMP _BOOT_ID
12
- _MACHINE_ID _HOSTNAME _TRANSPORT }
12
+ _MACHINE_ID _HOSTNAME _TRANSPORT )
13
13
 
14
14
  # Fields used in messages originating from the kernel.
15
- KERNEL_FIELDS = %w{ _KERNEL_DEVICE _KERNEL_SUBSYSTEM _UDEV_SYSNAME
16
- _UDEV_DEVNODE _UDEV_DEVLINK }
15
+ KERNEL_FIELDS = %w( _KERNEL_DEVICE _KERNEL_SUBSYSTEM _UDEV_SYSNAME
16
+ _UDEV_DEVNODE _UDEV_DEVLINK )
17
17
  end
18
18
  end
@@ -8,8 +8,8 @@ module Systemd
8
8
  # rubocop:disable LineLength
9
9
  require 'ffi'
10
10
  extend FFI::Library
11
- ffi_lib %w{ libsystemd.so.0 libsystemd.so
12
- libsystemd-journal.so.0 libsystemd-journal.so}
11
+ ffi_lib %w( libsystemd.so.0 libsystemd.so
12
+ libsystemd-journal.so.0 libsystemd-journal.so)
13
13
 
14
14
  # setup/teardown
15
15
  attach_function :sd_journal_open, [:pointer, :int], :int
@@ -40,6 +40,9 @@ module Systemd
40
40
  attach_function :sd_journal_get_catalog, [:pointer, :pointer], :int
41
41
  attach_function :sd_journal_get_catalog_for_message_id, [Systemd::Id128::Native::Id128.by_value, :pointer], :int
42
42
 
43
+ attach_function :sd_journal_get_realtime_usec, [:pointer, :pointer], :int
44
+ attach_function :sd_journal_get_monotonic_usec, [:pointer, :pointer, :pointer], :int
45
+
43
46
  attach_function :sd_journal_get_data_threshold, [:pointer, :pointer], :int
44
47
  attach_function :sd_journal_set_data_threshold, [:pointer, :size_t], :int
45
48
 
@@ -49,8 +52,8 @@ module Systemd
49
52
  attach_function :sd_journal_restart_unique, [:pointer], :void
50
53
 
51
54
  # event notification
52
- enum :wake_reason, [:nop, :append, :invalidate]
53
- attach_function :sd_journal_wait, [:pointer, :uint64], :wake_reason, blocking: true
55
+ enum :wake_reason, [:nop, :append, :invalidate]
56
+ attach_function :sd_journal_wait, [:pointer, :uint64], :wake_reason, blocking: true
54
57
  attach_function :sd_journal_get_fd, [:pointer], :int
55
58
  attach_function :sd_journal_process, [:pointer], :wake_reason
56
59
  attach_function :sd_journal_reliable_fd, [:pointer], :int
@@ -65,6 +68,8 @@ module Systemd
65
68
  attach_function :sd_journal_print, [:int, :string], :int
66
69
  attach_function :sd_journal_send, [:varargs], :int
67
70
  attach_function :sd_journal_perror, [:string], :int
71
+ attach_function :sd_journal_stream_fd, [:string, :int, :bool], :int
72
+
68
73
  # misc
69
74
  attach_function :sd_journal_get_usage, [:pointer, :pointer], :int
70
75
  end
@@ -1,6 +1,6 @@
1
1
  module Systemd
2
2
  class Journal
3
3
  # The version of the systemd-journal gem.
4
- VERSION = '1.1.3'
4
+ VERSION = '1.2.0'
5
5
  end
6
6
  end
@@ -68,7 +68,6 @@ module Systemd
68
68
  raise JournalError.new(rc) if rc.is_a?(Fixnum) && rc < 0
69
69
  rc == :nop ? nil : rc
70
70
  end
71
-
72
71
  end
73
72
  end
74
73
  end
@@ -7,7 +7,7 @@ module Systemd
7
7
  # the systemd-journal.gem by Daniel Mack
8
8
  # (https://github.com/zonque/systemd-journal.gem).
9
9
  module Writable
10
- # system is unusable
10
+ # system is unusable
11
11
  LOG_EMERG = 0
12
12
  # action must be taken immediately
13
13
  LOG_ALERT = 1
@@ -32,6 +32,24 @@ module Systemd
32
32
  # methods in this module will be available as class methods on
33
33
  # {Systemd::Journal}
34
34
  module ClassMethods
35
+
36
+ # Creates a new IO stream which writes newline-seperated messages to
37
+ # the journal.
38
+ # @param identifier [String] this value will be passed as
39
+ # SYSLOG_IDENTIFIER to the journal.
40
+ # @param priority [Integer] the log level for events writen to this
41
+ # stream.
42
+ # @param opts [Hash]
43
+ # @option opts [Boolean] :prefix true to enable kernel-style log
44
+ # priority prefixes
45
+ # @return [IO]
46
+ def log_stream(identifier, priority, opts = {})
47
+ fd = Native.sd_journal_stream_fd(identifier, priority, !!opts[:prefix])
48
+ raise JournalError.new(fd) if fd < 0
49
+
50
+ IO.new(fd, File::WRONLY, encoding: Encoding::UTF_8)
51
+ end
52
+
35
53
  # write the value of the c errno constant to the systemd journal in the
36
54
  # style of the perror() function.
37
55
  # @param [String] message the text to prefix the error message with.
@@ -10,8 +10,9 @@ module Systemd
10
10
  # {Systemd::Journal} methods such as {Systemd::Journal#current_entry}.
11
11
  # @param [Hash] entry a hash containing all the key-value pairs associated
12
12
  # with a given journal entry.
13
- def initialize(entry)
13
+ def initialize(entry, context = {})
14
14
  @entry = entry
15
+ @ctx = context
15
16
  @fields = entry.map do |key, value|
16
17
  name = key.downcase.to_sym
17
18
  define_singleton_method(name) { value } unless respond_to?(name)
@@ -19,9 +20,23 @@ module Systemd
19
20
  end
20
21
  end
21
22
 
22
- # not all journal entries will have all fields. don't raise an error.
23
- def method_missing(m)
24
- nil
23
+ # Returns the wall-clock time that this entry was received by the journal.
24
+ # @return [Time]
25
+ def realtime_timestamp
26
+ @realtime_timestamp ||= Time.at(0, @ctx[:realtime_ts])
27
+ end
28
+
29
+ # Returns the monotonic time (time since boot) that this entry was received
30
+ # by the journal. This should be associated with a boot_id.
31
+ # @return [Time]
32
+ def monotonic_timestamp
33
+ @monotonic_timestamp ||= Time.at(0, @ctx[:monotonic_ts].first)
34
+ end
35
+
36
+ def method_missing(m, *args)
37
+ # not all journal entries will have all fields. don't raise an error
38
+ # unless the user passed arguments.
39
+ super(m, *args) unless args.empty?
25
40
  end
26
41
 
27
42
  # Get the value of a given field in the entry, or nil if it doesn't exist
@@ -14,8 +14,6 @@ module Systemd
14
14
  super(LIBC.strerror(@code))
15
15
  end
16
16
 
17
- private
18
-
19
17
  # FFI wrapper for the C standard library to pull in `strerror`.
20
18
  # @private
21
19
  module LIBC
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,7 @@ require 'simplecov'
4
4
 
5
5
  module SpecHelper
6
6
  def fixture_dir
7
- @path ||= File.join(File.expand_path(__dir__), 'fixtures')
7
+ @path ||= File.join(File.expand_path('..', __FILE__), 'fixtures')
8
8
  end
9
9
 
10
10
  def journal_file
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Systemd::JournalEntry do
4
+ let(:msg) { 'test message' }
5
+ let(:pid) { '123' }
6
+ let(:hash) { { '_PID' => pid, 'MESSAGE' => msg } }
7
+ subject(:entry) { Systemd::JournalEntry.new(hash) }
8
+
9
+ describe 'initialize' do
10
+ it 'takes a hash as an argument' do
11
+ expect { Systemd::JournalEntry.new(hash) }.to_not raise_error
12
+ end
13
+ end
14
+
15
+ describe '[]' do
16
+ it 'accepts symbols as a field name' do
17
+ expect(entry[:message]).to eq(msg)
18
+ end
19
+
20
+ it 'accepts strings as a field name' do
21
+ expect(entry['message']).to eq(msg)
22
+ end
23
+
24
+ it 'doesnt care about case' do
25
+ expect(entry['MeSSage']).to eq(msg)
26
+ end
27
+
28
+ it 'returns nil if not found' do
29
+ expect(entry['missing']).to be nil
30
+ end
31
+ end
32
+
33
+ describe 'each' do
34
+ it 'is chainable as an enumerator' do
35
+ expect(entry.each.class).to be(Enumerator)
36
+ end
37
+
38
+ it 'yields each key/value in turn' do
39
+ expect(entry.map{ |k,v| [k,v] }).to eq([['_PID', pid], ['MESSAGE', msg]])
40
+ end
41
+ end
42
+
43
+ describe 'catalog' do
44
+ context 'without a catalog' do
45
+ it 'returns nil if the entry has no catalog' do
46
+ expect(entry.catalog).to be nil
47
+ end
48
+ end
49
+
50
+ context 'with a catalog' do
51
+ let(:catalog) { 'Process @_PID@ said @MESSAGE@' }
52
+ subject(:entry) do
53
+ Systemd::JournalEntry.new(hash.merge(message_id: '123'))
54
+ end
55
+
56
+ before(:each) do
57
+ allow(Systemd::Journal).to receive(:catalog_for).and_return(catalog)
58
+ end
59
+
60
+ it 'does field substitution by default' do
61
+ expect(entry.catalog).to eq('Process 123 said test message')
62
+ end
63
+
64
+ it 'does field substitution when requested' do
65
+ expect(entry.catalog(replace: true))
66
+ .to eq('Process 123 said test message')
67
+ end
68
+
69
+ it 'skips field substition if requested' do
70
+ expect(entry.catalog(replace: false)).to eq(catalog)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe Systemd::Journal do
4
4
  subject(:j) do
5
- Systemd::Journal.new(files: [journal_file]).tap do |j|
5
+ Systemd::Journal.new(file: journal_file).tap do |j|
6
6
  j.seek(:head)
7
7
  j.move_next
8
8
  end
@@ -31,7 +31,7 @@ RSpec.describe Systemd::Journal do
31
31
  transports = %w(syslog journal stdout kernel driver)
32
32
 
33
33
  expect(values.length).to eq(5)
34
- expect(values).to include(*transports)
34
+ expect(values).to include(*transports)
35
35
  end
36
36
  end
37
37
 
@@ -44,13 +44,15 @@ RSpec.describe Systemd::Journal do
44
44
  end
45
45
 
46
46
  it 'returns the disk usage of the example journal file' do
47
- expect(j.disk_usage).to eq(4005888)
47
+ pending 'blocks? bytes?'
48
+ expect(j.disk_usage).to eq(4_005_888)
48
49
  end
49
50
  end
50
51
 
51
52
  describe 'data_threshold' do
52
53
  it 'throws a JournalError on invalid return code' do
53
- expect(Systemd::Journal::Native).to receive(:sd_journal_get_data_threshold)
54
+ expect(Systemd::Journal::Native)
55
+ .to receive(:sd_journal_get_data_threshold)
54
56
  .and_return(-1)
55
57
 
56
58
  expect { j.data_threshold }.to raise_error(Systemd::JournalError)
@@ -63,7 +65,8 @@ RSpec.describe Systemd::Journal do
63
65
 
64
66
  describe 'data_threshold=' do
65
67
  it 'throws a JournalError on invalid return code' do
66
- expect(Systemd::Journal::Native).to receive(:sd_journal_set_data_threshold)
68
+ expect(Systemd::Journal::Native)
69
+ .to receive(:sd_journal_set_data_threshold)
67
70
  .and_return(-1)
68
71
 
69
72
  expect { j.data_threshold = 10 }.to raise_error(Systemd::JournalError)
@@ -94,7 +97,7 @@ RSpec.describe Systemd::Journal do
94
97
  it 'returns a JournalEntry with the correct values' do
95
98
  entry = j.current_entry
96
99
  expect(entry._hostname).to eq('arch')
97
- expect(entry.message).to start_with('Allowing runtime journal')
100
+ expect(entry.message).to start_with('Allowing runtime journal')
98
101
  end
99
102
  end
100
103
 
@@ -114,26 +117,212 @@ RSpec.describe Systemd::Journal do
114
117
  entries = j.each.map(&:message)
115
118
 
116
119
  expect(entries.first).to start_with('Allowing runtime journal')
117
- expect(entries.last).to start_with('ROOT LOGIN ON tty1')
120
+ expect(entries.last).to start_with('ROOT LOGIN ON tty1')
118
121
  end
119
122
  end
120
123
 
121
- describe 'catalog_for' do
122
- subject(:j) { Systemd::Journal }
123
- let(:message_id) { 'f77379a8490b408bbe5f6940505a777b' }
124
- let(:message_text) { 'Subject: The Journal has been started' }
124
+ context 'with catalog messages' do
125
+ let(:msg_id) { 'f77379a8490b408bbe5f6940505a777b' }
126
+ let(:msg_text) { 'Subject: The Journal has been started' }
125
127
 
126
- it 'throws a JournalError on invalid return code' do
127
- expect(Systemd::Journal::Native)
128
- .to receive(:sd_journal_get_catalog_for_message_id)
128
+ describe 'catalog_for' do
129
+ subject(:j) { Systemd::Journal }
130
+
131
+ it 'throws a JournalError on invalid return code' do
132
+ expect(Systemd::Journal::Native)
133
+ .to receive(:sd_journal_get_catalog_for_message_id)
134
+ .and_return(-1)
135
+
136
+ expect { j.catalog_for(msg_id) }.to raise_error(Systemd::JournalError)
137
+ end
138
+
139
+ it 'returns the correct catalog entry' do
140
+ cat = Systemd::Journal.catalog_for(msg_id)
141
+ expect(cat).to start_with(msg_text)
142
+ end
143
+ end
144
+
145
+ describe 'current_catalog' do
146
+ it 'throws a JournalError on invalid return code' do
147
+ expect(Systemd::Journal::Native)
148
+ .to receive(:sd_journal_get_catalog)
149
+ .and_return(-1)
150
+
151
+ expect { j.current_catalog }.to raise_error(Systemd::JournalError)
152
+ end
153
+
154
+ it 'returns the correct catalog entry' do
155
+ # find first entry with a catalog
156
+ j.move_next until j.current_entry.catalog?
157
+
158
+ expect(j.current_catalog).to start_with(msg_text)
159
+ end
160
+ end
161
+ end
162
+
163
+ describe 'filter' do
164
+ it 'does basic filtering as expected' do
165
+ j.filter(_transport: 'kernel')
166
+ j.each do |entry|
167
+ expect(entry._transport).to eq('kernel')
168
+ end
169
+ expect(j.count).to eq(435)
170
+ end
171
+
172
+ it 'does filtering with AND conditions' do
173
+ j.filter(_transport: 'kernel', priority: 3)
174
+ expect(j.count).to eq(2)
175
+ j.each do |e|
176
+ expect(e._transport).to eq('kernel')
177
+ expect(e.priority).to eq('3')
178
+ end
179
+ end
180
+
181
+ it 'does basic filtering with multiple options for the same key' do
182
+ j.filter(_transport: %w(kernel driver))
183
+ j.each do |entry|
184
+ expect(%w(kernel driver)).to include(entry._transport)
185
+ end
186
+ expect(j.count).to eq(438)
187
+ end
188
+
189
+ it 'does basic filtering with multiple keys' do
190
+ j.filter(
191
+ { _transport: 'kernel' },
192
+ { _systemd_unit: 'systemd-journald.service' }
193
+ )
194
+
195
+ c = j.each_with_object(Hash.new(0)) do |e, h|
196
+ h[:transport] += 1 if e._transport == 'kernel'
197
+ h[:unit] += 1 if e._systemd_unit == 'systemd-journald.service'
198
+ end
199
+
200
+ expect(c[:transport]).to eq(435)
201
+ expect(c[:unit]).to eq(3)
202
+ end
203
+
204
+ it 'does crazy stupid filtering' do
205
+ filter = [
206
+ { _transport: 'kernel', priority: 4 },
207
+ { _systemd_unit: 'getty@tty1.service' },
208
+ { _systemd_unit: 'systemd-logind.service', seat_id: 'seat0' },
209
+ { priority: [3, 5] }
210
+ ]
211
+
212
+ j.filter(*filter)
213
+
214
+ c = j.each_with_object(Hash.new(0)) do |e, h|
215
+ h[:a] += 1 if e._transport == 'kernel' && e.priority == '4'
216
+ h[:b] += 1 if e._systemd_unit == 'getty@tty1.service'
217
+ if e._systemd_unit == 'systemd-logind.service' && e[:seat_id] == 'seat0'
218
+ h[:c] += 1
219
+ end
220
+ h[:d] += 1 if %w(3 5).include?(e.priority)
221
+ end
222
+
223
+ # from journalctl --file <fixture> <filter> --output json | wc -l
224
+ expect(c[:a]).to eq(26)
225
+ expect(c[:b]).to eq(1)
226
+ expect(c[:c]).to eq(1)
227
+ expect(c[:d]).to eq(11)
228
+ end
229
+ end
230
+
231
+ describe 'cursor' do
232
+ it 'returns some opaque string' do
233
+ expect(j.cursor).to be_kind_of(String)
234
+ end
235
+
236
+ it 'throws an error on failure' do
237
+ expect(Systemd::Journal::Native).to receive(:sd_journal_get_cursor)
238
+ .and_return(-1)
239
+
240
+ expect { j.cursor }.to raise_error(Systemd::JournalError)
241
+ end
242
+ end
243
+
244
+ describe 'cursor?' do
245
+ let!(:cursor) { j.cursor }
246
+ it 'returns true if the cursor matches' do
247
+ expect(j.cursor?(cursor)).to be true
248
+ end
249
+
250
+ it 'throws an error on failure' do
251
+ expect(Systemd::Journal::Native).to receive(:sd_journal_test_cursor)
129
252
  .and_return(-1)
130
253
 
131
- expect { j.catalog_for(message_id) }.to raise_error(Systemd::JournalError)
254
+ expect { j.cursor?(cursor) }.to raise_error(Systemd::JournalError)
255
+ end
256
+ end
257
+
258
+ describe 'move' do
259
+ it 'moves by the specified number of entries' do
260
+ j.move(1)
261
+ expect(j.read_field(:message)).to eq(journal_json[1]['MESSAGE'])
262
+ end
263
+
264
+ it 'does not move with 0' do
265
+ j.move(1)
266
+ j.move(0)
267
+ expect(j.read_field(:message)).to eq(journal_json[1]['MESSAGE'])
268
+ end
269
+
270
+ it 'moves backwards' do
271
+ j.move(3)
272
+ j.move(-1)
273
+ expect(j.read_field(:message)).to eq(journal_json[2]['MESSAGE'])
274
+ end
275
+
276
+ it 'returns the number of entries moved' do
277
+ expect(j.move(3)).to eq(3)
278
+ end
279
+
280
+ it 'returns the number of entries moved even if if less' do
281
+ j.move(2)
282
+ expect(j.read_field(:message)).to eq(journal_json[2]['MESSAGE'])
283
+ expect(j.move(-5)).to eq(2)
284
+ end
285
+
286
+ it 'returns 0 if it did not move' do
287
+ expect(j.move(-1)).to eq(0)
288
+ end
289
+ end
290
+
291
+ describe 'seek' do
292
+ it 'treats a string parameter as the cursor' do
293
+ cursor = j.cursor
294
+ j.move(3)
295
+ expect(j.cursor?(cursor)).to be false
296
+ j.seek(cursor)
297
+ j.move_next
298
+ expect(j.cursor?(cursor)).to be true
299
+ end
300
+
301
+ it 'can seek to the end' do
302
+ j.seek(:tail)
303
+ j.move_previous
304
+ expect(j.move_next).to be false
305
+ end
306
+
307
+ it 'can seek to the start' do
308
+ j.seek(:start)
309
+ j.move_next
310
+ expect(j.move_previous).to be false
311
+ end
312
+
313
+ it 'can seek based on timestamp' do
314
+ j.seek(Time.parse('2013-03-28T21:07:21-04:00'))
315
+ j.move_next
316
+
317
+ entry = j.current_entry
318
+ ts = entry.realtime_timestamp
319
+
320
+ expect(entry.message).to start_with('input: ImExPS/2')
321
+ expect(ts.utc.iso8601).to eq('2013-03-29T01:07:21Z')
132
322
  end
133
323
 
134
- it 'returns the correct catalog entry' do
135
- cat = Systemd::Journal.catalog_for(message_id)
136
- expect(cat).to start_with(message_text)
324
+ it 'throws an ArgumentError for other types' do
325
+ expect { j.seek(5) }.to raise_error(ArgumentError)
137
326
  end
138
327
  end
139
328
 
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: 1.1.3
4
+ version: 1.2.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: 2014-06-17 00:00:00.000000000 Z
12
+ date: 2014-07-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
@@ -119,6 +119,7 @@ extra_rdoc_files: []
119
119
  files:
120
120
  - ".gitignore"
121
121
  - ".rubocop.yml"
122
+ - ".travis.yml"
122
123
  - Gemfile
123
124
  - LICENSE.txt
124
125
  - README.md
@@ -142,6 +143,7 @@ files:
142
143
  - spec/fixtures/test.journal
143
144
  - spec/fixtures/test.json
144
145
  - spec/spec_helper.rb
146
+ - spec/systemd/journal_entry_spec.rb
145
147
  - spec/systemd/journal_spec.rb
146
148
  - systemd-journal.gemspec
147
149
  homepage: https://github.com/ledbettj/systemd-journal
@@ -172,5 +174,6 @@ test_files:
172
174
  - spec/fixtures/test.journal
173
175
  - spec/fixtures/test.json
174
176
  - spec/spec_helper.rb
177
+ - spec/systemd/journal_entry_spec.rb
175
178
  - spec/systemd/journal_spec.rb
176
179
  has_rdoc: