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 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