class RPM::Transaction

Public Class Methods

new(opts={}) click to toggle source
# File lib/rpm/transaction.rb, line 19
def initialize(opts={})
  # http://markmail.org/message/ypsiqxop442p7rzz
  # The key pointer needs to stay valid during commit
  # so we keep a reference to them mapping from
  # object_id to ruby object.
  @keys = {}
  opts[:root] ||= '/'

  @ptr = ::FFI::AutoPointer.new(RPM::C.rpmtsCreate, Transaction.method(:release))
  RPM::C.rpmtsSetRootDir(@ptr, opts[:root])
end
release(ptr) click to toggle source
# File lib/rpm/transaction.rb, line 15
def self.release(ptr)
  RPM::C.rpmtsFree(ptr)
end

Public Instance Methods

check() { |problem| ... } click to toggle source
# File lib/rpm/transaction.rb, line 146
def check
  rc = RPM::C.rpmtsCheck(@ptr)
  probs = RPM::C.rpmtsProblems(@ptr)

  return if rc < 0
  begin
    psi = RPM::C.rpmpsInitIterator(probs)
    while (RPM::C.rpmpsNextIterator(psi) >= 0)
      problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
      yield problem
    end
  ensure
    RPM::C.rpmpsFree(probs)
  end
end
clean() click to toggle source

Free memory needed only for dependency checks and ordering.

# File lib/rpm/transaction.rb, line 142
def clean
  RPM::C.rpmtsClean(@ptr)
end
commit(&user_callback) click to toggle source

Performs the transaction. @param [Number] flag Transaction flags, default RPM::TRANS_FLAG_NONE @param [Number] filter Transaction filter, default RPM::PROB_FILTER_NONE @example

transaction.commit

You can supply your own callback @example

transaction.commit do |data|
end

end @yield [CallbackData] sig Transaction progress

# File lib/rpm/transaction.rb, line 173
def commit(&user_callback)
  flags = RPM::C::TransFlags[:none]

  callback = Proc.new do |hdr, type, amount, total, key_ptr, data_ignored|
    key_id = key_ptr.address
    key = @keys.include?(key_id) ? @keys[key_id] : nil

    case
      when block_given?
        package = hdr.null? ? nil : Package.new(hdr)
        data = CallbackData.new(type, key, package, amount, total)
        user_callback.call(data)
      else
        RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
    end
  end
  # We create a callback to pass to the C method and we
  # call the user supplied callback from there
  #
  # The C callback expects you to return a file handle,
  # We expect from the user to get a File, which we
  # then convert to a file handle to return.
  callback = Proc.new do |hdr, type, amount, total, key_ptr, data_ignored|
    key_id = key_ptr.address
    key = @keys.include?(key_id) ? @keys[key_id] : nil

    case
      when block_given?
        package = hdr.null? ? nil : Package.new(hdr)
        data = CallbackData.new(type, key, package, amount, total)
        ret = user_callback.call(data)

        # For OPEN_FILE we need to do some type conversion
        # for certain callback types we need to do some
        case type
          when :inst_open_file
            # For :inst_open_file the user callback has to
            # return the open file
            if !ret.is_a?(::File)
              raise TypeError, "illegal return value type #{ret.class}. Expected File."
            end
            fdt = RPM::C.fdDup(ret.to_i)
            if (fdt.null? || RPM::C.Ferror(fdt) != 0)
              raise RuntimeError, "Can't use opened file #{data.key}: #{RPM::C.Fstrerror(fdt)}"
              RPM::C.Fclose(fdt) if not fdt.nil?
            else
              fdt = RPM::C.fdLink(fdt)
              @fdt = fdt
            end
            # return the (RPM type) file handle
            fdt
          when :inst_close_file
            fdt = @fdt
            RPM::C.Fclose(fdt)
            @fdt = nil
          else
            ret
        end
      else
        # No custom callback given, use the default to show progress
        RPM::C.rpmShowProgress(hdr, type, amount, total, key, data_ignored)
    end
  end

  rc = RPM::C.rpmtsSetNotifyCallback(@ptr, callback, nil)
  raise "Can't set commit callback" if rc != 0

  rc = RPM::C.rpmtsRun(@ptr, nil, :none)

  raise "#{self}: #{RPM::C.rpmlogMessage}" if rc < 0

  if rc > 0
    ps = RPM::C.rpmtsProblems(@ptr)
    psi = RPM::C.rpmpsInitIterator(ps)
    while (RPM::C.rpmpsNextIterator(psi) >= 0)
      problem = Problem.from_ptr(RPM::C.rpmpsGetProblem(psi))
      STDERR.puts problem
    end
    RPM::C.rpmpsFree(ps)
  end
end
db() click to toggle source

@return [DB] the database associated with this transaction

# File lib/rpm/transaction.rb, line 256
def db
  RPM::DB.new(self)
end
delete(pkg) click to toggle source

Add a delete operation to the transaction @param [String, Package, Dependency] pkg Package to delete

# File lib/rpm/transaction.rb, line 92
def delete(pkg)
  iterator = case pkg
  when Package
    pkg[:sigmd5] ? each_match(:sigmd5, pkg[:sigmd5]) : each_match(:label, pkg[:label])
  when String
    each_match(:label, pkg)
  when Dependency
    each_match(:label, pkg.name).set_iterator_version(pkg.version)
  else
    raise TypeError, "illegal argument type"
  end

  iterator.each do |header|
    ret = RPM::C.rpmtsAddEraseElement(@ptr, header.ptr, iterator.offset)
    raise RuntimeError, "Error while adding erase/#{pkg} to transaction" if ret != 0
  end
end
each(&block) click to toggle source

@yield [Package] Called for each package in the database @example

db.each do |pkg|
  puts pkg.name
end
# File lib/rpm/transaction.rb, line 72
def each(&block)
  each_match(0, nil, &block)
end
each_match(key, val, &block) click to toggle source

@yield [Package] Called for each match @param [Number] key RPM tag key @param [String] val Value to match @example

RPM.transaction do |t|
  t.each_match(RPM::TAG_ARCH, "x86_64") do |pkg|
    puts pkg.name
  end
end
# File lib/rpm/transaction.rb, line 57
def each_match(key, val, &block)
  it = init_iterator(key, val)

  return it unless block_given?

  it.each(&block)
end
flags() click to toggle source
# File lib/rpm/transaction.rb, line 126
def flags
  RPM::C.rpmtsFlags(@ptr)
end
flags=(fl) click to toggle source
# File lib/rpm/transaction.rb, line 122
def flags=(fl)
  RPM::C.rpmtsSetFlags(@ptr, fl)
end
init_iterator(tag, val) click to toggle source

@return [RPM::MatchIterator] Creates an iterator for tag and val

# File lib/rpm/transaction.rb, line 32
def init_iterator(tag, val)
  raise TypeError if (val && !val.is_a?(String))

  it_ptr = RPM::C.rpmtsInitIterator(@ptr, tag.nil? ? 0 : tag, val, 0)

  raise "Can't init iterator for [#{tag}] -> '#{val}'" if it_ptr.null?
  return MatchIterator.from_ptr(it_ptr)
end
install(pkg, key) click to toggle source

Add a install operation to the transaction @param [Package] pkg Package to install @param [String] key e.g. filename where to install from

# File lib/rpm/transaction.rb, line 79
def install(pkg, key)
  install_element(pkg, key, :upgrade => false)
end
order() click to toggle source

Determine package order in the transaction according to dependencies

The final order ends up as installed packages followed by removed packages, with packages removed for upgrades immediately following the new package to be installed.

@returns [Fixnum] no. of (added) packages that could not be ordered

# File lib/rpm/transaction.rb, line 137
def order
  RPM::C.rpmtsOrder(@ptr)
end
ptr() click to toggle source

@visibility private

# File lib/rpm/transaction.rb, line 42
def ptr
  @ptr
end
root_dir() click to toggle source

@return [String ] the root directory for this transaction

# File lib/rpm/transaction.rb, line 118
def root_dir
  RPM::C.rpmtsRootDir(@ptr)
end
root_dir=(dir) click to toggle source

Sets the root directory for this transaction @param [String] root directory

# File lib/rpm/transaction.rb, line 112
def root_dir=(dir)
  rc = RPM::C.rpmtsSetRootDir(@ptr, dir)
  raise "Can't set #{dir} as root directory" if rc < 0
end
upgrade(pkg, key) click to toggle source

Add an upgrade operation to the transaction @param [Package] pkg Package to upgrade @param [String] key e.g. filename where to install from

# File lib/rpm/transaction.rb, line 86
def upgrade(pkg, key)
  install_element(pkg, key, :upgrade => true)
end

Private Instance Methods

install_element(pkg, key, opts={}) click to toggle source

@param [Package] pkg package to install @param [String] key e.g. filename where to install from @param opts options

@option :upgrade Upgrade packages if true
# File lib/rpm/transaction.rb, line 266
def install_element(pkg, key, opts={})
  raise TypeError, "illegal argument type" if not pkg.is_a?(RPM::Package)
  raise ArgumentError, "#{self}: key '#{key}' must be unique" if @keys.include?(key.object_id)

  # keep a reference to the key as rpmtsAddInstallElement will keep a copy
  # of the passed pointer (we pass the object_id)
  @keys[key.object_id] = key

  ret = RPM::C.rpmtsAddInstallElement(@ptr, pkg.ptr, FFI::Pointer.new(key.object_id), opts[:upgrade] ? 1 : 0, nil)
  raise RuntimeError if ret != 0
  nil
end