rpm2 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/workflows/ci.yaml +25 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +111 -0
- data/.whitesource +3 -0
- data/CHANGELOG.md +66 -0
- data/Gemfile +9 -0
- data/MIT-LICENSE +23 -0
- data/README.md +374 -0
- data/Rakefile +58 -0
- data/_docker/Dockerfile.ubi8 +8 -0
- data/_docker/Dockerfile.ubi9 +8 -0
- data/lib/rpm/c/header.rb +36 -0
- data/lib/rpm/c/rpmcallback.rb +27 -0
- data/lib/rpm/c/rpmcli.rb +6 -0
- data/lib/rpm/c/rpmdb.rb +20 -0
- data/lib/rpm/c/rpmds.rb +43 -0
- data/lib/rpm/c/rpmfi.rb +31 -0
- data/lib/rpm/c/rpmio.rb +17 -0
- data/lib/rpm/c/rpmlib.rb +12 -0
- data/lib/rpm/c/rpmlog.rb +23 -0
- data/lib/rpm/c/rpmmacro.rb +33 -0
- data/lib/rpm/c/rpmprob.rb +53 -0
- data/lib/rpm/c/rpmps.rb +11 -0
- data/lib/rpm/c/rpmtag.rb +304 -0
- data/lib/rpm/c/rpmtd.rb +34 -0
- data/lib/rpm/c/rpmts.rb +69 -0
- data/lib/rpm/c/rpmtypes.rb +28 -0
- data/lib/rpm/c.rb +49 -0
- data/lib/rpm/compat.rb +40 -0
- data/lib/rpm/db.rb +117 -0
- data/lib/rpm/dependency.rb +120 -0
- data/lib/rpm/file.rb +134 -0
- data/lib/rpm/gem_version.rb +7 -0
- data/lib/rpm/match_iterator.rb +66 -0
- data/lib/rpm/package.rb +332 -0
- data/lib/rpm/problem.rb +65 -0
- data/lib/rpm/transaction.rb +268 -0
- data/lib/rpm/utils.rb +7 -0
- data/lib/rpm/version.rb +146 -0
- data/lib/rpm.rb +88 -0
- data/lib/rpm2.rb +1 -0
- data/rpm2.gemspec +29 -0
- data/test/data/a.spec +49 -0
- data/test/data/simple-1.0-0.i586.rpm +0 -0
- data/test/data/simple.spec +38 -0
- data/test/data/simple_with_deps-1.0-0.i586.rpm +0 -0
- data/test/data/simple_with_deps.spec +41 -0
- data/test/helper.rb +7 -0
- data/test/test_dependency.rb +28 -0
- data/test/test_file.rb +36 -0
- data/test/test_lib.rb +33 -0
- data/test/test_package.rb +76 -0
- data/test/test_problem.rb +18 -0
- data/test/test_rpm.rb +33 -0
- data/test/test_transaction.rb +156 -0
- data/test/test_version.rb +64 -0
- metadata +133 -0
data/lib/rpm/package.rb
ADDED
@@ -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
|
data/lib/rpm/problem.rb
ADDED
@@ -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
|