solaris-contents 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +26 -0
- data/README.rdoc +84 -0
- data/Rakefile +27 -0
- data/lib/solaris.rb +8 -0
- data/lib/solaris/contents.rb +268 -0
- data/lib/solaris/contents/pkg.rb +71 -0
- data/test/test_contents.rb +205 -0
- data/test/test_pkg.rb +82 -0
- data/test/test_solaris.rb +22 -0
- metadata +60 -0
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright 2012 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,84 @@
|
|
1
|
+
|
2
|
+
= solaris-contents
|
3
|
+
|
4
|
+
Author:: Martin Carpenter
|
5
|
+
Email:: mcarpenter@free.fr
|
6
|
+
Copyright:: Copyright (c) Martin Carpenter 2011
|
7
|
+
|
8
|
+
|
9
|
+
== About
|
10
|
+
|
11
|
+
The solaris-contents gem helps with the manipulation of SunOS and Solaris
|
12
|
+
contents entries for SysV packages. Note that the manual page contents(4)
|
13
|
+
states that this interface is declared as unstable although practically
|
14
|
+
speaking this is unlikely to be an issue.
|
15
|
+
|
16
|
+
As shown above, the interface stability of
|
17
|
+
/var/sadm/install/contents is Unstable (see attributes(5)).
|
18
|
+
It is common practice to use this file in a read-only manner
|
19
|
+
to determine which files belong to which packages installed
|
20
|
+
on a system. While this file has been present for many
|
21
|
+
releases of the Solaris operating system, it might not be
|
22
|
+
present in future releases. The fully supported way to
|
23
|
+
obtain information from the installed package database is
|
24
|
+
through pkgchk(1M). It is highly recommended that you use
|
25
|
+
pkgchk rather than relying on the contents file.
|
26
|
+
|
27
|
+
This prescient comment is coming true: Solaris 11 moves to a new packaging
|
28
|
+
format (IPS) and although contents(4) remains on the system and in the
|
29
|
+
manual pages it appears to be unused.
|
30
|
+
|
31
|
+
This gem only handles "new style" contents entries. In extensive testing
|
32
|
+
we have not seen any old style entries (these are lines that do not
|
33
|
+
begin with a forward slash, /).
|
34
|
+
|
35
|
+
See Errata below from where Oracle's specification in contents(4)
|
36
|
+
differs from reality.
|
37
|
+
|
38
|
+
== Examples
|
39
|
+
|
40
|
+
=== Read and interpret contents(4)
|
41
|
+
|
42
|
+
require 'solaris/contents'
|
43
|
+
|
44
|
+
Solaris::Contents.read.each do |c|
|
45
|
+
puts c
|
46
|
+
puts c.ftype
|
47
|
+
puts c.path
|
48
|
+
puts c.rpath
|
49
|
+
puts c.install_path
|
50
|
+
puts c.packages.inspect
|
51
|
+
end
|
52
|
+
=> /bin=./usr/bin s none SUNWcsr
|
53
|
+
s
|
54
|
+
/bin
|
55
|
+
/usr/bin
|
56
|
+
none
|
57
|
+
[ "SUNWcsr" ]
|
58
|
+
...
|
59
|
+
|
60
|
+
== Errata
|
61
|
+
|
62
|
+
Testing against a corpus of ~150 contents files revealed the following
|
63
|
+
discrepancies from the specification in the manual pages for contents(4)
|
64
|
+
and pkgmap(4).
|
65
|
+
|
66
|
+
=== Multiple packages per entry
|
67
|
+
|
68
|
+
Multiple packages are only explicitly stated for ftype d in contents(4).
|
69
|
+
Numerous counterexamples.
|
70
|
+
|
71
|
+
=== Installation class name length
|
72
|
+
|
73
|
+
pkgmap(4) states that the class name should be "no longer than 12
|
74
|
+
characters". Counterexample: "pkcs11confbase".
|
75
|
+
|
76
|
+
=== Comments
|
77
|
+
pkgadd(1M) writes comments to the tail of the contents file:
|
78
|
+
|
79
|
+
# Last modified by pkgadd for SMClintl package
|
80
|
+
# Tue Sep 20 21:57:16 2011
|
81
|
+
|
82
|
+
If the Solaris::Contents#from_line constructor is fed a line like this
|
83
|
+
(or a blank line) then it will return nil.
|
84
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
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-contents.gemspec'
|
13
|
+
if File.exist?( SPECFILE )
|
14
|
+
spec = eval( File.read( SPECFILE ) )
|
15
|
+
Gem::PackageTask.new( spec ).define
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake::RDocTask.new do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'solaris-contents'
|
21
|
+
rdoc.options << '--charset' << 'utf-8' << '--main' << 'README.rdoc'
|
22
|
+
rdoc.options << '--all'
|
23
|
+
rdoc.rdoc_files.include( 'README.rdoc' )
|
24
|
+
rdoc.rdoc_files.include( FileList[ 'lib/**/*' ] )
|
25
|
+
rdoc.rdoc_files.include( FileList[ 'test/**/*' ] )
|
26
|
+
end
|
27
|
+
|
data/lib/solaris.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
|
2
|
+
require 'etc'
|
3
|
+
|
4
|
+
require 'solaris/contents/pkg'
|
5
|
+
|
6
|
+
module Solaris
|
7
|
+
|
8
|
+
# Class to represent Solaris package file contents.
|
9
|
+
# A contents line contains information regarding file
|
10
|
+
# type, location, ownership, permisions.
|
11
|
+
#
|
12
|
+
# See Solaris' contents(4) man-page for information on
|
13
|
+
# attributes.
|
14
|
+
class Contents
|
15
|
+
|
16
|
+
# The file type of the contents entry: one of the symbols
|
17
|
+
# :b, :c, :d, :e, :f, :v, :x, :l, :s.
|
18
|
+
attr_accessor :ftype
|
19
|
+
|
20
|
+
# The installation class of the file.
|
21
|
+
attr_accessor :install_class
|
22
|
+
|
23
|
+
# The path of the file.
|
24
|
+
attr_accessor :path
|
25
|
+
|
26
|
+
# The relative path for a linked file.
|
27
|
+
attr_accessor :rpath
|
28
|
+
|
29
|
+
# The major mode (integer), for device files (ftype :b or :c).
|
30
|
+
attr_accessor :major
|
31
|
+
|
32
|
+
# The minor mode (integer), for device files (ftype :b or :c).
|
33
|
+
attr_accessor :minor
|
34
|
+
|
35
|
+
# The mode of the file, as an integer. This is usually presented
|
36
|
+
# as an octal number.
|
37
|
+
attr_accessor :mode
|
38
|
+
|
39
|
+
# The file user (string).
|
40
|
+
attr_accessor :owner
|
41
|
+
|
42
|
+
# The file's group (string).
|
43
|
+
attr_accessor :group
|
44
|
+
|
45
|
+
# The file modification time (seconds from the start of 1970).
|
46
|
+
attr_accessor :mtime
|
47
|
+
|
48
|
+
# Array of package names to which this file belongs.
|
49
|
+
attr_accessor :packages
|
50
|
+
|
51
|
+
# The size of the file in bytes.
|
52
|
+
attr_accessor :size
|
53
|
+
|
54
|
+
# The size of the file in bytes modulo 65535. (See Contents#sum).
|
55
|
+
attr_accessor :sum
|
56
|
+
|
57
|
+
# Install class to use if not specified.
|
58
|
+
DEFAULT_INSTALL_CLASS = 'none'
|
59
|
+
|
60
|
+
# Default system contents(4) path.
|
61
|
+
DEFAULT_CONTENTS_PATH = '/var/sadm/install/contents'
|
62
|
+
|
63
|
+
# Regular expression for filetype.
|
64
|
+
RE_FTYPE = '([bcdefvxls])'
|
65
|
+
|
66
|
+
# Regular expression for install class.
|
67
|
+
# Solaris documentation states that this parameter is at most 12 characters
|
68
|
+
# but there are counterexamples in the wild from Sun/Oracle's own hand
|
69
|
+
# (eg class "pkcs11confbase").
|
70
|
+
RE_INSTALL_CLASS = '(\w+)'
|
71
|
+
|
72
|
+
# Regular expression for path.
|
73
|
+
RE_PATH = '(\S+)'
|
74
|
+
|
75
|
+
# Regular expression for major device mode.
|
76
|
+
RE_MAJOR = '(\d+)'
|
77
|
+
|
78
|
+
# Regular expression for minor device mode.
|
79
|
+
RE_MINOR = '(\d+)'
|
80
|
+
|
81
|
+
# Regular expression for octal file mode.
|
82
|
+
RE_MODE = '([0-7]{4})'
|
83
|
+
|
84
|
+
# Regular expression for file modification time.
|
85
|
+
RE_MTIME = '(\d+)'
|
86
|
+
|
87
|
+
# Regular expression for user name.
|
88
|
+
RE_OWNER = '(\S+)'
|
89
|
+
|
90
|
+
# Regular expression for group name.
|
91
|
+
RE_GROUP = '(\S+)'
|
92
|
+
|
93
|
+
# Regular expression for a package.
|
94
|
+
RE_PACKAGE = '(\S+)'
|
95
|
+
|
96
|
+
# Regular expression for file size.
|
97
|
+
RE_SIZE = '(\d+)'
|
98
|
+
|
99
|
+
# Regular expression for file sum.
|
100
|
+
RE_SUM = '(\d+)'
|
101
|
+
|
102
|
+
# Create a Contents object from a line from a contents(4) file. If line
|
103
|
+
# is empty or a comment (starts with a hash character) then return nil.
|
104
|
+
def self.from_line(line)
|
105
|
+
return nil if line.empty? || line =~ /^#/
|
106
|
+
ftype = $2.to_sym if line =~ /^#{RE_PATH} #{RE_FTYPE} /
|
107
|
+
re = case ftype
|
108
|
+
when :s, :l
|
109
|
+
/^#{RE_PATH}=#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} (#{RE_PACKAGE}( #{RE_PACKAGE})*)$/
|
110
|
+
when :d
|
111
|
+
/^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} (#{RE_PACKAGE}( #{RE_PACKAGE})*)$/
|
112
|
+
when :x
|
113
|
+
/^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} #{RE_PACKAGE}$/
|
114
|
+
when :b, :c
|
115
|
+
/^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MAJOR} #{RE_MINOR} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} #{RE_PACKAGE}$/
|
116
|
+
when :f, :v, :e
|
117
|
+
/^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} #{RE_SIZE} #{RE_SUM} #{RE_MTIME} (#{RE_PACKAGE}( #{RE_PACKAGE})*)$/
|
118
|
+
else
|
119
|
+
raise ArgumentError, "Unknown filetype in line #{line.inspect}"
|
120
|
+
end
|
121
|
+
if line =~ re
|
122
|
+
contents = self.new
|
123
|
+
contents.ftype = ftype
|
124
|
+
contents.path = $1
|
125
|
+
case ftype
|
126
|
+
when :s, :l
|
127
|
+
contents.rpath = $2
|
128
|
+
contents.install_class = $3
|
129
|
+
contents.packages = $4.split( /\s+/ ).map { |pkg| Pkg.new(pkg) }
|
130
|
+
when :d, :x
|
131
|
+
contents.install_class = $2
|
132
|
+
contents.mode = $3.to_i( 8 )
|
133
|
+
contents.owner = $4
|
134
|
+
contents.group = $5
|
135
|
+
contents.packages = $6.split( /\s+/ ).map { |pkg| Pkg.new(pkg) }
|
136
|
+
when :b, :c
|
137
|
+
contents.install_class = $2
|
138
|
+
contents.major = $3.to_i
|
139
|
+
contents.minor = $4.to_i
|
140
|
+
contents.mode = $5.to_i( 8 )
|
141
|
+
contents.owner = $6
|
142
|
+
contents.group = $7
|
143
|
+
contents.packages = $8.split( /\s+/ ).map { |pkg| Pkg.new(pkg) }
|
144
|
+
when :f, :v, :e
|
145
|
+
contents.install_class = $2
|
146
|
+
contents.mode = $3.to_i( 8 )
|
147
|
+
contents.owner = $4
|
148
|
+
contents.group =$5
|
149
|
+
contents.size = $6.to_i
|
150
|
+
contents.sum = $7.to_i
|
151
|
+
contents.mtime = $8.to_i
|
152
|
+
contents.packages = $9.split( /\s+/ ).map { |pkg| Pkg.new(pkg) }
|
153
|
+
end
|
154
|
+
else
|
155
|
+
raise ArgumentError, "Could not parse line #{line.inspect}"
|
156
|
+
end
|
157
|
+
contents
|
158
|
+
end
|
159
|
+
|
160
|
+
# Create a Contents entry from the file at the +path+ on the local
|
161
|
+
# filesystem.
|
162
|
+
#
|
163
|
+
# If +actual+ is provided then this is the path that is used for the
|
164
|
+
# object's pathname property although all other properties are
|
165
|
+
# created from the +path+ argument. The process must be able to
|
166
|
+
# stat(2) the file at +path+ to determine these properties.
|
167
|
+
def self.from_path(path, actual=nil)
|
168
|
+
contents = self.new
|
169
|
+
# Use #lstat since we are always interested in the link source,
|
170
|
+
# not the target.
|
171
|
+
stat = File.lstat( path )
|
172
|
+
raise RuntimeError, 'Unknown file type' if stat.ftype == 'unknown'
|
173
|
+
# Stat returns "link" for symlink, not "symlink"
|
174
|
+
contents.ftype = stat.symlink? ? :s : stat.ftype[0].to_sym
|
175
|
+
case contents.ftype
|
176
|
+
when :f
|
177
|
+
contents.sum = sum( path )
|
178
|
+
contents.size = stat.size
|
179
|
+
contents.mtime = stat.mtime.to_i
|
180
|
+
when :s
|
181
|
+
contents.rpath = File.realpath( path )
|
182
|
+
when :b, :c
|
183
|
+
contents.major = stat.dev_major
|
184
|
+
contents.minor = stat.dev_minor
|
185
|
+
when :d
|
186
|
+
#
|
187
|
+
else
|
188
|
+
raise RuntimeError, "Unknown ftype #{contents.ftype.inspect}"
|
189
|
+
end
|
190
|
+
contents.path = actual || path
|
191
|
+
contents.install_class = DEFAULT_INSTALL_CLASS
|
192
|
+
contents.mode = stat.mode & 07777
|
193
|
+
contents.owner = Etc.getpwuid( stat.uid ).name
|
194
|
+
contents.group = Etc.getgrgid( stat.gid ).name
|
195
|
+
contents
|
196
|
+
end
|
197
|
+
|
198
|
+
# Read a contents(4) file (default /var/sadm/install/contents)
|
199
|
+
# and return an array of package contents entries.
|
200
|
+
def self.read(path=DEFAULT_CONTENTS_PATH)
|
201
|
+
File.open( path ).lines.map do |line|
|
202
|
+
from_line( line )
|
203
|
+
end.compact
|
204
|
+
end
|
205
|
+
|
206
|
+
# Return the sum of the byte values of the file, modulo 65535. This is
|
207
|
+
# the value returned by Solaris' sum(1) (NB. not cksum(1) or sum(1B)).
|
208
|
+
# This is a weak checksum and should not be used for security purposes.
|
209
|
+
def self.sum(io_or_string)
|
210
|
+
io_or_string.each_byte.inject { |r, v| ( r + v ) & 0xffff }
|
211
|
+
end
|
212
|
+
|
213
|
+
# Create a new contents(4) object.
|
214
|
+
def initialize
|
215
|
+
@packages = []
|
216
|
+
end
|
217
|
+
|
218
|
+
# Return nil if no package has been specified for this contents entry.
|
219
|
+
# If only one package has been specified for this contents entry
|
220
|
+
# (all cases except, possibly, directory) then return that package.
|
221
|
+
# Otherwise throw a RuntimeError.
|
222
|
+
def package
|
223
|
+
raise RuntimeError, 'Ambiguous: contains more than one package' if @packages.size > 1
|
224
|
+
@packages[0]
|
225
|
+
end
|
226
|
+
|
227
|
+
# Convert the object to a contents(4) line (string).
|
228
|
+
def to_s
|
229
|
+
case @ftype
|
230
|
+
when :b, :c
|
231
|
+
[ @path, @ftype, @install_class, @major, @minor, mode_s, @owner, @group ] + @packages
|
232
|
+
when :d
|
233
|
+
[ @path, @ftype, @install_class, mode_s, @owner, @group ] + @packages
|
234
|
+
when :x
|
235
|
+
[ @path, @ftype, @install_class, mode_s, @owner, @group ] + @packages
|
236
|
+
when :e, :f, :v
|
237
|
+
[ @path, @ftype, @install_class, mode_s, @owner, @group, @size, @sum, @mtime ] + @packages
|
238
|
+
when :l, :s
|
239
|
+
[ "#{@path}=#{@rpath}", @ftype, @install_class ] + @packages
|
240
|
+
else
|
241
|
+
raise RuntimeError, "Unknown ftype #{@ftype.inspect}"
|
242
|
+
end.join( ' ' )
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns true if the object is a valid contents specification, false
|
246
|
+
# otherwise.
|
247
|
+
def valid?
|
248
|
+
begin
|
249
|
+
self.class.from_line( to_s )
|
250
|
+
rescue ArgumentError, RuntimeError
|
251
|
+
false
|
252
|
+
else
|
253
|
+
true
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
# Convert the file mode to a 4-digit octal string.
|
261
|
+
def mode_s
|
262
|
+
'%04o' % @mode
|
263
|
+
end
|
264
|
+
|
265
|
+
end # Contents
|
266
|
+
|
267
|
+
end # Solaris
|
268
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
module Solaris
|
3
|
+
|
4
|
+
class Contents
|
5
|
+
|
6
|
+
# Class to represent a package in the contents(4) file. Package
|
7
|
+
# names may be prefixed with a single character (and passed to
|
8
|
+
# the constructor). The semantics of that single character are
|
9
|
+
# not publicly supported but are described in
|
10
|
+
# usr/src/cmd/svr4pkg/hdrs/libinst.h:
|
11
|
+
# #define INST_RDY '+' /* entry is ready to installf -f */
|
12
|
+
# #define RM_RDY '-' /* entry is ready for removef -f */
|
13
|
+
# #define NOT_FND '!' /* entry (or part of entry) was not found */
|
14
|
+
# #define SERVED_FILE '%' /* using the file server's RO partition */
|
15
|
+
# #define STAT_NEXT '@' /* this is awaiting eptstat */
|
16
|
+
# #define DUP_ENTRY '#' /* there's a duplicate of this */
|
17
|
+
# #define CONFIRM_CONT '*' /* need to confirm contents */
|
18
|
+
# #define CONFIRM_ATTR '~' /* need to confirm attributes */
|
19
|
+
# #define ENTRY_OK '\0' /* entry is a confirmed file */
|
20
|
+
class Pkg
|
21
|
+
|
22
|
+
# The name of this package, without status indicator.
|
23
|
+
attr_accessor :name
|
24
|
+
|
25
|
+
# The status if this package as a symbol (see STATUS_BY_SYM).
|
26
|
+
attr_accessor :status
|
27
|
+
|
28
|
+
# Hash to map status symbols to their string representation.
|
29
|
+
STATUS_BY_SYM = {
|
30
|
+
:inst_rdy => '+',
|
31
|
+
:rm_rdy => '-',
|
32
|
+
:not_fnd => '!',
|
33
|
+
:served_file => '%',
|
34
|
+
:stat_next => '@',
|
35
|
+
:dup_entry => '#',
|
36
|
+
:confirm_cont => '*',
|
37
|
+
:confirm_attr => '~',
|
38
|
+
:entry_ok => ''
|
39
|
+
}
|
40
|
+
|
41
|
+
# Hash to map status strings to their symbolic representation.
|
42
|
+
STATUS_BY_STR = STATUS_BY_SYM.invert
|
43
|
+
|
44
|
+
def initialize(pkg)
|
45
|
+
# Use #chr for ruby 1.8 compatibility
|
46
|
+
status_char = pkg[0].chr
|
47
|
+
if STATUS_BY_STR.keys.include?(status_char)
|
48
|
+
@name = pkg[1..-1]
|
49
|
+
@status = STATUS_BY_STR[status_char]
|
50
|
+
else
|
51
|
+
@name = pkg
|
52
|
+
@status = :entry_ok
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
STATUS_BY_SYM[@status] + @name
|
58
|
+
end
|
59
|
+
|
60
|
+
STATUS_BY_SYM.keys.each do |key|
|
61
|
+
define_method("#{key}?") do
|
62
|
+
@status == key
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end # Pkg
|
67
|
+
|
68
|
+
end # Contents
|
69
|
+
|
70
|
+
end # Solaris
|
71
|
+
|
@@ -0,0 +1,205 @@
|
|
1
|
+
|
2
|
+
require 'stringio'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'solaris/contents'
|
6
|
+
|
7
|
+
# Unit tests for Solaris::Contents.
|
8
|
+
class TestContents < Test::Unit::TestCase #:nodoc:
|
9
|
+
|
10
|
+
def test_sum_from_string
|
11
|
+
assert_equal( 64, Solaris::Contents.sum( '@' ) )
|
12
|
+
assert_equal( 128, Solaris::Contents.sum( '@@' ) )
|
13
|
+
assert_equal( 64_000, Solaris::Contents.sum( '@' * 1_000 ) )
|
14
|
+
assert_equal( 65_472, Solaris::Contents.sum( '@' * 1_023 ) )
|
15
|
+
assert_equal( 0, Solaris::Contents.sum( '@' * 1_024 ) )
|
16
|
+
assert_equal( 64, Solaris::Contents.sum( '@' * 1_025 ) )
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_sum_from_io
|
20
|
+
assert_equal( 97, Solaris::Contents.sum( StringIO.new( 'a' ) ) )
|
21
|
+
assert_equal( 194, Solaris::Contents.sum( StringIO.new( 'aa' ) ) )
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_block_device
|
25
|
+
skip 'No block device example found'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_character_device
|
29
|
+
line = '/devices/pseudo/arp@0:arp c none 44 0 0666 root sys SUNWcsd'
|
30
|
+
contents = Solaris::Contents.from_line( line )
|
31
|
+
assert_equal( :c, contents.ftype )
|
32
|
+
assert_equal( '/devices/pseudo/arp@0:arp', contents.path )
|
33
|
+
assert_equal( nil, contents.rpath )
|
34
|
+
assert_equal( 'none', contents.install_class )
|
35
|
+
assert_equal( 438, contents.mode )
|
36
|
+
assert_equal( 44, contents.major )
|
37
|
+
assert_equal( 0, contents.minor )
|
38
|
+
assert_equal( nil, contents.mtime )
|
39
|
+
assert_equal( 'root', contents.owner )
|
40
|
+
assert_equal( 'sys', contents.group )
|
41
|
+
assert_equal( 'SUNWcsd', contents.package.to_s )
|
42
|
+
assert_equal( %w{ SUNWcsd }, contents.packages.map(&:to_s) )
|
43
|
+
assert_equal( nil, contents.size )
|
44
|
+
assert_equal( nil, contents.sum )
|
45
|
+
assert_equal( line, contents.to_s )
|
46
|
+
assert( contents.valid? )
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_directory
|
50
|
+
line = '/dev d none 0755 root sys SUNWcsr SUNWcsd'
|
51
|
+
contents = Solaris::Contents.from_line( line )
|
52
|
+
assert_equal( :d, contents.ftype )
|
53
|
+
assert_equal( '/dev', contents.path )
|
54
|
+
assert_equal( nil, contents.rpath )
|
55
|
+
assert_equal( 'none', contents.install_class )
|
56
|
+
assert_equal( 493, contents.mode )
|
57
|
+
assert_equal( nil, contents.major )
|
58
|
+
assert_equal( nil, contents.minor )
|
59
|
+
assert_equal( nil, contents.mtime )
|
60
|
+
assert_equal( 'root', contents.owner )
|
61
|
+
assert_equal( 'sys', contents.group )
|
62
|
+
assert_raise RuntimeError do
|
63
|
+
contents.package
|
64
|
+
end
|
65
|
+
assert_equal( %w{ SUNWcsr SUNWcsd }, contents.packages.map(&:to_s) )
|
66
|
+
assert_equal( nil, contents.size )
|
67
|
+
assert_equal( nil, contents.sum )
|
68
|
+
assert_equal( line, contents.to_s )
|
69
|
+
assert( contents.valid? )
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_editable
|
73
|
+
line = '/etc/passwd e passwd 0644 root sys 580 48299 1077177419 SUNWcsr'
|
74
|
+
contents = Solaris::Contents.from_line( line )
|
75
|
+
assert_equal( :e, contents.ftype )
|
76
|
+
assert_equal( '/etc/passwd', contents.path )
|
77
|
+
assert_equal( nil, contents.rpath )
|
78
|
+
assert_equal( 'passwd', contents.install_class )
|
79
|
+
assert_equal( 420, contents.mode )
|
80
|
+
assert_equal( nil, contents.major )
|
81
|
+
assert_equal( nil, contents.minor )
|
82
|
+
assert_equal( 1077177419, contents.mtime )
|
83
|
+
assert_equal( 'root', contents.owner )
|
84
|
+
assert_equal( 'sys', contents.group )
|
85
|
+
assert_equal( %w{ SUNWcsr }, contents.packages.map(&:to_s) )
|
86
|
+
assert_equal( 'SUNWcsr', contents.package.to_s )
|
87
|
+
assert_equal( 580, contents.size )
|
88
|
+
assert_equal( 48299, contents.sum )
|
89
|
+
assert_equal( line, contents.to_s )
|
90
|
+
assert( contents.valid? )
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_file
|
94
|
+
line = '/boot/grub/bin/grub f none 0555 root sys 378124 54144 1281112186 SUNWgrub'
|
95
|
+
contents = Solaris::Contents.from_line( line )
|
96
|
+
assert_equal( :f, contents.ftype )
|
97
|
+
assert_equal( '/boot/grub/bin/grub', contents.path )
|
98
|
+
assert_equal( nil, contents.rpath )
|
99
|
+
assert_equal( 'none', contents.install_class )
|
100
|
+
assert_equal( 365, contents.mode )
|
101
|
+
assert_equal( nil, contents.major )
|
102
|
+
assert_equal( nil, contents.minor )
|
103
|
+
assert_equal( 1281112186, contents.mtime )
|
104
|
+
assert_equal( 'root', contents.owner )
|
105
|
+
assert_equal( 'sys', contents.group )
|
106
|
+
assert_equal( %w{ SUNWgrub }, contents.packages.map(&:to_s) )
|
107
|
+
assert_equal( 'SUNWgrub', contents.package.to_s )
|
108
|
+
assert_equal( 378124, contents.size )
|
109
|
+
assert_equal( 54144, contents.sum )
|
110
|
+
assert_equal( line, contents.to_s )
|
111
|
+
assert( contents.valid? )
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_link
|
115
|
+
line = '/etc/crypto/certs/SUNWObjectCA=../../../etc/certs/SUNWObjectCA l none SUNWcsr'
|
116
|
+
contents = Solaris::Contents.from_line( line )
|
117
|
+
assert_equal( :l, contents.ftype )
|
118
|
+
assert_equal( '/etc/crypto/certs/SUNWObjectCA', contents.path )
|
119
|
+
assert_equal( '../../../etc/certs/SUNWObjectCA', contents.rpath )
|
120
|
+
assert_equal( 'none', contents.install_class )
|
121
|
+
assert_equal( nil, contents.mode )
|
122
|
+
assert_equal( nil, contents.major )
|
123
|
+
assert_equal( nil, contents.minor )
|
124
|
+
assert_equal( nil, contents.mtime )
|
125
|
+
assert_equal( nil, contents.owner )
|
126
|
+
assert_equal( nil, contents.group )
|
127
|
+
assert_equal( 'SUNWcsr', contents.package.to_s )
|
128
|
+
assert_equal( %w{ SUNWcsr }, contents.packages.map(&:to_s) )
|
129
|
+
assert_equal( nil, contents.size )
|
130
|
+
assert_equal( nil, contents.sum )
|
131
|
+
assert_equal( line, contents.to_s )
|
132
|
+
assert( contents.valid? )
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_symlink
|
136
|
+
line = '/bin=./usr/bin s none SUNWcsr'
|
137
|
+
contents = Solaris::Contents.from_line( line )
|
138
|
+
assert_equal( :s, contents.ftype )
|
139
|
+
assert_equal( '/bin', contents.path )
|
140
|
+
assert_equal( './usr/bin', contents.rpath )
|
141
|
+
assert_equal( 'none', contents.install_class )
|
142
|
+
assert_equal( nil, contents.mode )
|
143
|
+
assert_equal( nil, contents.major )
|
144
|
+
assert_equal( nil, contents.minor )
|
145
|
+
assert_equal( nil, contents.mtime )
|
146
|
+
assert_equal( nil, contents.owner )
|
147
|
+
assert_equal( nil, contents.group )
|
148
|
+
assert_equal( %w{ SUNWcsr }, contents.packages.map(&:to_s) )
|
149
|
+
assert_equal( nil, contents.size )
|
150
|
+
assert_equal( nil, contents.sum )
|
151
|
+
assert_equal( line, contents.to_s )
|
152
|
+
assert( contents.valid? )
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_volatile
|
156
|
+
line = '/boot/x86.miniroot-safe v failsafe 0644 root sys 65 5585 1279140915 SUNWcsd'
|
157
|
+
contents = Solaris::Contents.from_line( line )
|
158
|
+
assert_equal( :v, contents.ftype )
|
159
|
+
assert_equal( '/boot/x86.miniroot-safe', contents.path )
|
160
|
+
assert_equal( nil, contents.rpath )
|
161
|
+
assert_equal( 'failsafe', contents.install_class )
|
162
|
+
assert_equal( 420, contents.mode )
|
163
|
+
assert_equal( nil, contents.major )
|
164
|
+
assert_equal( nil, contents.minor )
|
165
|
+
assert_equal( 1279140915, contents.mtime )
|
166
|
+
assert_equal( 'root', contents.owner )
|
167
|
+
assert_equal( 'sys', contents.group )
|
168
|
+
assert_equal( 'SUNWcsd', contents.package.to_s )
|
169
|
+
assert_equal( %w{ SUNWcsd }, contents.packages.map(&:to_s) )
|
170
|
+
assert_equal( 65, contents.size )
|
171
|
+
assert_equal( 5585, contents.sum )
|
172
|
+
assert_equal( line, contents.to_s )
|
173
|
+
assert( contents.valid? )
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_exclusive
|
177
|
+
skip 'No exclusive example found'
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_unknown_ftype
|
181
|
+
line = '/boot/grub/bin/grub Z none 0555 root sys 378124 54144 1281112186 SUNWgrub'
|
182
|
+
assert_raise ArgumentError do
|
183
|
+
Solaris::Contents.from_line( line )
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_unparseable_line
|
188
|
+
line = '/boot/grub/bin/grub f nonsense'
|
189
|
+
assert_raise ArgumentError do
|
190
|
+
Solaris::Contents.from_line( line )
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_valid
|
195
|
+
line = '/boot/grub/bin/grub f none 0555 root sys 378124 54144 1281112186 SUNWgrub'
|
196
|
+
proto = Solaris::Contents.from_line( line )
|
197
|
+
assert( proto.valid? )
|
198
|
+
proto.ftype = :invalid
|
199
|
+
assert( ! proto.valid? )
|
200
|
+
proto.ftype = :f
|
201
|
+
assert( proto.valid? )
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
data/test/test_pkg.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require 'solaris/contents'
|
5
|
+
|
6
|
+
# Unit tests for Solaris::Contents::Pkg.
|
7
|
+
class TestPkg < Test::Unit::TestCase #:nodoc:
|
8
|
+
|
9
|
+
def test_inst_rdy
|
10
|
+
pkg = Solaris::Contents::Pkg.new( '+mypackage' )
|
11
|
+
assert_equal( :inst_rdy, pkg.status )
|
12
|
+
assert_equal( 'mypackage', pkg.name )
|
13
|
+
assert_equal( '+mypackage', pkg.to_s )
|
14
|
+
assert( pkg.inst_rdy? )
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_rm_rdy
|
18
|
+
pkg = Solaris::Contents::Pkg.new( '-mypackage' )
|
19
|
+
assert_equal( :rm_rdy, pkg.status )
|
20
|
+
assert_equal( 'mypackage', pkg.name )
|
21
|
+
assert_equal( '-mypackage', pkg.to_s )
|
22
|
+
assert( pkg.rm_rdy? )
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_not_fnd
|
26
|
+
pkg = Solaris::Contents::Pkg.new( '!mypackage' )
|
27
|
+
assert_equal( :not_fnd, pkg.status )
|
28
|
+
assert_equal( 'mypackage', pkg.name )
|
29
|
+
assert_equal( '!mypackage', pkg.to_s )
|
30
|
+
assert( pkg.not_fnd? )
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_served_file
|
34
|
+
pkg = Solaris::Contents::Pkg.new( '%mypackage' )
|
35
|
+
assert_equal( :served_file, pkg.status )
|
36
|
+
assert_equal( 'mypackage', pkg.name )
|
37
|
+
assert_equal( '%mypackage', pkg.to_s )
|
38
|
+
assert( pkg.served_file? )
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_stat_next
|
42
|
+
pkg = Solaris::Contents::Pkg.new( '@mypackage' )
|
43
|
+
assert_equal( :stat_next, pkg.status )
|
44
|
+
assert_equal( 'mypackage', pkg.name )
|
45
|
+
assert_equal( '@mypackage', pkg.to_s )
|
46
|
+
assert( pkg.stat_next? )
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_dup_entry
|
50
|
+
pkg = Solaris::Contents::Pkg.new( '#mypackage' )
|
51
|
+
assert_equal( :dup_entry, pkg.status )
|
52
|
+
assert_equal( 'mypackage', pkg.name )
|
53
|
+
assert_equal( '#mypackage', pkg.to_s )
|
54
|
+
assert( pkg.dup_entry? )
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_confirm_cont
|
58
|
+
pkg = Solaris::Contents::Pkg.new( '*mypackage' )
|
59
|
+
assert_equal( :confirm_cont, pkg.status )
|
60
|
+
assert_equal( 'mypackage', pkg.name )
|
61
|
+
assert_equal( '*mypackage', pkg.to_s )
|
62
|
+
assert( pkg.confirm_cont? )
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_confirm_attr
|
66
|
+
pkg = Solaris::Contents::Pkg.new( '~mypackage' )
|
67
|
+
assert_equal( :confirm_attr, pkg.status )
|
68
|
+
assert_equal( 'mypackage', pkg.name )
|
69
|
+
assert_equal( '~mypackage', pkg.to_s )
|
70
|
+
assert( pkg.confirm_attr? )
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_entry_ok
|
74
|
+
pkg = Solaris::Contents::Pkg.new( 'mypackage' )
|
75
|
+
assert_equal( :entry_ok, pkg.status )
|
76
|
+
assert_equal( 'mypackage', pkg.name )
|
77
|
+
assert_equal( 'mypackage', pkg.to_s )
|
78
|
+
assert( pkg.entry_ok? )
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require 'solaris'
|
5
|
+
|
6
|
+
# Unit tests for top level require.
|
7
|
+
class TestSolaris < Test::Unit::TestCase #:nodoc:
|
8
|
+
|
9
|
+
def test_solaris
|
10
|
+
assert_nothing_raised { Solaris }
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_solaris_contents
|
14
|
+
assert_nothing_raised { Solaris::Contents }
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_solaris_contents
|
18
|
+
assert_nothing_raised { Solaris::Contents::Pkg }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: solaris-contents
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Martin Carpenter
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-15 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Parse and write Solaris package contents records
|
15
|
+
email: mcarpenter@free.fr
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files:
|
19
|
+
- LICENSE
|
20
|
+
- Rakefile
|
21
|
+
- README.rdoc
|
22
|
+
files:
|
23
|
+
- lib/solaris/contents.rb
|
24
|
+
- lib/solaris/contents/pkg.rb
|
25
|
+
- lib/solaris.rb
|
26
|
+
- test/test_pkg.rb
|
27
|
+
- test/test_contents.rb
|
28
|
+
- test/test_solaris.rb
|
29
|
+
- LICENSE
|
30
|
+
- Rakefile
|
31
|
+
- README.rdoc
|
32
|
+
homepage: http://mcarpenter.org/projects/solaris-contents
|
33
|
+
licenses:
|
34
|
+
- BSD
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
requirements: []
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.8.10
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: Parse and write Solaris package contents records
|
57
|
+
test_files:
|
58
|
+
- test/test_pkg.rb
|
59
|
+
- test/test_contents.rb
|
60
|
+
- test/test_solaris.rb
|