#!/usr/bin/ruby
require 'solv'
require 'rubygems'
require 'inifile'
require 'tempfile'
class Repo_generic
def initialize(name, type, attribs = {})
@name = name
@type = type
@attribs = attribs.dup
@incomplete = false
end
def enabled?
return @attribs['enabled'].to_i != 0
end
def autorefresh?
return @attribs['autorefresh'].to_i != 0
end
def id
return @handle ? @handle.id : 0
end
def calc_cookie_fp(f)
chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
chksum.add("1.1")
chksum.add_fp(f)
return chksum.raw
end
def calc_cookie_file(filename)
chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
chksum.add("1.1")
chksum.add_stat(filename)
return chksum.raw
end
def calc_cookie_ext(f, cookie)
chksum = Solv::Chksum.new(Solv::REPOKEY_TYPE_SHA256)
chksum.add("1.1")
chksum.add(cookie)
chksum.add_fstat(f.fileno)
return chksum.raw()
end
def cachepath(ext = nil)
path = @name.sub(/^\./, '_')
path += ext ? "_#{ext}.solvx" : '.solv'
return '/var/cache/solv/' + path.gsub(/\//, '_')
end
def load(pool)
@handle = pool.add_repo(@name)
@handle.appdata = self
@handle.priority = 99 - @attribs['priority'].to_i if @attribs['priority']
dorefresh = autorefresh?
if dorefresh
begin
s = File.stat(cachepath)
dorefresh = false if s && (@attribs['metadata_expire'].to_i == -1 || Time.now - s.mtime < @attribs['metadata_expire'].to_i)
rescue SystemCallError
end
end
@cookie = nil
@extcookie = nil
if !dorefresh && usecachedrepo(nil)
puts "repo: '#{@name}' cached"
return true
end
return false
end
def load_ext(repodata)
return false
end
def download(file, uncompress, chksum, markincomplete = false)
url = @attribs['baseurl']
if !url
puts "%{@name}: no baseurl"
return nil
end
url = url.sub(/\/$/, '') + "/#{file}"
f = Tempfile.new('rbsolv')
f.unlink
st = system('curl', '-f', '-s', '-L', '-o', "/dev/fd/" + f.fileno.to_s, '--', url)
return nil if f.stat.size == 0 && (st || !chksum)
if !st
puts "#{file}: download error #{$? >> 8}"
@incomplete = true if markincomplete
return nil
end
if chksum
fchksum = Solv::Chksum.new(chksum.type)
fchksum.add_fd(f.fileno)
if !fchksum == chksum
puts "#{file}: checksum error"
@incomplete = true if markincomplete
return nil
end
end
rf = nil
if uncompress
rf = Solv::xfopen_fd(file, f.fileno)
else
rf = Solv::xfopen_fd('', f.fileno)
end
f.close
return rf
end
def usecachedrepo(ext, mark = false)
cookie = ext ? @extcookie : @cookie
begin
repopath = cachepath(ext)
f = File.new(repopath, "r")
f.sysseek(-32, IO::SEEK_END)
fcookie = f.sysread(32)
return false if fcookie.length != 32
return false if cookie && fcookie != cookie
if !ext && @type != 'system'
f.sysseek(-32 * 2, IO::SEEK_END)
fextcookie = f.sysread(32)
return false if fextcookie.length != 32
end
f.sysseek(0, IO::SEEK_SET)
nf = Solv::xfopen_fd('', f.fileno)
f.close
flags = ext ? Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES : 0
flags |= Solv::Repo::REPO_LOCALPOOL if ext && ext != 'DL'
if ! @handle.add_solv(nf, flags)
nf.close
return false
end
nf.close()
@cookie = fcookie unless ext
@extcookie = fextcookie if !ext && @type != 'system'
now = Time.now
begin
File::utime(now, now, repopath) if mark
rescue SystemCallError
end
return true
rescue SystemCallError
return false
end
return true
end
def writecachedrepo(ext, repodata = nil)
return if @incomplete
begin
Dir::mkdir("/var/cache/solv", 0755) unless FileTest.directory?("/var/cache/solv")
f = Tempfile.new('.newsolv-', '/var/cache/solv')
f.chmod(0444)
sf = Solv::xfopen_fd('', f.fileno)
if !repodata
@handle.write(sf)
elsif ext
repodata.write(sf)
else
@handle.write_first_repodata(sf)
end
sf.close
f.sysseek(0, IO::SEEK_END)
if @type != 'system' && !ext
@extcookie = calc_cookie_ext(f, @cookie) unless @extcookie
f.syswrite(@extcookie)
end
f.syswrite(ext ? @extcookie : @cookie)
f.close
if @handle.iscontiguous?
sf = Solv::xfopen(f.path)
if sf
if !ext
@handle.empty()
abort("internal error, cannot reload solv file") unless @handle.add_solv(sf, repodata ? 0 : Solv::Repo::SOLV_ADD_NO_STUBS)
else
repodata.extend_to_repo()
flags = Solv::Repo::REPO_EXTEND_SOLVABLES
flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
repodata.add_solv(sf, flags)
end
sf.close
end
end
File.rename(f.path, cachepath(ext))
f.unlink
return true
rescue SystemCallError
return false
end
end
def updateaddedprovides(addedprovides)
return if @incomplete
return unless @handle && !@handle.isempty?
repodata = @handle.first_repodata()
return unless repodata
oldaddedprovides = repodata.lookup_idarray(Solv::SOLVID_META, Solv::REPOSITORY_ADDEDFILEPROVIDES)
return if (oldaddedprovides | addedprovides) == oldaddedprovides
for id in addedprovides
repodata.add_idarray(Solv::SOLVID_META, Solv::REPOSITORY_ADDEDFILEPROVIDES, id)
end
repodata.internalize()
writecachedrepo(nil, repodata)
end
def packagespath()
return ''
end
@@langtags = {
Solv::SOLVABLE_SUMMARY => Solv::REPOKEY_TYPE_STR,
Solv::SOLVABLE_DESCRIPTION => Solv::REPOKEY_TYPE_STR,
Solv::SOLVABLE_EULA => Solv::REPOKEY_TYPE_STR,
Solv::SOLVABLE_MESSAGEINS => Solv::REPOKEY_TYPE_STR,
Solv::SOLVABLE_MESSAGEDEL => Solv::REPOKEY_TYPE_STR,
Solv::SOLVABLE_CATEGORY => Solv::REPOKEY_TYPE_ID,
}
def add_ext_keys(ext, repodata, h)
if ext == 'DL'
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOSITORY_DELTAINFO)
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_FLEXARRAY)
elsif ext == 'DU'
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_DISKUSAGE)
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRNUMNUMARRAY)
elsif ext == 'FL'
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_FILELIST)
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRSTRARRAY)
else
@@langtags.sort.each do |langid, langtype|
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, @handle.pool.id2langid(langid, ext, true))
repodata.add_idarray(h, Solv::REPOSITORY_KEYS, langtype)
end
end
end
end
class Repo_rpmmd < Repo_generic
def find(what)
di = @handle.Dataiterator_meta(Solv::REPOSITORY_REPOMD_TYPE, what, Solv::Dataiterator::SEARCH_STRING)
di.prepend_keyname(Solv::REPOSITORY_REPOMD)
for d in di
dp = d.parentpos()
filename = dp.lookup_str(Solv::REPOSITORY_REPOMD_LOCATION)
next unless filename
checksum = dp.lookup_checksum(Solv::REPOSITORY_REPOMD_CHECKSUM)
if !checksum
puts "no #{filename} checksum!"
return nil, nil
end
return filename, checksum
end
return nil, nil
end
def load(pool)
return true if super(pool)
print "rpmmd repo '#{@name}: "
f = download("repodata/repomd.xml", false, nil, nil)
if !f
puts "no repomd.xml file, skipped"
@handle.free(true)
@handle = nil
return false
end
@cookie = calc_cookie_fp(f)
if usecachedrepo(nil, true)
puts "cached"
f.close
return true
end
@handle.add_repomdxml(f, 0)
f.close
puts "fetching"
filename, filechksum = find('primary')
if filename
f = download(filename, true, filechksum, true)
if f
@handle.add_rpmmd(f, nil, 0)
f.close
end
return false if @incomplete
end
filename, filechksum = find('updateinfo')
if filename
f = download(filename, true, filechksum, true)
if f
@handle.add_updateinfoxml(f, 0)
f.close
end
end
add_exts()
writecachedrepo(nil)
@handle.create_stubs()
return true
end
def add_ext(repodata, what, ext)
filename, filechksum = find(what)
filename, filechksum = find('prestodelta') if !filename && what == 'deltainfo'
return unless filename
h = repodata.new_handle()
repodata.set_poolstr(h, Solv::REPOSITORY_REPOMD_TYPE, what)
repodata.set_str(h, Solv::REPOSITORY_REPOMD_LOCATION, filename)
repodata.set_checksum(h, Solv::REPOSITORY_REPOMD_CHECKSUM, filechksum)
add_ext_keys(ext, repodata, h)
repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
end
def add_exts
repodata = @handle.add_repodata(0)
repodata.extend_to_repo()
add_ext(repodata, 'deltainfo', 'DL')
add_ext(repodata, 'filelists', 'FL')
repodata.internalize()
end
def load_ext(repodata)
repomdtype = repodata.lookup_str(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_TYPE)
if repomdtype == 'filelists'
ext = 'FL'
elsif repomdtype == 'deltainfo'
ext = 'DL'
else
return false
end
print "[#{@name}:#{ext}: "
STDOUT.flush
if usecachedrepo(ext)
puts "cached]\n"
return true
end
puts "fetching]\n"
filename = repodata.lookup_str(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_LOCATION)
filechksum = repodata.lookup_checksum(Solv::SOLVID_META, Solv::REPOSITORY_REPOMD_CHECKSUM)
f = download(filename, true, filechksum)
return false unless f
if ext == 'FL'
@handle.add_rpmmd(f, 'FL', Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES|Solv::Repo::REPO_LOCALPOOL)
elsif ext == 'DL'
@handle.add_deltainfoxml(f, Solv::Repo::REPO_USE_LOADING)
end
f.close
writecachedrepo(ext, repodata)
return true
end
end
class Repo_susetags < Repo_generic
def find(what)
di = @handle.Dataiterator_meta(Solv::SUSETAGS_FILE_NAME, what, Solv::Dataiterator::SEARCH_STRING)
di.prepend_keyname(Solv::SUSETAGS_FILE)
for d in di
dp = d.parentpos()
checksum = dp.lookup_checksum(Solv::SUSETAGS_FILE_CHECKSUM)
return what, checksum
end
return nil, nil
end
def load(pool)
return true if super(pool)
print "susetags repo '#{@name}: "
f = download("content", false, nil, nil)
if !f
puts "no content file, skipped"
@handle.free(true)
@handle = nil
return false
end
@cookie = calc_cookie_fp(f)
if usecachedrepo(nil, true)
puts "cached"
f.close
return true
end
@handle.add_content(f, 0)
f.close
puts "fetching"
defvendorid = @handle.meta.lookup_id(Solv::SUSETAGS_DEFAULTVENDOR)
descrdir = @handle.meta.lookup_str(Solv::SUSETAGS_DESCRDIR)
descrdir = "suse/setup/descr" unless descrdir
(filename, filechksum) = find('packages.gz')
(filename, filechksum) = find('packages') unless filename
if filename
f = download("#{descrdir}/#{filename}", true, filechksum, true)
if f
@handle.add_susetags(f, defvendorid, nil, Solv::Repo::REPO_NO_INTERNALIZE|Solv::Repo::SUSETAGS_RECORD_SHARES)
f.close
(filename, filechksum) = find('packages.en.gz')
(filename, filechksum) = find('packages.en') unless filename
if filename
f = download("#{descrdir}/#{filename}", true, filechksum, true)
if f
@handle.add_susetags(f, defvendorid, nil, Solv::Repo::REPO_NO_INTERNALIZE|Solv::Repo::REPO_REUSE_REPODATA|Solv::Repo::REPO_EXTEND_SOLVABLES)
f.close
end
end
@handle.internalize()
end
end
add_exts()
writecachedrepo(nil)
@handle.create_stubs()
return true
end
def add_ext(repodata, what, ext)
(filename, filechksum) = find(what)
h = repodata.new_handle()
repodata.set_str(h, Solv::SUSETAGS_FILE_NAME, filename)
repodata.set_checksum(h, Solv::SUSETAGS_FILE_CHECKSUM, filechksum)
add_ext_keys(ext, repodata, h)
repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
end
def add_exts
repodata = @handle.add_repodata(0)
di = @handle.Dataiterator_meta(Solv::SUSETAGS_FILE_NAME, nil, 0)
di.prepend_keyname(Solv::SUSETAGS_FILE)
for d in di
filename = d.str
next unless filename && filename =~ /^packages\.(..)(?:\..*)$/
next if $1 == 'en' || $1 == 'gz'
add_ext(repodata, filename, $1)
end
repodata.internalize()
end
def load_ext(repodata)
filename = repodata.lookup_str(Solv::SOLVID_META, Solv::SUSETAGS_FILE_NAME)
ext = filename[9,2]
print "[#{@name}:#{ext}: "
STDOUT.flush
if usecachedrepo(ext)
puts "cached]\n"
return true
end
puts "fetching]\n"
defvendorid = @handle.meta.lookup_id(Solv::SUSETAGS_DEFAULTVENDOR)
descrdir = @handle.meta.lookup_str(Solv::SUSETAGS_DESCRDIR)
descrdir = "suse/setup/descr" unless descrdir
filechksum = repodata.lookup_checksum(Solv::SOLVID_META, Solv::SUSETAGS_FILE_CHECKSUM)
f = download("#{descrdir}/#{filename}", true, filechksum)
return false unless f
flags = Solv::Repo::REPO_USE_LOADING|Solv::Repo::REPO_EXTEND_SOLVABLES
flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
@handle.add_susetags(f, defvendorid, ext, flags)
f.close
writecachedrepo(ext, repodata)
return true
end
def packagespath()
datadir = @handle.meta.lookup_str(Solv::SUSETAGS_DATADIR)
datadir = "suse" unless datadir
return datadir + '/'
end
end
class Repo_unknown < Repo_generic
def load(pool)
puts "unsupported repo '#{@name}: skipped"
return false
end
end
class Repo_system < Repo_generic
def load(pool)
@handle = pool.add_repo(@name)
@handle.appdata = self
pool.installed = @handle
print "rpm database: "
@cookie = calc_cookie_file("/var/lib/rpm/Packages")
if usecachedrepo(nil)
puts "cached"
return true
end
puts "reading"
if @handle.respond_to? :add_products
@handle.add_products("/etc/products.d", Solv::Repo::REPO_NO_INTERNALIZE)
end
f = Solv::xfopen(cachepath())
@handle.add_rpmdb_reffp(f, Solv::Repo::REPO_REUSE_REPODATA)
f.close if f
writecachedrepo(nil)
return true
end
end
args = ARGV
cmd = args.shift
cmdabbrev = { 'ls' => 'list', 'in' => 'install', 'rm' => 'erase',
've' => 'verify', 'se' => 'search' }
cmd = cmdabbrev[cmd] if cmdabbrev.has_key?(cmd)
cmdactionmap = {
'install' => Solv::Job::SOLVER_INSTALL,
'erase' => Solv::Job::SOLVER_ERASE,
'up' => Solv::Job::SOLVER_UPDATE,
'dup' => Solv::Job::SOLVER_DISTUPGRADE,
'verify' => Solv::Job::SOLVER_VERIFY,
'list' => 0,
'info' => 0,
}
repos = []
reposdirs = []
if FileTest.directory?('/etc/zypp/repos.d')
reposdirs = [ '/etc/zypp/repos.d' ]
else
reposdirs = [ '/etc/yum/repos.d' ]
end
for reposdir in reposdirs do
next unless FileTest.directory?(reposdir)
for reponame in Dir["#{reposdir}/*.repo"].sort do
cfg = IniFile.load(reponame)
cfg.each_section do |ali|
repoattr = { 'alias' => ali, 'enabled' => 0, 'priority' => 99, 'autorefresh' => 1, 'type' => 'rpm-md', 'metadata_expire' => 900}
repoattr.update(cfg[ali])
if repoattr['type'] == 'rpm-md'
repo = Repo_rpmmd.new(ali, 'repomd', repoattr)
elsif repoattr['type'] == 'yast2'
repo = Repo_susetags.new(ali, 'susetags', repoattr)
else
repo = Repo_unknown.new(ali, 'unknown', repoattr)
end
repos.push(repo)
end
end
end
pool = Solv::Pool.new()
pool.setarch()
pool.set_loadcallback { |repodata|
repo = repodata.repo.appdata
repo ? repo.load_ext(repodata) : false
}
sysrepo = Repo_system.new('@System', 'system')
sysrepo.load(pool)
for repo in repos
repo.load(pool) if repo.enabled?
end
if cmd == 'search'
pool.createwhatprovides()
sel = pool.Selection
for di in pool.Dataiterator(Solv::SOLVABLE_NAME, args[0], Solv::Dataiterator::SEARCH_SUBSTRING | Solv::Dataiterator::SEARCH_NOCASE)
sel.add_raw(Solv::Job::SOLVER_SOLVABLE, di.solvid)
end
for s in sel.solvables
puts "- #{s.str} [#{s.repo.name}]: #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
end
exit
end
abort("unknown command '#{cmd}'\n") unless cmdactionmap.has_key?(cmd)
addedprovides = pool.addfileprovides_queue()
if !addedprovides.empty?
sysrepo.updateaddedprovides(addedprovides)
for repo in repos
repo.updateaddedprovides(addedprovides)
end
end
pool.createwhatprovides()
jobs = []
for arg in args
flags = Solv::Selection::SELECTION_NAME | Solv::Selection::SELECTION_PROVIDES | Solv::Selection::SELECTION_GLOB
flags |= Solv::Selection::SELECTION_CANON | Solv::Selection::SELECTION_DOTARCH | Solv::Selection::SELECTION_REL
if arg =~ /^\//
flags |= Solv::Selection::SELECTION_FILELIST
flags |= Solv::Selection::SELECTION_INSTALLED_ONLY if cmd == 'erase'
end
sel = pool.select(arg, flags)
if sel.isempty?
sel = pool.select(arg, flags | Solv::Selection::SELECTION_NOCASE)
puts "[ignoring case for '#{arg}']" unless sel.isempty?
end
puts "[using file list match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_FILELIST != 0
puts "[using capability match for '#{arg}']" if sel.flags & Solv::Selection::SELECTION_PROVIDES != 0
jobs += sel.jobs(cmdactionmap[cmd])
end
if jobs.empty? && (cmd == 'up' || cmd == 'dup' || cmd == 'verify')
sel = pool.Selection_all()
jobs += sel.jobs(cmdactionmap[cmd])
end
abort("no package matched.") if jobs.empty?
if cmd == 'list' || cmd == 'info'
for job in jobs
for s in job.solvables()
if cmd == 'info'
puts "Name: #{s.str}"
puts "Repo: #{s.repo.name}"
puts "Summary: #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
str = s.lookup_str(Solv::SOLVABLE_URL)
puts "Url: #{str}" if str
str = s.lookup_str(Solv::SOLVABLE_LICENSE)
puts "License: #{str}" if str
puts "Description:\n#{s.lookup_str(Solv::SOLVABLE_DESCRIPTION)}"
puts
else
puts " - #{s.str} [#{s.repo.name}]"
puts " #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
end
end
end
exit
end
for job in jobs
job.how ^= Solv::Job::SOLVER_UPDATE ^ Solv::Job::SOLVER_INSTALL if cmd == 'up' and job.isemptyupdate?
end
solver = pool.Solver
solver.set_flag(Solv::Solver::SOLVER_FLAG_SPLITPROVIDES, 1)
solver.set_flag(Solv::Solver::SOLVER_FLAG_ALLOW_UNINSTALL, 1) if cmd == 'erase'
#pool.set_debuglevel(1)
while true
problems = solver.solve(jobs)
break if problems.empty?
for problem in problems
puts "Problem #{problem.id}/#{problems.count}:"
puts problem
solutions = problem.solutions
for solution in solutions
puts " Solution #{solution.id}:"
elements = solution.elements(true)
for element in elements
puts " - #{element.str}"
end
puts
end
sol = nil
while true
print "Please choose a solution: "
STDOUT.flush
sol = STDIN.gets.strip
break if sol == 's' || sol == 'q'
break if sol =~ /^\d+$/ && sol.to_i >= 1 && sol.to_i <= solutions.length
end
next if sol == 's'
abort if sol == 'q'
solution = solutions[sol.to_i - 1]
for element in solution.elements
newjob = element.Job()
if element.type == Solv::Solver::SOLVER_SOLUTION_JOB
jobs[element.jobidx] = newjob
else
jobs.push(newjob) if newjob && !jobs.include?(newjob)
end
end
end
end
trans = solver.transaction
solver = nil
if trans.isempty?
puts "Nothing to do."
exit
end
puts "\nTransaction summary:\n"
for cl in trans.classify(Solv::Transaction::SOLVER_TRANSACTION_SHOW_OBSOLETES | Solv::Transaction::SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE)
if cl.type == Solv::Transaction::SOLVER_TRANSACTION_ERASE
puts "#{cl.count} erased packages:"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_INSTALL
puts "#{cl.count} installed packages:"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_REINSTALLED
puts "#{cl.count} reinstalled packages:"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
puts "#{cl.count} downgraded packages:"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_CHANGED
puts "#{cl.count} changed packages:"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED
puts "#{cl.count} upgraded packages:"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE
puts "#{cl.count} vendor changes from '#{cl.fromstr}' to '#{cl.tostr}':"
elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE
puts "#{cl.count} arch changes from '#{cl.fromstr}' to '#{cl.tostr}':"
else
next
end
for p in cl.solvables
if cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED || cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
puts " - #{p.str} -> #{trans.othersolvable(p).str}"
else
puts " - #{p.str}"
end
end
puts
end
puts "install size change: #{trans.calc_installsizechange()} K\n\n"
while true
print("OK to continue (y/n)? ")
STDOUT.flush
yn = STDIN.gets.strip
break if yn == 'y'
abort if yn == 'n' || yn == 'q'
end
newpkgs = trans.newsolvables()
newpkgsfp = {}
if !newpkgs.empty?
downloadsize = 0
for p in newpkgs
downloadsize += p.lookup_num(Solv::SOLVABLE_DOWNLOADSIZE)
end
puts "Downloading #{newpkgs.length} packages, #{downloadsize / 1024} K"
for p in newpkgs
repo = p.repo.appdata
location, medianr = p.lookup_location()
next unless location
location = repo.packagespath + location
chksum = p.lookup_checksum(Solv::SOLVABLE_CHECKSUM)
f = repo.download(location, false, chksum)
abort("\n#{@name}: #{location} not found in repository\n") unless f
newpkgsfp[p.id] = f
print "."
STDOUT.flush()
end
puts
end
puts "Committing transaction:"
puts
trans.order()
for p in trans.steps
steptype = trans.steptype(p, Solv::Transaction::SOLVER_TRANSACTION_RPM_ONLY)
if steptype == Solv::Transaction::SOLVER_TRANSACTION_ERASE
puts "erase #{p.str}"
next unless p.lookup_num(Solv::RPM_RPMDBID)
evr = p.evr.sub(/^[0-9]+:/, '')
system('rpm', '-e', '--nodeps', '--nodigest', '--nosignature', "#{p.name}-#{evr}.#{p.arch}") || abort("rpm failed: #{$? >> 8}")
elsif (steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL || steptype == Solv::Transaction::SOLVER_TRANSACTION_MULTIINSTALL)
puts "install #{p.str}"
f = newpkgsfp.delete(p.id)
next unless f
mode = steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i'
f.cloexec(0)
system('rpm', mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/#{f.fileno().to_s}") || abort("rpm failed: #{$? >> 8}")
f.close
end
end