solaris-contents 1.0.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.
- 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
|