solaris-utmpx 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/README.rdoc +34 -0
- data/Rakefile +28 -0
- data/examples/filter_root.rb +11 -0
- data/examples/wtmpx.rb +10 -0
- data/lib/solaris.rb +7 -0
- data/lib/solaris/utmpx.rb +113 -0
- data/test/test_create.rb +45 -0
- data/test/test_utmpx_i386.rb +59 -0
- data/test/test_utmpx_sparc.rb +59 -0
- data/test/wtmpx.i386 +0 -0
- data/test/wtmpx.sparc +0 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bea80841580cccc83697be3b85853852a04fc799
|
4
|
+
data.tar.gz: a23e1f074cd99fbe8b488827deddfbafaed497dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: df97be060d9ced25a54d277008531a3912243d14147b85b93b8e6340b5cd64267359fa93b4ff744e5e6b97f5a2af3a05a7198d3af1a90baef89ae0a7afb2abdb
|
7
|
+
data.tar.gz: 506f30185ec4434791475c8af09a515e500e3851cc83915109f6bd6f78164cef1724f4e1a70405e14b7f79d3fff8c121a974ca34f5553d6ac64661e58244f34e
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright 2013 Martin Carpenter. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are
|
4
|
+
permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this list of
|
7
|
+
conditions and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY Martin Carpenter ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
14
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Martin Carpenter OR
|
16
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
17
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
18
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
19
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
20
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
21
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
|
+
|
23
|
+
The views and conclusions contained in the software and documentation are those of the
|
24
|
+
authors and should not be interpreted as representing official policies, either expressed
|
25
|
+
or implied, of Martin Carpenter.
|
26
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
= solaris-utmpx
|
3
|
+
|
4
|
+
Author:: Martin Carpenter
|
5
|
+
Email:: mcarpenter@free.fr
|
6
|
+
Copyright:: Copyright (c) Martin Carpenter 2013
|
7
|
+
|
8
|
+
== About
|
9
|
+
The solaris-utmpx gem helps with the reading and writing of Solaris
|
10
|
+
binary utmpx(4) and wtmpx(4) files.
|
11
|
+
|
12
|
+
== Examples
|
13
|
+
|
14
|
+
=== Read and display all records in wtmpx
|
15
|
+
|
16
|
+
require 'solaris/utmpx'
|
17
|
+
|
18
|
+
io = File.open('/var/adm/wtmpx', 'r')
|
19
|
+
reader = Solaris::Wtmpx.new(:endian => :little)
|
20
|
+
while !io.eof? do
|
21
|
+
puts reader.read(io)
|
22
|
+
end
|
23
|
+
|
24
|
+
=== Filter all root entries from wtmpx
|
25
|
+
|
26
|
+
require 'solaris/utmpx'
|
27
|
+
|
28
|
+
io = File.open('/var/adm/wtmpx', 'r')
|
29
|
+
reader = Solaris::Wtmpx.new(:endian => :little)
|
30
|
+
while !io.eof? do
|
31
|
+
record = reader.read(io)
|
32
|
+
print record.to_binary_s unless record.ut_user == 'root'
|
33
|
+
end
|
34
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rdoc/task'
|
5
|
+
require 'rubygems/package_task'
|
6
|
+
|
7
|
+
desc 'Default task (package)'
|
8
|
+
task :default => [:package]
|
9
|
+
|
10
|
+
Rake::TestTask.new( 'test' )
|
11
|
+
|
12
|
+
SPECFILE = 'solaris-utmpx.gemspec'
|
13
|
+
if File.exist?( SPECFILE )
|
14
|
+
spec = eval( File.read( SPECFILE ) )
|
15
|
+
Gem::PackageTask.new( spec ).define
|
16
|
+
end
|
17
|
+
|
18
|
+
RDoc::Task.new do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'solaris-utmpx'
|
21
|
+
rdoc.options << '--charset' << 'utf-8'
|
22
|
+
rdoc.options << '--main' << 'README.rdoc'
|
23
|
+
rdoc.options << '--all'
|
24
|
+
rdoc.rdoc_files.include( 'README.rdoc' )
|
25
|
+
rdoc.rdoc_files.include( FileList[ 'lib/**/*.rb' ] )
|
26
|
+
rdoc.rdoc_files.include( FileList[ 'test/**/*.rb' ] )
|
27
|
+
end
|
28
|
+
|
data/examples/wtmpx.rb
ADDED
data/lib/solaris.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
require 'bindata'
|
3
|
+
|
4
|
+
module Solaris
|
5
|
+
|
6
|
+
# See "struct futmpx" in /usr/include/utmpx.h:
|
7
|
+
#
|
8
|
+
# struct futmpx {
|
9
|
+
# char ut_user[32]; /* user login name */
|
10
|
+
# char ut_id[4]; /* inittab id */
|
11
|
+
# char ut_line[32]; /* device name (console, lnxx) */
|
12
|
+
# pid32_t ut_pid; /* process id */
|
13
|
+
# int16_t ut_type; /* type of entry */
|
14
|
+
# struct {
|
15
|
+
# int16_t e_termination; /* process termination status */
|
16
|
+
# int16_t e_exit; /* process exit status */
|
17
|
+
# } ut_exit; /* exit status of a process */
|
18
|
+
# struct timeval32 ut_tv; /* time entry was made */
|
19
|
+
# int32_t ut_session; /* session ID, user for windowing */
|
20
|
+
# int32_t pad[5]; /* reserved for future use */
|
21
|
+
# int16_t ut_syslen; /* significant length of ut_host */
|
22
|
+
# char ut_host[257]; /* remote host name */
|
23
|
+
# };
|
24
|
+
#
|
25
|
+
class Utmpx < BinData::Record
|
26
|
+
|
27
|
+
# The (anonymous) class of generated records.
|
28
|
+
attr_reader :record_class
|
29
|
+
|
30
|
+
# Length of a raw utmpx record in bytes.
|
31
|
+
RECORD_LENGTH = 372
|
32
|
+
|
33
|
+
# Create a new Utmpx factory object. Options are:
|
34
|
+
# * :endian -- mandatory, set to :big (SPARC) or :little (i386)
|
35
|
+
# * :trim_padding -- trim terminating nulls from read strings
|
36
|
+
# This will generate objects that are subclasses of BinData::Record.
|
37
|
+
def initialize(opts)
|
38
|
+
|
39
|
+
endianism = nil
|
40
|
+
trim_padding = true
|
41
|
+
opts.each do |key, value|
|
42
|
+
case key
|
43
|
+
when :endian
|
44
|
+
endianism = value
|
45
|
+
when :trim_padding
|
46
|
+
trim_padding = value
|
47
|
+
else
|
48
|
+
raise ArgumentError, "Unknown option #{key.inspect}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@record_class = Class.new(BinData::Record) do
|
53
|
+
|
54
|
+
endian endianism
|
55
|
+
|
56
|
+
string :ut_user, :length => 32, :trim_padding => trim_padding
|
57
|
+
string :ut_id, :length => 4, :trim_padding => trim_padding
|
58
|
+
string :ut_line, :length => 32, :trim_padding => trim_padding
|
59
|
+
uint32 :ut_pid
|
60
|
+
uint16 :ut_type
|
61
|
+
string :pad_word1, :length => 2 # align to 4 byte words
|
62
|
+
uint16 :ut_termination
|
63
|
+
uint16 :ut_exit
|
64
|
+
uint32 :ut_tv_sec
|
65
|
+
uint32 :ut_tv_usec
|
66
|
+
uint32 :ut_session
|
67
|
+
uint32 :pad0
|
68
|
+
uint32 :pad1
|
69
|
+
uint32 :pad2
|
70
|
+
uint32 :pad3
|
71
|
+
uint32 :pad4
|
72
|
+
uint16 :ut_syslen
|
73
|
+
string :ut_host, :length => 257, :trim_padding => trim_padding
|
74
|
+
string :pad_word2, :length => 1 # align to 4 byte words
|
75
|
+
|
76
|
+
# Return the timestamp of this record as a Time object in the local TZ.
|
77
|
+
def localtime
|
78
|
+
Time.at(self.ut_tv_sec + self.ut_tv_usec / 1_000_000.0)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a new record. Interesting option keys are:
|
86
|
+
# * :ut_user
|
87
|
+
# * :ut_id
|
88
|
+
# * :ut_line
|
89
|
+
# * :ut_pid
|
90
|
+
# * :ut_type
|
91
|
+
# * :ut_termination
|
92
|
+
# * :ut_exit
|
93
|
+
# * :ut_tv_sec
|
94
|
+
# * :ut_tv_usec
|
95
|
+
# * :ut_session
|
96
|
+
# * :ut_syslen
|
97
|
+
# * :ut_host
|
98
|
+
def create(opts={})
|
99
|
+
# BinData silently discards unknown fields so we check.
|
100
|
+
unknown_fields = opts.keys - self.record_class.fields.fields.map(&:name)
|
101
|
+
raise ArgumentError, "Unknown fields #{unknown_fields.inspect}" unless unknown_fields.empty?
|
102
|
+
@record_class.new(opts)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Read a utmpx record from the given IO object.
|
106
|
+
def read(io)
|
107
|
+
@record_class.read(io)
|
108
|
+
end
|
109
|
+
|
110
|
+
end # Utmpx
|
111
|
+
|
112
|
+
end # Solaris
|
113
|
+
|
data/test/test_create.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'solaris/utmpx'
|
4
|
+
|
5
|
+
class TestCreate < Test::Unit::TestCase #:nodoc:
|
6
|
+
|
7
|
+
def test_create
|
8
|
+
utmpx = Solaris::Utmpx.new(:endian => :big)
|
9
|
+
record = utmpx.create(
|
10
|
+
:ut_user => "root",
|
11
|
+
:ut_id => "co10",
|
12
|
+
:ut_line => "console",
|
13
|
+
:ut_pid => 1407,
|
14
|
+
:ut_type => 7,
|
15
|
+
:ut_termination => 0,
|
16
|
+
:ut_exit => 53726,
|
17
|
+
:ut_tv_sec => 1297338834,
|
18
|
+
:ut_tv_usec => 0,
|
19
|
+
:ut_session => 0,
|
20
|
+
:ut_syslen => 8,
|
21
|
+
:ut_host => "jupiter"
|
22
|
+
)
|
23
|
+
assert_equal("root", record.ut_user)
|
24
|
+
assert_equal("co10", record.ut_id)
|
25
|
+
assert_equal("console", record.ut_line)
|
26
|
+
assert_equal(1407, record.ut_pid)
|
27
|
+
assert_equal(7, record.ut_type)
|
28
|
+
assert_equal(0, record.ut_termination)
|
29
|
+
assert_equal(53726, record.ut_exit)
|
30
|
+
assert_equal(1297338834, record.ut_tv_sec)
|
31
|
+
assert_equal(0, record.ut_tv_usec)
|
32
|
+
assert_equal(0, record.ut_session)
|
33
|
+
assert_equal(8, record.ut_syslen)
|
34
|
+
assert_equal("jupiter", record.ut_host)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_create_invalid_fields
|
38
|
+
utmpx = Solaris::Utmpx.new(:endian => :big)
|
39
|
+
assert_raise(ArgumentError) do
|
40
|
+
utmpx.create(:quack => :boom)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end # TestCreate
|
45
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'solaris/utmpx'
|
4
|
+
|
5
|
+
class TestUtmpxI386 < Test::Unit::TestCase #:nodoc:
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@reader = Solaris::Utmpx.new(:endian => :little)
|
9
|
+
@io = File.open(File.join(File.dirname(__FILE__), 'wtmpx.i386'), 'r')
|
10
|
+
@records = []
|
11
|
+
4.times { @records << @reader.read(@io) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@io.close
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_read
|
19
|
+
assert_equal("root", @records[3].ut_user)
|
20
|
+
assert_equal("co10", @records[3].ut_id)
|
21
|
+
assert_equal("console", @records[3].ut_line)
|
22
|
+
assert_equal(1407, @records[3].ut_pid)
|
23
|
+
assert_equal(7, @records[3].ut_type)
|
24
|
+
assert_equal(0, @records[3].ut_termination)
|
25
|
+
assert_equal(53726, @records[3].ut_exit)
|
26
|
+
assert_equal(1297338834, @records[3].ut_tv_sec)
|
27
|
+
assert_equal(0, @records[3].ut_tv_usec)
|
28
|
+
assert_equal(0, @records[3].ut_session)
|
29
|
+
assert_equal(0, @records[3].ut_syslen)
|
30
|
+
assert_equal("", @records[3].ut_host)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_record_length
|
34
|
+
@records.each do |record|
|
35
|
+
assert_equal(Solaris::Utmpx::RECORD_LENGTH, record.to_binary_s.length)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_ut_syslen
|
40
|
+
@records.each do |record|
|
41
|
+
# +1 since ut_syslen includes terminating null but only when non-zero
|
42
|
+
if record.ut_syslen == 0
|
43
|
+
assert_equal(record.ut_syslen, record.ut_host.length, "#{record}")
|
44
|
+
else
|
45
|
+
assert_equal(record.ut_syslen, record.ut_host.length + 1, "#{record}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_to_binary_s
|
51
|
+
@io.rewind
|
52
|
+
@records.each do |record|
|
53
|
+
raw = @io.read(Solaris::Utmpx::RECORD_LENGTH)
|
54
|
+
assert_equal(raw, record.to_binary_s)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end # TestUtmpxI386
|
59
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'solaris/utmpx'
|
4
|
+
|
5
|
+
class TestUtmpxSparc < Test::Unit::TestCase #:nodoc:
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@reader = Solaris::Utmpx.new(:endian => :big)
|
9
|
+
@io = File.open(File.join(File.dirname(__FILE__), 'wtmpx.sparc'), 'r')
|
10
|
+
@records = []
|
11
|
+
4.times { @records << @reader.read(@io) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@io.close
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_read
|
19
|
+
assert_equal("root", @records[3].ut_user)
|
20
|
+
assert_equal("co10", @records[3].ut_id)
|
21
|
+
assert_equal("console", @records[3].ut_line)
|
22
|
+
assert_equal(3944, @records[3].ut_pid)
|
23
|
+
assert_equal(7, @records[3].ut_type)
|
24
|
+
assert_equal(0, @records[3].ut_termination)
|
25
|
+
assert_equal(0, @records[3].ut_exit)
|
26
|
+
assert_equal(1230681969, @records[3].ut_tv_sec)
|
27
|
+
assert_equal(0, @records[3].ut_tv_usec)
|
28
|
+
assert_equal(0, @records[3].ut_session)
|
29
|
+
assert_equal(0, @records[3].ut_syslen)
|
30
|
+
assert_equal("", @records[3].ut_host)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_record_length
|
34
|
+
@records.each do |record|
|
35
|
+
assert_equal(Solaris::Utmpx::RECORD_LENGTH, record.to_binary_s.length)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_ut_syslen
|
40
|
+
@records.each do |record|
|
41
|
+
# +1 since ut_syslen includes terminating null but only when non-zero
|
42
|
+
if record.ut_syslen == 0
|
43
|
+
assert_equal(record.ut_syslen, record.ut_host.length, "#{record}")
|
44
|
+
else
|
45
|
+
assert_equal(record.ut_syslen, record.ut_host.length + 1, "#{record}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_to_binary_s
|
51
|
+
@io.rewind
|
52
|
+
@records.each do |record|
|
53
|
+
raw = @io.read(Solaris::Utmpx::RECORD_LENGTH)
|
54
|
+
assert_equal(raw, record.to_binary_s)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end # TestUtmpxSparc
|
59
|
+
|
data/test/wtmpx.i386
ADDED
Binary file
|
data/test/wtmpx.sparc
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: solaris-utmpx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Carpenter
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bindata
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.5.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.5.0
|
27
|
+
description: Read and write Solaris utmpx and wtmpx files
|
28
|
+
email: mcarpenter@free.fr
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- Rakefile
|
34
|
+
- README.rdoc
|
35
|
+
files:
|
36
|
+
- examples/filter_root.rb
|
37
|
+
- examples/wtmpx.rb
|
38
|
+
- lib/solaris.rb
|
39
|
+
- lib/solaris/utmpx.rb
|
40
|
+
- test/test_create.rb
|
41
|
+
- test/test_utmpx_i386.rb
|
42
|
+
- test/test_utmpx_sparc.rb
|
43
|
+
- test/wtmpx.i386
|
44
|
+
- test/wtmpx.sparc
|
45
|
+
- LICENSE
|
46
|
+
- Rakefile
|
47
|
+
- README.rdoc
|
48
|
+
homepage: http://github.com/mcarpenter/solaris-utmpx
|
49
|
+
licenses:
|
50
|
+
- BSD
|
51
|
+
metadata: {}
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.0.3
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: Read and write Solaris utmpx and wtmpx files
|
72
|
+
test_files:
|
73
|
+
- test/test_create.rb
|
74
|
+
- test/test_utmpx_i386.rb
|
75
|
+
- test/test_utmpx_sparc.rb
|