solaris-contents 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,8 @@
1
+
2
+ # Namespace for child classes.
3
+ module Solaris
4
+
5
+ require 'solaris/contents'
6
+
7
+ end # Solaris
8
+
@@ -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