#!/usr/bin/env ruby # Copyright (C) 2005 SIPfoundry Inc. # Licensed by SIPfoundry under the LGPL license. # # Copyright (C) 2005 Pingtel Corp. # Licensed to SIPfoundry under a Contributor Agreement. require 'getoptlong' require 'tempfile' class Setup attr_accessor :previous attr_accessor :current # Default implementation is a Redhat system def initialize(system) @system = system end def verbose=(enabled) @system.verbose = enabled end def upgrade raise "previous and current parameters required" unless previous && current @system.unresolved(current).each do |file| if resolve(file) @system.mark_resolved(file) end end end def show_unresolved raise "current parameters required" unless current @system.unresolved(current).each do |file| @system.console file end end def resolve(file) patch = create_patch(file) if patch != '' if @system.test_patch(file, patch) @system.apply_patch(file, patch) return true end end return false end def create_patch(file) tar_file=file[1..-1] current_file = @system.extract_file(current, tar_file) previous_file = @system.extract_file(previous, tar_file) patch = @system.create_patch(previous_file, current_file) @system.delete_file(current_file) @system.delete_file(previous_file) return patch end end # commands AND so calls can be stubbed out for unitttests class SystemService @@patch = 'patch' @@diff = 'diff' @@tar = 'tar' attr_accessor :verbose def initialize() verbose = false end alias oldBackquote ` def `(cmd) if verbose puts cmd end return oldBackquote(cmd) end def unresolved(archive) unresolved = [] raise "File missing #{archive}" unless File.exists?(archive) `tar -tzf #{archive}`.each do |file| filename = '/' + file.chomp if File.exist?(filename + '.rpmnew') unresolved.push filename end end return unresolved end def console(msg) puts msg if verbose return msg end def mark_resolved(file) File.delete(file + '.rpmnew') end def create_patch(file1, file2) patch = `#{@@diff} --unified #{file1} #{file2} 2>/dev/null` rc = "#{$?}" return rc != '2' ? patch : '' end def delete_file(file) File.delete(file) end def test_patch(file, patch) cmd = console("#{@@patch} --unified --forward --dry-run #{file} 1>& 2>/dev/null") IO.popen(cmd, "w") {|pipe| pipe.puts patch } rc = "#{$?}" return rc == '0' end def apply_patch(file, patch) stat = File.stat(file) cmd = console("#{@@patch} --unified --forward \"#{file}\" 1>& 2>/dev/null") IO.popen(cmd, "w") {|pipe| pipe.puts patch } File.chown(stat.uid, stat.gid, file) end def extract_file(archive, file) extract = Tempfile.new('pkg-upgrade').path `tar --to-stdout -xzf "#{archive}" "#{file}" > #{extract} 2>/dev/null` return extract end end def usage_exit(error_code=1) usage = <<__EOU__ Usage: #{ $0 } parameters Utilities to upgrade a package's configuration files Parameters: --help This help text --verbose Verbose output --previous Name of archive containing pristine copies of previous configuration --current Name of archive containing pristine copies of current configuration __EOU__ STDERR << usage exit error_code end if __FILE__ == $0 # XPB-863 - used to ensure patch command forces config files to # -rw-r--r-- permission bits File.umask(022) OptSet = [ ['--previous','-p', GetoptLong::REQUIRED_ARGUMENT], ['--current','-c', GetoptLong::REQUIRED_ARGUMENT], ['--show-unresolved','-u', GetoptLong::NO_ARGUMENT], ['--verbose','-v', GetoptLong::NO_ARGUMENT], ['--help','-h', GetoptLong::NO_ARGUMENT], ] setup = Setup.new(SystemService.new) opts = GetoptLong.new(*OptSet) begin action = 'upgrade' opts.each do |name, arg| case name when '--help' usage_exit 0 when '--previous' setup.previous = arg when '--current' setup.current = arg when '--show-unresolved' action = 'show_unresolved' when '--verbose' setup.verbose = true else usage_exit end end rescue usage_exit end begin setup.send(action) end end