rpm2 0.1.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.
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