systemd-journal 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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: