rpm2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/workflows/ci.yaml +25 -0
  4. data/.gitignore +8 -0
  5. data/.rubocop.yml +1 -0
  6. data/.rubocop_todo.yml +111 -0
  7. data/.whitesource +3 -0
  8. data/CHANGELOG.md +66 -0
  9. data/Gemfile +9 -0
  10. data/MIT-LICENSE +23 -0
  11. data/README.md +374 -0
  12. data/Rakefile +58 -0
  13. data/_docker/Dockerfile.ubi8 +8 -0
  14. data/_docker/Dockerfile.ubi9 +8 -0
  15. data/lib/rpm/c/header.rb +36 -0
  16. data/lib/rpm/c/rpmcallback.rb +27 -0
  17. data/lib/rpm/c/rpmcli.rb +6 -0
  18. data/lib/rpm/c/rpmdb.rb +20 -0
  19. data/lib/rpm/c/rpmds.rb +43 -0
  20. data/lib/rpm/c/rpmfi.rb +31 -0
  21. data/lib/rpm/c/rpmio.rb +17 -0
  22. data/lib/rpm/c/rpmlib.rb +12 -0
  23. data/lib/rpm/c/rpmlog.rb +23 -0
  24. data/lib/rpm/c/rpmmacro.rb +33 -0
  25. data/lib/rpm/c/rpmprob.rb +53 -0
  26. data/lib/rpm/c/rpmps.rb +11 -0
  27. data/lib/rpm/c/rpmtag.rb +304 -0
  28. data/lib/rpm/c/rpmtd.rb +34 -0
  29. data/lib/rpm/c/rpmts.rb +69 -0
  30. data/lib/rpm/c/rpmtypes.rb +28 -0
  31. data/lib/rpm/c.rb +49 -0
  32. data/lib/rpm/compat.rb +40 -0
  33. data/lib/rpm/db.rb +117 -0
  34. data/lib/rpm/dependency.rb +120 -0
  35. data/lib/rpm/file.rb +134 -0
  36. data/lib/rpm/gem_version.rb +7 -0
  37. data/lib/rpm/match_iterator.rb +66 -0
  38. data/lib/rpm/package.rb +332 -0
  39. data/lib/rpm/problem.rb +65 -0
  40. data/lib/rpm/transaction.rb +268 -0
  41. data/lib/rpm/utils.rb +7 -0
  42. data/lib/rpm/version.rb +146 -0
  43. data/lib/rpm.rb +88 -0
  44. data/lib/rpm2.rb +1 -0
  45. data/rpm2.gemspec +29 -0
  46. data/test/data/a.spec +49 -0
  47. data/test/data/simple-1.0-0.i586.rpm +0 -0
  48. data/test/data/simple.spec +38 -0
  49. data/test/data/simple_with_deps-1.0-0.i586.rpm +0 -0
  50. data/test/data/simple_with_deps.spec +41 -0
  51. data/test/helper.rb +7 -0
  52. data/test/test_dependency.rb +28 -0
  53. data/test/test_file.rb +36 -0
  54. data/test/test_lib.rb +33 -0
  55. data/test/test_package.rb +76 -0
  56. data/test/test_problem.rb +18 -0
  57. data/test/test_rpm.rb +33 -0
  58. data/test/test_transaction.rb +156 -0
  59. data/test/test_version.rb +64 -0
  60. metadata +133 -0
@@ -0,0 +1,332 @@
1
+
2
+ module RPM
3
+ class ChangeLog
4
+ attr_accessor :time, :name, :text
5
+ end
6
+
7
+ class Package
8
+ # Create a new package object from data
9
+ # @param [String] str Header data
10
+ # @return [Package]
11
+ def load(_data)
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def self.create(name, version)
16
+ unless name.is_a?(String)
17
+ raise TypeError, 'illegal argument type: name should be String'
18
+ end
19
+ unless version.is_a?(RPM::Version)
20
+ raise TypeError, 'illegal argument type: version should be RPM::Version'
21
+ end
22
+ hdr = RPM::C.headerNew
23
+ if RPM::C.headerPutString(hdr, :name, name) != 1
24
+ raise "Can't set package name: #{name}"
25
+ end
26
+ if RPM::C.headerPutString(hdr, :version, version.v) != 1
27
+ raise "Can't set package version: #{version.v}"
28
+ end
29
+ if version.e
30
+ if RPM::C.headerPutUint32(hdr, :epoch, version.e) != 1
31
+ raise "Can't set package epoch: #{version.e}"
32
+ end
33
+ end
34
+ Package.new(hdr)
35
+ end
36
+
37
+ # Add a dependency to the package header
38
+ # @param [Dependency] dep Dependency to add
39
+ def add_dependency(dep)
40
+ unless dep.is_a?(Dependency)
41
+ raise TypeError, 'illegal argument type: must be a Dependency'
42
+ end
43
+
44
+ raise NotImplementedError
45
+ end
46
+
47
+ # Add a int32 value to the package header
48
+ # @param [Number] tag Tag
49
+ # @param [Number] val Value
50
+ def add_int32(_tag, _val)
51
+ raise NotImplementedError
52
+ end
53
+
54
+ # Add a list of strings to the package header
55
+ # @param [Number] tag Tag
56
+ # @param [Array<String>] val Strings to add
57
+ def add_string_array(_tag, _val)
58
+ raise NotImplementedError
59
+ end
60
+
61
+ # Add a binary value to the package header
62
+ # @param [Number] tag Tag
63
+ # @param [String] val String to add
64
+ def add_string(_tag, _val)
65
+ raise NotImplementedError
66
+ end
67
+
68
+ # Add a binary value to the package header
69
+ # @param [Number] tag Tag
70
+ # @param [String] val Value
71
+ def add_binary(_tag, _val)
72
+ raise NotImplementedError
73
+ end
74
+
75
+ # Deletes a tag of the package header
76
+ # @param [Number] tag Tag
77
+ def delete_tag(_tag)
78
+ raise NotImplementedError
79
+ end
80
+
81
+ # @return a formated string
82
+ # @example
83
+ # pkg.sprintf("%{name}") => "apache2"
84
+ def sprintf(fmt)
85
+ error = ::FFI::MemoryPointer.new(:pointer, 1)
86
+ val = RPM::C.headerFormat(@hdr, fmt, error)
87
+ raise error.get_pointer(0).read_string if val.null?
88
+ val.read_string
89
+ end
90
+
91
+ # @return [Number] This package signature
92
+ def signature
93
+ sprintf('%{sigmd5}')
94
+ end
95
+
96
+ # @return [Array<RPM::File>] File list for this package
97
+ def files
98
+ basenames = self[:basenames]
99
+
100
+ return [] if basenames.nil?
101
+
102
+ dirnames = self[:dirnames]
103
+ diridxs = self[:dirindexes]
104
+ statelist = self[:filestates]
105
+ flaglist = self[:fileflags]
106
+ sizelist = self[:filesizes]
107
+ modelist = self[:filemodes]
108
+ mtimelist = self[:filemtimes]
109
+ rdevlist = self[:filerdevs]
110
+ linklist = self[:filelinktos]
111
+ md5list = self[:filemd5s]
112
+ ownerlist = self[:fileusername]
113
+ grouplist = self[:filegroupname]
114
+
115
+ ret = []
116
+
117
+ basenames.each_with_index do |_basename, i|
118
+ file = RPM::File.new("#{dirnames[diridxs[i]]}#{basenames[i]}",
119
+ md5list[i],
120
+ linklist[i],
121
+ sizelist[i],
122
+ mtimelist[i],
123
+ ownerlist[i],
124
+ grouplist[i],
125
+ rdevlist[i],
126
+ modelist[i],
127
+ flaglist.nil? ? RPM::C::FileAttrs[:none] : flaglist[i],
128
+ statelist.nil? ? RPM::C::FileState[:normal] : statelist[i])
129
+ ret << file
130
+ end
131
+ ret
132
+ end
133
+
134
+ # @return [Array<RPM::Dependency>] Dependencies for +klass+
135
+ # @example
136
+ # dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
137
+ #
138
+ # @visibility private
139
+ def dependencies(klass, nametag, versiontag, flagtag)
140
+ deps = []
141
+
142
+ nametd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
143
+ versiontd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
144
+ flagtd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
145
+
146
+ min = RPM::C::HEADERGET_MINMEM
147
+ return deps if RPM::C.headerGet(@hdr, nametag, nametd, min) != 1
148
+ return deps if RPM::C.headerGet(@hdr, versiontag, versiontd, min) != 1
149
+ return deps if RPM::C.headerGet(@hdr, flagtag, flagtd, min) != 1
150
+
151
+ RPM::C.rpmtdInit(nametd)
152
+ while RPM::C.rpmtdNext(nametd) != -1
153
+ deps << klass.new(RPM::C.rpmtdGetString(nametd),
154
+ RPM::Version.new(RPM::C.rpmtdNextString(versiontd)),
155
+ RPM::C.rpmtdNextUint32(flagtd).read_uint, self)
156
+ end
157
+ deps
158
+ end
159
+
160
+ # @return [Array<RPM::Provide>] Provides list for this package
161
+ def provides
162
+ dependencies(RPM::Provide, :providename, :provideversion, :provideflags)
163
+ end
164
+
165
+ # @return [Array<RPM::Require>] Requires list for this package
166
+ def requires
167
+ dependencies(RPM::Require, :requirename, :requireversion, :requireflags)
168
+ end
169
+
170
+ # @return [Array<RPM::Conflicts>] Conflicts list for this package
171
+ def conflicts
172
+ dependencies(RPM::Conflict, :conflictname, :conflictversion, :conflictflags)
173
+ end
174
+
175
+ # @return [Array<RPM::Obsolete>] Obsoletes list for this package
176
+ def obsoletes
177
+ dependencies(RPM::Obsolete, :obsoletename, :obsoleteversion, :obsoleteflags)
178
+ end
179
+
180
+ # @return [Array<RPM::Changelog>] changelog of the package as an array
181
+ def changelog
182
+ entries = []
183
+ nametd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
184
+ timetd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
185
+ texttd = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
186
+
187
+ min = RPM::C::HEADERGET_MINMEM
188
+ return deps if RPM::C.headerGet(@hdr, :changelogtime, timetd, min) != 1
189
+ return deps if RPM::C.headerGet(@hdr, :changelogname, nametd, min) != 1
190
+ return deps if RPM::C.headerGet(@hdr, :changelogtext, texttd, min) != 1
191
+
192
+ RPM::C.rpmtdInit(timetd)
193
+ while RPM::C.rpmtdNext(timetd) != -1
194
+ entry = RPM::ChangeLog.new
195
+ entry.time = RPM::C.rpmtdGetUint32(timetd)
196
+ entry.name = RPM::C.rpmtdNextString(nametd)
197
+ entry.text = RPM::C.rpmtdNextString(texttd)
198
+ entries << entry
199
+ end
200
+ entries
201
+ end
202
+
203
+ # Access a header entry
204
+ # @param [Number] tag Tag to return
205
+ # @return [] Value of the entry
206
+ # @example
207
+ # pkg => #<RPM::Package name="xmlgraphics-fop", version=#<RPM::Version v="1.0", r="22.4">>
208
+ # pkg[:name] => "xmlgraphics-fop"
209
+ #
210
+ # or if you have the old ruby-rpm compat loaded
211
+ #
212
+ # require 'rpm/compat'
213
+ # pkg[RPM::TAG_NAME] => "xmlgraphics-fop"
214
+ #
215
+ # @return [String, Fixnum, Array<String>, Array<Fixnum>, nil]
216
+ # The value of the entry
217
+ def [](tag)
218
+ tagc = ::FFI::AutoPointer.new(RPM::C.rpmtdNew, Package.method(:release_td))
219
+
220
+ return nil if RPM::C.headerGet(ptr, tag, tagc,
221
+ RPM::C::HEADERGET_MINMEM) == 0
222
+
223
+ type = RPM::C.rpmtdType(tagc)
224
+ count = RPM::C.rpmtdCount(tagc)
225
+ ret_type = RPM::C.rpmTagGetReturnType(tag)
226
+
227
+ method_name = case type
228
+ when :int8_type, :char_type, :int16_type, :int32_type, :int64_type then :rpmtdGetNumber
229
+ when :string_type, :string_array_type, :bin_type then :rpmtdGetString
230
+ else raise NotImplementedError, "Don't know how to retrieve type '#{type}'"
231
+ end
232
+
233
+ is_array = if count > 1 then true
234
+ elsif ret_type == :array_return_type then true
235
+ elsif type == :string_array_type then true
236
+ else false
237
+ end
238
+
239
+ if is_array
240
+ ret = []
241
+ RPM::C.rpmtdInit(tagc)
242
+ ret << RPM::C.send(method_name, tagc) while RPM::C.rpmtdNext(tagc) != -1
243
+ return ret
244
+ end
245
+
246
+ RPM::C.send(method_name, tagc)
247
+ end
248
+
249
+ # @return [String] This package name
250
+ def name
251
+ self[:name]
252
+ end
253
+
254
+ # @return [String] This package architecture
255
+ def arch
256
+ self[:arch]
257
+ end
258
+
259
+ # TODO: signature
260
+
261
+ # @return [Version] Version for this package
262
+ def version
263
+ Version.new(self[:version], self[:release], self[:epoch])
264
+ end
265
+
266
+ # String representation of the package: "name-version-release-arch"
267
+ # @return [String]
268
+ def to_s
269
+ return '' if name.nil?
270
+ return name if version.nil?
271
+ return "#{name}-#{version}" if arch.nil?
272
+ "#{name}-#{version}-#{arch}"
273
+ end
274
+
275
+ def self.open(filename)
276
+ Package.new(filename)
277
+ end
278
+
279
+ # @visibility private
280
+ def self.release(ptr)
281
+ RPM::C.headerFree(ptr)
282
+ end
283
+
284
+ # @visibility private
285
+ def self.release_td(ptr)
286
+ RPM::C.rpmtdFree(ptr)
287
+ end
288
+
289
+ # @visibility private
290
+ def initialize(what)
291
+ case what
292
+ when String then initialize_from_filename(what)
293
+ else initialize_from_header(what)
294
+ end
295
+ end
296
+
297
+ # @visibility private
298
+ def initialize_from_header(hdr = nil)
299
+ if hdr.nil?
300
+ @hdr = ::FFI::AutoPointer.new(RPM::C.headerNew, Header.method(:release))
301
+ elsif hdr.is_a?(::FFI::Pointer)
302
+ # ref
303
+ hdr = RPM::C.headerLink(hdr)
304
+ @hdr = ::FFI::AutoPointer.new(hdr, Package.method(:release))
305
+ else
306
+ raise "Can't initialize header with '#{hdr}'"
307
+ end
308
+ end
309
+
310
+ def initialize_from_filename(filename)
311
+ # it sucks not using the std File.open here
312
+ hdr = ::FFI::MemoryPointer.new(:pointer)
313
+ fd = nil
314
+ begin
315
+ fd = RPM::C.Fopen(filename, 'r')
316
+ raise "#{filename} : #{RPM::C.Fstrerror(fd)}" if RPM::C.Ferror(fd) != 0
317
+ RPM.transaction do |ts|
318
+ RPM::C.rpmReadPackageFile(ts.ptr, fd, filename, hdr)
319
+ end
320
+ ensure
321
+ RPM::C.Fclose(fd) unless fd.nil?
322
+ end
323
+ initialize_from_header(hdr.get_pointer(0))
324
+ end
325
+
326
+ # @return [RPM::C::Header] header pointer
327
+ # @visibility private
328
+ def ptr
329
+ @hdr
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,65 @@
1
+
2
+ module RPM
3
+ class Problem
4
+ def self.release(ptr)
5
+ RPM::C.rpmProblemFree(ptr)
6
+ end
7
+
8
+ # Creates a problem from an existing C pointer, refcounting it
9
+ # first.
10
+ # @param [FFI::Pointer] ptr existing C pointer
11
+ # @return [RPM::Problem] wrapped object
12
+ def self.from_ptr(ptr)
13
+ case ptr
14
+ when FFI::Pointer
15
+ new(FFI::AutoPointer.new(RPM::C.rpmProblemLink(ptr), Problem.method(:release)))
16
+ else
17
+ raise "Can't initialize header with '#{ptr}'"
18
+ end
19
+ end
20
+
21
+ # Create a problem item.
22
+ # @param [RPM::ProblemType] type problem type
23
+ # @param [String] pkg_nver name-version-edition-release of the related package
24
+ # @param [String] key key of the related package
25
+ # @param [String] alt_nver name-version-edition-release of the other related package
26
+ # @param [String] str generic data string from a problem
27
+ def self.create(type, pkg_nevr, key, alt_nevr, str, number)
28
+ ptr = ::FFI::AutoPointer.new(RPM::C.rpmProblemCreate(type, pkg_nevr, key, alt_nevr, str, number), Problem.method(:release))
29
+ new(ptr)
30
+ end
31
+
32
+ # @visibility private
33
+ def initialize(ptr)
34
+ @ptr = ptr
35
+ end
36
+
37
+ # @return [RPM::ProblemType] type of problem (dependency, diskpace etc).
38
+ def type
39
+ RPM::C.rpmProblemGetType(@ptr)
40
+ end
41
+
42
+ # @return [String] filename or python object address of a problem.
43
+ def key
44
+ RPM::C.rpmProblemGetKey(@ptr).read_string
45
+ end
46
+
47
+ # @return [String] a generic data string from a problem.
48
+ def str
49
+ RPM::C.rpmProblemGetStr(@ptr)
50
+ end
51
+
52
+ # @return [String] formatted string representation of a problem
53
+ def to_s
54
+ RPM::C.rpmProblemString(@ptr)
55
+ end
56
+
57
+ # @return [Fixnum] compare two problems for equality.
58
+ def <=>(other)
59
+ RPM::C.rpmProblemCompare(@ptr, other.ptr)
60
+ end
61
+
62
+ # @visibility private
63
+ attr_reader :ptr
64
+ end
65
+ end
@@ -0,0 +1,268 @@
1
+
2
+ module RPM
3
+ CallbackData = Struct.new(:type, :key, :package, :amount, :total) do
4
+ def to_s
5
+ "#{type} #{key} #{package} #{amount} #{total}"
6
+ end
7
+ end
8
+
9
+ class Transaction
10
+ def self.release(ptr)
11
+ RPM::C.rpmtsFree(ptr)
12
+ end
13
+
14
+ def initialize(opts = {})
15
+ # http://markmail.org/message/ypsiqxop442p7rzz
16
+ # The key pointer needs to stay valid during commit
17
+ # so we keep a reference to them mapping from
18
+ # object_id to ruby object.
19
+ @keys = {}
20
+ opts[:root] ||= '/'
21
+
22
+ @ptr = ::FFI::AutoPointer.new(RPM::C.rpmtsCreate, Transaction.method(:release))
23
+ RPM::C.rpmtsSetRootDir(@ptr, opts[:root])
24
+ end
25
+
26
+ # @return [RPM::MatchIterator] Creates an iterator for +tag+ and +val+
27
+ def init_iterator(tag, val)
28
+ raise TypeError if val && !val.is_a?(String)
29
+
30
+ it_ptr = RPM::C.rpmtsInitIterator(@ptr, tag.nil? ? 0 : tag, val, 0)
31
+
32
+ raise "Can't init iterator for [#{tag}] -> '#{val}'" if it_ptr.null?
33
+ MatchIterator.from_ptr(it_ptr)
34
+ end
35
+
36
+ # @visibility private
37
+ attr_reader :ptr
38
+
39
+ #
40
+ # @yield [Package] Called for each match
41
+ # @param [Number] key RPM tag key
42
+ # @param [String] val Value to match
43
+ # @example
44
+ # RPM.transaction do |t|
45
+ # t.each_match(RPM::TAG_ARCH, "x86_64") do |pkg|
46
+ # puts pkg.name
47
+ # end
48
+ # end
49
+ #
50
+ def each_match(key, val, &block)
51
+ it = init_iterator(key, val)
52
+
53
+ return it unless block_given?
54
+
55
+ it.each(&block)
56
+ end
57
+
58
+ #
59
+ # @yield [Package] Called for each package in the database
60
+ # @example
61
+ # db.each do |pkg|
62
+ # puts pkg.name
63
+ # end
64
+ #
65
+ def each(&block)
66
+ each_match(0, nil, &block)
67
+ end
68
+
69
+ # Add a install operation to the transaction
70
+ # @param [Package] pkg Package to install
71
+ # @param [String] key e.g. filename where to install from
72
+ def install(pkg, key)
73
+ install_element(pkg, key, upgrade: false)
74
+ end
75
+
76
+ # Add an upgrade operation to the transaction
77
+ # @param [Package] pkg Package to upgrade
78
+ # @param [String] key e.g. filename where to install from
79
+ def upgrade(pkg, key)
80
+ install_element(pkg, key, upgrade: true)
81
+ end
82
+
83
+ # Add a delete operation to the transaction
84
+ # @param [String, Package, Dependency] pkg Package to delete
85
+ def delete(pkg)
86
+ iterator = case pkg
87
+ when Package
88
+ pkg[:sigmd5] ? each_match(:sigmd5, pkg[:sigmd5]) : each_match(:label, pkg[:label])
89
+ when String
90
+ each_match(:label, pkg)
91
+ when Dependency
92
+ each_match(:label, pkg.name).set_iterator_version(pkg.version)
93
+ else
94
+ raise TypeError, 'illegal argument type'
95
+ end
96
+
97
+ iterator.each do |header|
98
+ ret = RPM::C.rpmtsAddEraseElement(@ptr, header.ptr, iterator.offset)
99
+ raise "Error while adding erase/#{pkg} to transaction" if ret != 0
100
+ end
101
+ end
102
+
103
+ # Sets the root directory for this transaction
104
+ # @param [String] root directory
105
+ def root_dir=(dir)
106
+ rc = RPM::C.rpmtsSetRootDir(@ptr, dir)
107
+ raise "Can't set #{dir} as root directory" if rc < 0
108
+ end
109
+
110
+ # @return [String ] the root directory for this transaction
111
+ def root_dir
112
+ RPM::C.rpmtsRootDir(@ptr)
113
+ end
114
+
115
+ def flags=(fl)
116
+ RPM::C.rpmtsSetFlags(@ptr, fl)
117
+ end
118
+
119
+ def flags
120
+ RPM::C.rpmtsFlags(@ptr)
121
+ end
122
+
123
+ # Determine package order in the transaction according to dependencies
124
+ #
125
+ # The final order ends up as installed packages followed by removed
126
+ # packages, with packages removed for upgrades immediately following
127
+ # the new package to be installed.
128
+ #
129
+ # @returns [Fixnum] no. of (added) packages that could not be ordered
130
+ def order
131
+ RPM::C.rpmtsOrder(@ptr)
132
+ end
133
+
134
+ # Free memory needed only for dependency checks and ordering.
135
+ def clean
136
+ RPM::C.rpmtsClean(@ptr)
137
+ end
138
+
139
+ def check
140
+ rc = RPM::C.rpmtsCheck(@ptr)
141
+ probs = RPM::C.rpmtsProblems(@ptr)
142
+
143
+ return if rc < 0
144
+ begin
145
+ psi = RPM::C.rpmpsInitIterator(probs)
146
+ while RPM::C.rpmpsNextIterator(psi) >= 0
147
+ problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
148
+ yield problem
149
+ end
150
+ ensure
151
+ RPM::C.rpmpsFree(probs)
152
+ end
153
+ end
154
+
155
+ # Performs the transaction.
156
+ # @param [Number] flag Transaction flags, default +RPM::TRANS_FLAG_NONE+
157
+ # @param [Number] filter Transaction filter, default +RPM::PROB_FILTER_NONE+
158
+ # @example
159
+ # transaction.commit
160
+ # You can supply your own callback
161
+ # @example
162
+ # transaction.commit do |data|
163
+ # end
164
+ # end
165
+ # @yield [CallbackData] sig Transaction progress
166
+ def commit
167
+ callback = proc do |hdr, type, amount, total, key_ptr, data_ignored|
168
+ key_id = key_ptr.address
169
+ key = @keys.include?(key_id) ? @keys[key_id] : nil
170
+
171
+ if block_given?
172
+ package = hdr.null? ? nil : Package.new(hdr)
173
+ data = CallbackData.new(type, key, package, amount, total)
174
+ yield(data)
175
+ else
176
+ RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
177
+ end
178
+ end
179
+ # We create a callback to pass to the C method and we
180
+ # call the user supplied callback from there
181
+ #
182
+ # The C callback expects you to return a file handle,
183
+ # We expect from the user to get a File, which we
184
+ # then convert to a file handle to return.
185
+ callback = proc do |hdr, type, amount, total, key_ptr, data_ignored|
186
+ key_id = key_ptr.address
187
+ key = @keys.include?(key_id) ? @keys[key_id] : nil
188
+
189
+ if block_given?
190
+ package = hdr.null? ? nil : Package.new(hdr)
191
+ data = CallbackData.new(type, key, package, amount, total)
192
+ ret = yield(data)
193
+
194
+ # For OPEN_FILE we need to do some type conversion
195
+ # for certain callback types we need to do some
196
+ case type
197
+ when :inst_open_file
198
+ # For :inst_open_file the user callback has to
199
+ # return the open file
200
+ unless ret.is_a?(::File)
201
+ raise TypeError, "illegal return value type #{ret.class}. Expected File."
202
+ end
203
+ fdt = RPM::C.fdDup(ret.to_i)
204
+ if fdt.null? || RPM::C.Ferror(fdt) != 0
205
+ raise "Can't use opened file #{data.key}: #{RPM::C.Fstrerror(fdt)}"
206
+ RPM::C.Fclose(fdt) unless fdt.nil?
207
+ else
208
+ fdt = RPM::C.fdLink(fdt)
209
+ @fdt = fdt
210
+ end
211
+ # return the (RPM type) file handle
212
+ fdt
213
+ when :inst_close_file
214
+ fdt = @fdt
215
+ RPM::C.Fclose(fdt)
216
+ @fdt = nil
217
+ else
218
+ ret
219
+ end
220
+ else
221
+ # No custom callback given, use the default to show progress
222
+ RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
223
+ end
224
+ end
225
+
226
+ rc = RPM::C.rpmtsSetNotifyCallback(@ptr, callback, nil)
227
+ raise "Can't set commit callback" if rc != 0
228
+
229
+ rc = RPM::C.rpmtsRun(@ptr, nil, :none)
230
+
231
+ raise "#{self}: #{RPM::C.rpmlogMessage}" if rc < 0
232
+
233
+ if rc > 0
234
+ ps = RPM::C.rpmtsProblems(@ptr)
235
+ psi = RPM::C.rpmpsInitIterator(ps)
236
+ while RPM::C.rpmpsNextIterator(psi) >= 0
237
+ problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
238
+ STDERR.puts problem
239
+ end
240
+ RPM::C.rpmpsFree(ps)
241
+ end
242
+ end
243
+
244
+ # @return [DB] the database associated with this transaction
245
+ def db
246
+ RPM::DB.new(self)
247
+ end
248
+
249
+ private
250
+
251
+ # @param [Package] pkg package to install
252
+ # @param [String] key e.g. filename where to install from
253
+ # @param opts options
254
+ # @option :upgrade Upgrade packages if true
255
+ def install_element(pkg, key, opts = {})
256
+ raise TypeError, 'illegal argument type' unless pkg.is_a?(RPM::Package)
257
+ raise ArgumentError, "#{self}: key '#{key}' must be unique" if @keys.include?(key.object_id)
258
+
259
+ # keep a reference to the key as rpmtsAddInstallElement will keep a copy
260
+ # of the passed pointer (we pass the object_id)
261
+ @keys[key.object_id] = key
262
+
263
+ ret = RPM::C.rpmtsAddInstallElement(@ptr, pkg.ptr, FFI::Pointer.new(key.object_id), opts[:upgrade] ? 1 : 0, nil)
264
+ raise RuntimeError if ret != 0
265
+ nil
266
+ end
267
+ end
268
+ end
data/lib/rpm/utils.rb ADDED
@@ -0,0 +1,7 @@
1
+ module RPM
2
+ module Utils
3
+ def self.check_type(var, type)
4
+ raise(TypeError, "wrong argument type #{var.class} (expected #{type.class})") unless var.is_a?(type)
5
+ end
6
+ end
7
+ end