tpkg 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/bin/tpkg +28 -23
- data/lib/tpkg.rb +243 -233
- data/lib/tpkg/deployer.rb +37 -32
- metadata +2 -2
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ spec = Gem::Specification.new do |s|
|
|
5
5
|
s.add_dependency('facter')
|
6
6
|
s.add_dependency('net-ssh')
|
7
7
|
s.add_dependency('ddao-kwalify')
|
8
|
-
s.version = '2.0
|
8
|
+
s.version = '2.1.0'
|
9
9
|
s.authors = ['Darren Dao', 'Jason Heiss']
|
10
10
|
s.email = 'tpkg-users@lists.sourceforge.net'
|
11
11
|
s.homepage = 'http://tpkg.sourceforge.net'
|
data/bin/tpkg
CHANGED
@@ -9,7 +9,6 @@ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
|
9
9
|
|
10
10
|
require 'optparse'
|
11
11
|
require 'tpkg'
|
12
|
-
require 'etc'
|
13
12
|
|
14
13
|
#
|
15
14
|
# Parse the command line options
|
@@ -108,30 +107,36 @@ opts.on('--verify', '-V', '=NAME', 'Verify packages') do |opt|
|
|
108
107
|
@action = :verify
|
109
108
|
@action_value = opt
|
110
109
|
end
|
111
|
-
opts.on('--start', '=NAME', 'Start the init script for
|
110
|
+
opts.on('--start', '=NAME', 'Start the init script for specified package', Array) do |opt|
|
112
111
|
@rerun_with_sudo = true
|
113
112
|
@action = :execute_init
|
114
113
|
@init_options[:packages] = opt
|
115
114
|
@init_options[:cmd] = 'start'
|
116
115
|
end
|
117
|
-
opts.on('--stop', '=NAME', 'Stop the init script for
|
116
|
+
opts.on('--stop', '=NAME', 'Stop the init script for specified package', Array) do |opt|
|
118
117
|
@rerun_with_sudo = true
|
119
118
|
@action = :execute_init
|
120
119
|
@init_options[:packages] = opt
|
121
120
|
@init_options[:cmd] = 'stop'
|
122
121
|
end
|
123
|
-
opts.on('--restart', '=NAME', 'Restart the init script for
|
122
|
+
opts.on('--restart', '=NAME', 'Restart the init script for specified package', Array) do |opt|
|
124
123
|
@rerun_with_sudo = true
|
125
124
|
@action = :execute_init
|
126
125
|
@init_options[:packages] = opt
|
127
126
|
@init_options[:cmd] = 'restart'
|
128
127
|
end
|
129
|
-
opts.on('--reload', '=NAME', 'Reload the init script for
|
128
|
+
opts.on('--reload', '=NAME', 'Reload the init script for specified package', Array) do |opt|
|
130
129
|
@rerun_with_sudo = true
|
131
130
|
@action = :execute_init
|
132
131
|
@init_options[:packages] = opt
|
133
132
|
@init_options[:cmd] = 'reload'
|
134
133
|
end
|
134
|
+
opts.on('--status', '=NAME', 'Get status from init script for specified package', Array) do |opt|
|
135
|
+
@rerun_with_sudo = true
|
136
|
+
@action = :execute_init
|
137
|
+
@init_options[:packages] = opt
|
138
|
+
@init_options[:cmd] = 'status'
|
139
|
+
end
|
135
140
|
opts.on('--start-all', 'Start the init scripts for all packages') do |opt|
|
136
141
|
@rerun_with_sudo = true
|
137
142
|
@action = :execute_init
|
@@ -152,7 +157,7 @@ opts.on('--reload-all', 'Reload the init script for all packages') do |opt|
|
|
152
157
|
@action = :execute_init
|
153
158
|
@init_options[:cmd] = 'reload'
|
154
159
|
end
|
155
|
-
opts.on('--exec-init', '=NAME', 'Execute init scripts for
|
160
|
+
opts.on('--exec-init', '=NAME', 'Execute init scripts for specified packages', Array) do |opt|
|
156
161
|
@rerun_with_sudo = true
|
157
162
|
@init_options[:packages] = opt
|
158
163
|
@action = :execute_init
|
@@ -161,7 +166,7 @@ opts.on('--init-script', '=NAME', 'What init scripts to execute', Array) do |opt
|
|
161
166
|
@rerun_with_sudo = true
|
162
167
|
@init_options[:scripts] = opt
|
163
168
|
end
|
164
|
-
opts.on('--init-cmd', '=CMD', 'Invoke
|
169
|
+
opts.on('--init-cmd', '=CMD', 'Invoke specified init script command') do |opt|
|
165
170
|
@rerun_with_sudo = true
|
166
171
|
@init_options[:cmd] = opt
|
167
172
|
end
|
@@ -306,7 +311,7 @@ def instantiate_tpkg(options = {})
|
|
306
311
|
sources = options["sources"] || []
|
307
312
|
report_server = nil
|
308
313
|
|
309
|
-
[File.join(Tpkg::
|
314
|
+
[File.join(Tpkg::DEFAULT_CONFIGDIR, 'tpkg.conf'), File.join(ENV['HOME'], ".tpkg.conf")].each do |configfile|
|
310
315
|
if File.exist?(configfile)
|
311
316
|
IO.foreach(configfile) do |line|
|
312
317
|
line.chomp!
|
@@ -337,23 +342,23 @@ def instantiate_tpkg(options = {})
|
|
337
342
|
# if they don't realize there's an environment variable set.
|
338
343
|
warn "Using base '#{base}' base from $TPKG_HOME"
|
339
344
|
end
|
340
|
-
|
341
|
-
|
345
|
+
|
346
|
+
if !@sudo
|
347
|
+
curruid = Process.euid
|
342
348
|
if curruid == 0
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
puts "** ERROR **\nWhen specifying --no-sudo, #{File.join(ENV['HOME'], ".tpkg.conf")} and custom 'base' dir with write privileges is required\n" and exit 1
|
348
|
-
elsif base == '/home/t'
|
349
|
-
puts "** ERROR **\nWhen specifying --no-sudo, custom base dir with write privileges is required\n" and exit 1
|
349
|
+
# Besides there being no point to running with --no-sudo when root, we
|
350
|
+
# don't want users to accidentally create files/directories that can't be
|
351
|
+
# modified by other users who properly run --no-sudo as a regular user.
|
352
|
+
raise "--no-sudo cannot be used as 'root' user or via sudo"
|
350
353
|
end
|
351
|
-
baseuid = File.stat(base).uid
|
352
|
-
|
353
|
-
|
354
|
-
|
354
|
+
baseuid = File.stat(base).uid
|
355
|
+
# We want to ensure that all --no-sudo usage within a given base directory
|
356
|
+
# is done under the same account.
|
357
|
+
if baseuid != curruid
|
358
|
+
raise "Base dir #{base} owned by UID #{baseuid}, not your UID #{curruid}"
|
355
359
|
end
|
356
360
|
end
|
361
|
+
|
357
362
|
tpkg = Tpkg.new(:base => base, :sources => sources, :report_server => report_server, :lockforce => @lockforce, :force => @force, :sudo => @sudo)
|
358
363
|
end
|
359
364
|
|
@@ -378,8 +383,8 @@ if @deploy
|
|
378
383
|
@deploy_options["abort-on-fail"] = false
|
379
384
|
|
380
385
|
# check to see if ssh-key is accessible
|
381
|
-
|
382
|
-
|
386
|
+
if @deploy_options["use-ssh-key"]
|
387
|
+
ssh_key = @deploy_options["ssh-key"]
|
383
388
|
if !File.readable?(ssh_key) && Process.euid == 0
|
384
389
|
raise "Unable to read ssh key from #{ssh_key}"
|
385
390
|
elsif !File.readable?(ssh_key)
|
data/lib/tpkg.rb
CHANGED
@@ -56,8 +56,7 @@ require 'kwalify' # for validating yaml
|
|
56
56
|
|
57
57
|
class Tpkg
|
58
58
|
|
59
|
-
VERSION = '2.0
|
60
|
-
CONFIGDIR = '/etc'
|
59
|
+
VERSION = '2.1.0'
|
61
60
|
|
62
61
|
GENERIC_ERR = 1
|
63
62
|
POSTINSTALL_ERR = 2
|
@@ -121,11 +120,11 @@ class Tpkg
|
|
121
120
|
raise "Unable to find GNU tar or bsdtar in PATH"
|
122
121
|
end
|
123
122
|
end
|
124
|
-
# bsdtar uses pax format by default. This format allows for vendor extensions, such
|
123
|
+
# bsdtar uses pax format by default. This format allows for vendor extensions, such
|
125
124
|
# as the SCHILY.* extensions which were introduced by star). bsdtar actually uses
|
126
125
|
# these extensions. These extension headers includde useful, but not vital information.
|
127
126
|
# gnu tar should just ignore them and gives a warning. This is what the latest gnu tar
|
128
|
-
# will do. However, on older gnu tar, it only threw an error at the end. The work
|
127
|
+
# will do. However, on older gnu tar, it only threw an error at the end. The work
|
129
128
|
# around is to explicitly tell gnu tar to ignore those extensions.
|
130
129
|
if @@tarinfo[:type] == 'gnu' && @@tarinfo[:version] != 'unknown' && @@tarinfo[:version] >= '1.15.1'
|
131
130
|
@@taroptions = "--pax-option='delete=SCHILY.*,delete=LIBARCHIVE.*'"
|
@@ -286,7 +285,7 @@ class Tpkg
|
|
286
285
|
# code (tar) ever touch the user's files.
|
287
286
|
system("#{find_tar} -C #{pkgsrcdir} -cf - . | #{find_tar} -C #{tpkgdir} -xpf -") || raise("Package content copy failed")
|
288
287
|
|
289
|
-
# check metadata file
|
288
|
+
# check metadata file
|
290
289
|
errors = []
|
291
290
|
metadata = Metadata::instantiate_from_dir(tpkgdir)
|
292
291
|
if !metadata
|
@@ -298,14 +297,15 @@ class Tpkg
|
|
298
297
|
schema_dir = File.join(File.dirname(File.dirname(__FILE__)), "schema")
|
299
298
|
# This is the directory where we put our dtd/schema for validating
|
300
299
|
# the metadata file
|
301
|
-
|
302
|
-
|
300
|
+
# FIXME: This method should become an instance method and use @configdir
|
301
|
+
elsif File.exist?(File.join(DEFAULT_CONFIGDIR, 'tpkg', 'schema'))
|
302
|
+
schema_dir = File.join(DEFAULT_CONFIGDIR, 'tpkg', 'schema')
|
303
303
|
else
|
304
304
|
warn "Warning: unable to find schema for tpkg.yml"
|
305
305
|
end
|
306
306
|
|
307
307
|
errors = metadata.validate(schema_dir) if schema_dir
|
308
|
-
if errors && !errors.empty?
|
308
|
+
if errors && !errors.empty?
|
309
309
|
puts "Bad metadata file. Possible error(s):"
|
310
310
|
errors.each {|e| puts e }
|
311
311
|
raise "Failed to create package." unless options[:force]
|
@@ -316,10 +316,10 @@ class Tpkg
|
|
316
316
|
File.open(File.join(tpkgdir, "file_metadata.bin"), "w") do |file|
|
317
317
|
filemetadata = get_filemetadata_from_directory(tpkgdir)
|
318
318
|
filemetadata[:files].each do |file1|
|
319
|
-
if metadata[:files] && metadata[:files][:files] &&
|
319
|
+
if metadata[:files] && metadata[:files][:files] &&
|
320
320
|
metadata[:files][:files].any?{|file2|file2[:path] == file1[:path] && file2[:config]}
|
321
321
|
file1[:config] = true
|
322
|
-
end
|
322
|
+
end
|
323
323
|
end
|
324
324
|
data = filemetadata.to_hash.recursively{|h| h.stringify_keys }
|
325
325
|
Marshal::dump(data, file)
|
@@ -344,7 +344,7 @@ class Tpkg
|
|
344
344
|
data = {:actual_file => working_path, :metadata => metadata, :file_metadata => tpkgfile}
|
345
345
|
perms, uid, gid = predict_file_perms_and_ownership(data)
|
346
346
|
# crontab needs to be owned by root, and is not writable by group or others
|
347
|
-
if uid != 0
|
347
|
+
if uid != 0
|
348
348
|
warn "Warning: Your cron jobs in \"#{tpkgfile[:path]}\" might fail to run because the file is not owned by root."
|
349
349
|
end
|
350
350
|
if (perms & 0022) != 0
|
@@ -369,7 +369,7 @@ class Tpkg
|
|
369
369
|
package_directory = File.join(workdir, package_filename)
|
370
370
|
Dir.mkdir(package_directory)
|
371
371
|
|
372
|
-
if outdir
|
372
|
+
if outdir
|
373
373
|
pkgfile = File.join(outdir, package_filename + '.tpkg')
|
374
374
|
else
|
375
375
|
pkgfile = File.join(File.dirname(pkgsrcdir), package_filename + '.tpkg')
|
@@ -386,7 +386,7 @@ class Tpkg
|
|
386
386
|
File.delete(pkgfile)
|
387
387
|
end
|
388
388
|
|
389
|
-
# update metadata file with the tpkg version
|
389
|
+
# update metadata file with the tpkg version
|
390
390
|
metadata.add_tpkg_version(VERSION)
|
391
391
|
|
392
392
|
# Tar up the tpkg directory
|
@@ -395,7 +395,7 @@ class Tpkg
|
|
395
395
|
|
396
396
|
# Checksum the tarball
|
397
397
|
# Older ruby version doesn't support this
|
398
|
-
# digest = Digest::SHA256.file(tpkgfile).hexdigest
|
398
|
+
# digest = Digest::SHA256.file(tpkgfile).hexdigest
|
399
399
|
digest = Digest::SHA256.hexdigest(File.read(tpkgfile))
|
400
400
|
|
401
401
|
# Create checksum.xml
|
@@ -410,7 +410,7 @@ class Tpkg
|
|
410
410
|
|
411
411
|
# compress if needed
|
412
412
|
if options[:compress]
|
413
|
-
tpkgfile = compress_file(tpkgfile, options[:compress])
|
413
|
+
tpkgfile = compress_file(tpkgfile, options[:compress])
|
414
414
|
end
|
415
415
|
|
416
416
|
# Tar up checksum.xml and the main tarball
|
@@ -494,7 +494,7 @@ class Tpkg
|
|
494
494
|
return FileMetadata.new(Marshal::dump(filemetadata),'bin')
|
495
495
|
end
|
496
496
|
|
497
|
-
def self.verify_package_checksum(package_file, options = {})
|
497
|
+
def self.verify_package_checksum(package_file, options = {})
|
498
498
|
topleveldir = options[:topleveldir] || package_toplevel_directory(package_file)
|
499
499
|
# Extract checksum.xml from the package
|
500
500
|
checksum_xml = nil
|
@@ -555,10 +555,10 @@ class Tpkg
|
|
555
555
|
file = File.join('tpkg', "tpkg.#{format}")
|
556
556
|
|
557
557
|
# use popen3 instead of popen because popen display stderr when there's an error such as
|
558
|
-
# tpkg.yml not being there, which is something we want to ignore since old tpkg doesn't
|
558
|
+
# tpkg.yml not being there, which is something we want to ignore since old tpkg doesn't
|
559
559
|
# have tpkg.yml file
|
560
560
|
extract_tpkg_tar_command = cmd_to_extract_tpkg_tar(package_file, topleveldir)
|
561
|
-
stdin, stdout, stderr = Open3.popen3("#{extract_tpkg_tar_command} | #{find_tar} -xf - -O #{file}")
|
561
|
+
stdin, stdout, stderr = Open3.popen3("#{extract_tpkg_tar_command} | #{find_tar} -xf - -O #{file}")
|
562
562
|
filecontent = stdout.read
|
563
563
|
if filecontent.nil? or filecontent.empty?
|
564
564
|
next
|
@@ -975,33 +975,6 @@ class Tpkg
|
|
975
975
|
gid.to_i
|
976
976
|
end
|
977
977
|
|
978
|
-
def self.gethttp(uri)
|
979
|
-
if uri.scheme != 'http' && uri.scheme != 'https'
|
980
|
-
# It would be possible to add support for FTP and possibly
|
981
|
-
# other things if anyone cares
|
982
|
-
raise "Only http/https URIs are supported, got: '#{uri}'"
|
983
|
-
end
|
984
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
985
|
-
if uri.scheme == 'https'
|
986
|
-
# Eliminate the OpenSSL "using default DH parameters" warning
|
987
|
-
if File.exist?(File.join(CONFIGDIR, 'tpkg', 'dhparams'))
|
988
|
-
dh = OpenSSL::PKey::DH.new(IO.read(File.join(CONFIGDIR, 'tpkg', 'dhparams')))
|
989
|
-
Net::HTTP.ssl_context_accessor(:tmp_dh_callback)
|
990
|
-
http.tmp_dh_callback = proc { dh }
|
991
|
-
end
|
992
|
-
http.use_ssl = true
|
993
|
-
if File.exist?(File.join(CONFIGDIR, 'tpkg', 'ca.pem'))
|
994
|
-
http.ca_file = File.join(CONFIGDIR, 'tpkg', 'ca.pem')
|
995
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
996
|
-
elsif File.directory?(File.join(CONFIGDIR, 'tpkg', 'ca'))
|
997
|
-
http.ca_path = File.join(CONFIGDIR, 'tpkg', 'ca')
|
998
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
999
|
-
end
|
1000
|
-
end
|
1001
|
-
http.start
|
1002
|
-
http
|
1003
|
-
end
|
1004
|
-
|
1005
978
|
# foo
|
1006
979
|
# foo=1.0
|
1007
980
|
# foo=1.0=1
|
@@ -1012,7 +985,7 @@ class Tpkg
|
|
1012
985
|
parts = request.split('=')
|
1013
986
|
|
1014
987
|
# upgrade/remove/query options could take package filenames
|
1015
|
-
# We're assuming that the filename ends in .tpkg and has a version number that starts
|
988
|
+
# We're assuming that the filename ends in .tpkg and has a version number that starts
|
1016
989
|
# with a digit. For example: foo-1.0.tpkg, foo-bar-1.0-1.tpkg
|
1017
990
|
if request =~ /\.tpkg$/
|
1018
991
|
req = {:filename => request, :name => request.split(/-\d/)[0]}
|
@@ -1038,10 +1011,10 @@ class Tpkg
|
|
1038
1011
|
|
1039
1012
|
# deploy_options is used for configuration the deployer. It is a map of option_names => option_values. Possible
|
1040
1013
|
# options are: use-ssh-key, deploy-as, worker-count, abort-on-fail
|
1041
|
-
#
|
1014
|
+
#
|
1042
1015
|
# deploy_params is an array that holds the list of paramters that is used when invoking tpkg on to the remote
|
1043
|
-
# servers where we want to deploy to.
|
1044
|
-
#
|
1016
|
+
# servers where we want to deploy to.
|
1017
|
+
#
|
1045
1018
|
# servers is an array, a filename or a callback that list the remote servers where we want to deploy to
|
1046
1019
|
def self.deploy(deploy_params, deploy_options, servers)
|
1047
1020
|
servers.uniq!
|
@@ -1105,7 +1078,7 @@ class Tpkg
|
|
1105
1078
|
return File.basename(filename) !~ /^\./
|
1106
1079
|
end
|
1107
1080
|
|
1108
|
-
# helper method for predicting the permissions and ownership of a file that
|
1081
|
+
# helper method for predicting the permissions and ownership of a file that
|
1109
1082
|
# will be installed by tpkg. This is done by looking at:
|
1110
1083
|
# 1) its current perms & ownership
|
1111
1084
|
# 2) the file_defaults settings of the metadata file
|
@@ -1138,7 +1111,7 @@ class Tpkg
|
|
1138
1111
|
perms = file_metadata[:posix][:perms] if file_metadata[:posix][:perms]
|
1139
1112
|
end
|
1140
1113
|
return perms, uid, gid
|
1141
|
-
end
|
1114
|
+
end
|
1142
1115
|
|
1143
1116
|
# Given a package file, figure out if tpkg.tar was compressed
|
1144
1117
|
# Return what type of compression. If tpkg.tar wasn't compressed, then return nil.
|
@@ -1147,7 +1120,7 @@ class Tpkg
|
|
1147
1120
|
IO.popen("#{find_tar} #{@@taroptions} -tf #{package_file}") do |pipe|
|
1148
1121
|
pipe.each do |file|
|
1149
1122
|
if file =~ /tpkg.tar.gz$/
|
1150
|
-
compression = "gzip"
|
1123
|
+
compression = "gzip"
|
1151
1124
|
elsif file =~ /tpkg.tar.bz2$/
|
1152
1125
|
compression = "bz2"
|
1153
1126
|
end
|
@@ -1170,7 +1143,7 @@ class Tpkg
|
|
1170
1143
|
end
|
1171
1144
|
|
1172
1145
|
# Compresses the file using the compression type
|
1173
|
-
# specified by the compress flag
|
1146
|
+
# specified by the compress flag
|
1174
1147
|
# Returns the compressed file
|
1175
1148
|
def self.compress_file(file, compress)
|
1176
1149
|
if compress == true or compress == "gzip"
|
@@ -1183,7 +1156,7 @@ class Tpkg
|
|
1183
1156
|
raise "Compression #{compress} is not supported"
|
1184
1157
|
end
|
1185
1158
|
if !$?.success? or !File.exists?(result)
|
1186
|
-
raise "Failed to compress the package"
|
1159
|
+
raise "Failed to compress the package"
|
1187
1160
|
end
|
1188
1161
|
return result
|
1189
1162
|
end
|
@@ -1203,10 +1176,11 @@ class Tpkg
|
|
1203
1176
|
#
|
1204
1177
|
|
1205
1178
|
DEFAULT_BASE = '/opt/tpkg'
|
1179
|
+
DEFAULT_CONFIGDIR = '/etc'
|
1206
1180
|
|
1207
1181
|
def initialize(options)
|
1208
1182
|
# Options
|
1209
|
-
@base = options[:base]
|
1183
|
+
@base = options[:base] ? options[:base] : DEFAULT_BASE
|
1210
1184
|
# An array of filenames or URLs which point to individual package files
|
1211
1185
|
# or directories containing packages and extracted metadata.
|
1212
1186
|
@sources = []
|
@@ -1238,11 +1212,14 @@ class Tpkg
|
|
1238
1212
|
@sudo = options[:sudo]
|
1239
1213
|
end
|
1240
1214
|
|
1215
|
+
@configdir = DEFAULT_CONFIGDIR
|
1216
|
+
|
1241
1217
|
@file_system_root = '/' # Not sure if this needs to be more portable
|
1242
1218
|
# This option is only intended for use by the test suite
|
1243
1219
|
if options[:file_system_root]
|
1244
1220
|
@file_system_root = options[:file_system_root]
|
1245
1221
|
@base = File.join(@file_system_root, @base)
|
1222
|
+
@configdir = File.join(@file_system_root, @configdir)
|
1246
1223
|
end
|
1247
1224
|
|
1248
1225
|
# Various external scripts that we run might need to adjust things for
|
@@ -1264,7 +1241,7 @@ class Tpkg
|
|
1264
1241
|
if Tpkg::get_os =~ /Darwin/
|
1265
1242
|
# Try to help our Mac OS X users, otherwise this could be
|
1266
1243
|
# rather confusing.
|
1267
|
-
warn "\nNote: /home is controlled by the automounter by default on Mac OS X.\n" +
|
1244
|
+
warn "\nNote: /home is controlled by the automounter by default on Mac OS X.\n" +
|
1268
1245
|
"You'll either need to disable that in /etc/auto_master or configure\n" +
|
1269
1246
|
"tpkg to use a different base via tpkg.conf.\n"
|
1270
1247
|
end
|
@@ -1277,7 +1254,7 @@ class Tpkg
|
|
1277
1254
|
@tmp_directory = File.join(@var_directory, 'tmp')
|
1278
1255
|
@log_directory = File.join(@var_directory, 'logs')
|
1279
1256
|
# It is important to create these dirs in correct order
|
1280
|
-
dirs_to_create = [@installed_directory, @metadata_directory, @sources_directory,
|
1257
|
+
dirs_to_create = [@installed_directory, @metadata_directory, @sources_directory,
|
1281
1258
|
@tmp_directory, @log_directory]
|
1282
1259
|
dirs_to_create.each do |dir|
|
1283
1260
|
if !File.exist?(dir)
|
@@ -1305,6 +1282,33 @@ class Tpkg
|
|
1305
1282
|
attr_reader :sudo
|
1306
1283
|
attr_reader :file_system_root
|
1307
1284
|
|
1285
|
+
def gethttp(uri)
|
1286
|
+
if uri.scheme != 'http' && uri.scheme != 'https'
|
1287
|
+
# It would be possible to add support for FTP and possibly
|
1288
|
+
# other things if anyone cares
|
1289
|
+
raise "Only http/https URIs are supported, got: '#{uri}'"
|
1290
|
+
end
|
1291
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
1292
|
+
if uri.scheme == 'https'
|
1293
|
+
# Eliminate the OpenSSL "using default DH parameters" warning
|
1294
|
+
if File.exist?(File.join(@configdir, 'tpkg', 'dhparams'))
|
1295
|
+
dh = OpenSSL::PKey::DH.new(IO.read(File.join(@configdir, 'tpkg', 'dhparams')))
|
1296
|
+
Net::HTTP.ssl_context_accessor(:tmp_dh_callback)
|
1297
|
+
http.tmp_dh_callback = proc { dh }
|
1298
|
+
end
|
1299
|
+
http.use_ssl = true
|
1300
|
+
if File.exist?(File.join(@configdir, 'tpkg', 'ca.pem'))
|
1301
|
+
http.ca_file = File.join(@configdir, 'tpkg', 'ca.pem')
|
1302
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
1303
|
+
elsif File.directory?(File.join(@configdir, 'tpkg', 'ca'))
|
1304
|
+
http.ca_path = File.join(@configdir, 'tpkg', 'ca')
|
1305
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
1306
|
+
end
|
1307
|
+
end
|
1308
|
+
http.start
|
1309
|
+
http
|
1310
|
+
end
|
1311
|
+
|
1308
1312
|
def source_to_local_directory(source)
|
1309
1313
|
source_as_directory = source.gsub(/[^a-zA-Z0-9]/, '')
|
1310
1314
|
File.join(@sources_directory, source_as_directory)
|
@@ -1327,22 +1331,22 @@ class Tpkg
|
|
1327
1331
|
warn "Warning: the source directory #{source} has no metadata.yml file. Try running tpkg -x #{source} first."
|
1328
1332
|
next
|
1329
1333
|
end
|
1330
|
-
|
1334
|
+
|
1331
1335
|
metadata_contents = File.read(File.join(source, 'metadata.yml'))
|
1332
1336
|
Metadata::get_pkgs_metadata_from_yml_doc(metadata_contents, metadata, source)
|
1333
1337
|
else
|
1334
1338
|
uri = http = localdate = remotedate = localdir = localpath = nil
|
1335
|
-
|
1339
|
+
|
1336
1340
|
uri = URI.join(source, 'metadata.yml')
|
1337
|
-
http =
|
1338
|
-
|
1341
|
+
http = gethttp(uri)
|
1342
|
+
|
1339
1343
|
# Calculate the path to the local copy of the metadata for this URI
|
1340
1344
|
localdir = source_to_local_directory(source)
|
1341
1345
|
localpath = File.join(localdir, 'metadata.yml')
|
1342
1346
|
if File.exist?(localpath)
|
1343
1347
|
localdate = File.mtime(localpath)
|
1344
1348
|
end
|
1345
|
-
|
1349
|
+
|
1346
1350
|
# get last modified time of the metadata file from the server
|
1347
1351
|
response = http.head(uri.path)
|
1348
1352
|
case response
|
@@ -1352,7 +1356,7 @@ class Tpkg
|
|
1352
1356
|
puts "Error fetching metadata from #{uri}: #{response.body}"
|
1353
1357
|
response.error! # Throws an exception
|
1354
1358
|
end
|
1355
|
-
|
1359
|
+
|
1356
1360
|
# Fetch the metadata if necessary
|
1357
1361
|
metadata_contents = nil
|
1358
1362
|
if !localdate || remotedate != localdate
|
@@ -1406,7 +1410,7 @@ class Tpkg
|
|
1406
1410
|
# Populate our list of available packages for a given package name
|
1407
1411
|
def load_available_packages(name=nil)
|
1408
1412
|
prep_metadata
|
1409
|
-
|
1413
|
+
|
1410
1414
|
if name
|
1411
1415
|
if !@available_packages[name]
|
1412
1416
|
packages = []
|
@@ -1417,7 +1421,7 @@ class Tpkg
|
|
1417
1421
|
end
|
1418
1422
|
end
|
1419
1423
|
@available_packages[name] = packages
|
1420
|
-
|
1424
|
+
|
1421
1425
|
if @@debug
|
1422
1426
|
puts "Loaded #{@available_packages[name].size} available packages for #{name}"
|
1423
1427
|
end
|
@@ -1436,7 +1440,7 @@ class Tpkg
|
|
1436
1440
|
end
|
1437
1441
|
end
|
1438
1442
|
end
|
1439
|
-
|
1443
|
+
|
1440
1444
|
# Used by load_available_native_packages to stuff all the info about a
|
1441
1445
|
# native package into a hash to match the structure we pass around
|
1442
1446
|
# internally for tpkgs
|
@@ -1483,7 +1487,7 @@ class Tpkg
|
|
1483
1487
|
elsif line =~ /^\s*$/
|
1484
1488
|
pkg = pkg_for_native_package(name, version, package_version, yum[:source])
|
1485
1489
|
native_packages << pkg
|
1486
|
-
name = version = package_version = nil
|
1490
|
+
name = version = package_version = nil
|
1487
1491
|
end
|
1488
1492
|
# In the end we ignore the architecture. Anything that
|
1489
1493
|
# shows up in yum should be installable on this box, and
|
@@ -1783,7 +1787,7 @@ class Tpkg
|
|
1783
1787
|
|
1784
1788
|
if File.directory?(@metadata_directory)
|
1785
1789
|
Dir.foreach(@metadata_directory) do |entry|
|
1786
|
-
next if entry == '.' || entry == '..'
|
1790
|
+
next if entry == '.' || entry == '..'
|
1787
1791
|
next if package_files && !package_files.include?(entry)
|
1788
1792
|
file_metadata = FileMetadata::instantiate_from_dir(File.join(@metadata_directory, entry))
|
1789
1793
|
ret[file_metadata[:package_file]] = file_metadata
|
@@ -1802,8 +1806,8 @@ class Tpkg
|
|
1802
1806
|
else
|
1803
1807
|
pkgs = []
|
1804
1808
|
if req
|
1805
|
-
req = req.clone # we're using req as the key for our cache, so it's important
|
1806
|
-
# that we clone it here. Otherwise, req can be changed later on from
|
1809
|
+
req = req.clone # we're using req as the key for our cache, so it's important
|
1810
|
+
# that we clone it here. Otherwise, req can be changed later on from
|
1807
1811
|
# the calling method and modify our cache inadvertently
|
1808
1812
|
if req[:type] == :native
|
1809
1813
|
load_available_native_packages(req[:name])
|
@@ -1904,7 +1908,7 @@ class Tpkg
|
|
1904
1908
|
end
|
1905
1909
|
def normalize_path(path,root=nil,base=nil)
|
1906
1910
|
root ||= @file_system_root
|
1907
|
-
base ||= @base
|
1911
|
+
base ||= @base
|
1908
1912
|
if path[0,1] == File::SEPARATOR
|
1909
1913
|
normalized_path = File.join(root, path)
|
1910
1914
|
else
|
@@ -2257,7 +2261,7 @@ class Tpkg
|
|
2257
2261
|
end
|
2258
2262
|
|
2259
2263
|
def download(source, path, downloaddir = nil, use_cache = true)
|
2260
|
-
http =
|
2264
|
+
http = gethttp(URI.parse(source))
|
2261
2265
|
localdir = source_to_local_directory(source)
|
2262
2266
|
localpath = File.join(localdir, File.basename(path))
|
2263
2267
|
# Don't download again if file is already there from previous installation
|
@@ -2276,10 +2280,10 @@ class Tpkg
|
|
2276
2280
|
# If downloaddir is specified, then download to that directory. Otherwise,
|
2277
2281
|
# download to default source directory
|
2278
2282
|
localdir = downloaddir || localdir
|
2279
|
-
if !File.exist?(localdir)
|
2283
|
+
if !File.exist?(localdir)
|
2280
2284
|
FileUtils.mkdir_p(localdir)
|
2281
2285
|
end
|
2282
|
-
localpath = File.join(localdir, File.basename(path))
|
2286
|
+
localpath = File.join(localdir, File.basename(path))
|
2283
2287
|
end
|
2284
2288
|
uri = URI.join(source, path)
|
2285
2289
|
tmpfile = Tempfile.new(File.basename(localpath), File.dirname(localpath))
|
@@ -2319,7 +2323,7 @@ class Tpkg
|
|
2319
2323
|
installed_path = normalize_path(tpkg_path)
|
2320
2324
|
init_scripts[installed_path] = tpkgfile
|
2321
2325
|
end
|
2322
|
-
end
|
2326
|
+
end
|
2323
2327
|
init_scripts
|
2324
2328
|
end
|
2325
2329
|
|
@@ -2353,24 +2357,24 @@ class Tpkg
|
|
2353
2357
|
Tpkg::get_os =~ /Solaris/
|
2354
2358
|
init_directory = File.join(@file_system_root, 'etc')
|
2355
2359
|
end
|
2356
|
-
|
2360
|
+
|
2357
2361
|
# in case user specify levels in yaml as string/integer instead of array
|
2358
2362
|
if !levels.kind_of?(Array)
|
2359
2363
|
levels = levels.to_s.split(//)
|
2360
2364
|
end
|
2361
|
-
|
2365
|
+
|
2362
2366
|
levels.each do |level|
|
2363
2367
|
links[File.join(init_directory, "rc#{level}.d", 'S' + start.to_s + File.basename(installed_path))] = installed_path
|
2364
2368
|
end
|
2365
2369
|
elsif Tpkg::get_os =~ /FreeBSD/
|
2366
|
-
init_directory = File.join(@file_system_root, 'usr', 'local', 'etc', 'rc.d')
|
2370
|
+
init_directory = File.join(@file_system_root, 'usr', 'local', 'etc', 'rc.d')
|
2367
2371
|
if tpkgfile[:init][:levels] && tpkgfile[:init][:levels].empty?
|
2368
2372
|
# User doesn't want the init script linked in to auto-start
|
2369
2373
|
else
|
2370
2374
|
links[File.join(init_directory, File.basename(installed_path))] = installed_path
|
2371
2375
|
end
|
2372
2376
|
else
|
2373
|
-
|
2377
|
+
warn "No init script support for #{Tpkg::get_os}"
|
2374
2378
|
end
|
2375
2379
|
end
|
2376
2380
|
links
|
@@ -2380,12 +2384,12 @@ class Tpkg
|
|
2380
2384
|
# package and where they need to be installed on the system
|
2381
2385
|
def crontab_destinations(metadata)
|
2382
2386
|
destinations = {}
|
2383
|
-
|
2387
|
+
|
2384
2388
|
# Don't do anything unless we have to
|
2385
2389
|
unless metadata[:files] && metadata[:files][:files]
|
2386
2390
|
return destinations
|
2387
2391
|
end
|
2388
|
-
|
2392
|
+
|
2389
2393
|
metadata[:files][:files].each do |tpkgfile|
|
2390
2394
|
if tpkgfile[:crontab]
|
2391
2395
|
tpkg_path = tpkgfile[:path]
|
@@ -2429,7 +2433,7 @@ class Tpkg
|
|
2429
2433
|
destinations[installed_path][:link] = File.join(@file_system_root, 'etc', 'cron.d', File.basename(installed_path))
|
2430
2434
|
end
|
2431
2435
|
else
|
2432
|
-
|
2436
|
+
warn "No crontab support for #{Tpkg::get_os}"
|
2433
2437
|
end
|
2434
2438
|
end
|
2435
2439
|
end
|
@@ -2479,7 +2483,7 @@ class Tpkg
|
|
2479
2483
|
def unpack(package_file, options={})
|
2480
2484
|
ret_val = 0
|
2481
2485
|
|
2482
|
-
# set env variable to let pre/post install know whether this unpack
|
2486
|
+
# set env variable to let pre/post install know whether this unpack
|
2483
2487
|
# is part of an install or upgrade
|
2484
2488
|
if options[:is_doing_upgrade]
|
2485
2489
|
ENV['TPKG_ACTION'] = "upgrade"
|
@@ -2503,7 +2507,7 @@ class Tpkg
|
|
2503
2507
|
|
2504
2508
|
metadata = Tpkg::metadata_from_package(package_file, {:topleveldir => topleveldir})
|
2505
2509
|
|
2506
|
-
# Get list of files/directories that already exist in the system. Store their perm/ownership.
|
2510
|
+
# Get list of files/directories that already exist in the system. Store their perm/ownership.
|
2507
2511
|
# That way, when we copy over the new files, we can set the new files to have the same perm/owernship.
|
2508
2512
|
conflicting_files = {}
|
2509
2513
|
fip = Tpkg::files_in_package(package_file)
|
@@ -2539,7 +2543,7 @@ class Tpkg
|
|
2539
2543
|
end
|
2540
2544
|
|
2541
2545
|
# Set default dir uid/gid to be same as for file.
|
2542
|
-
default_dir_uid = default_uid
|
2546
|
+
default_dir_uid = default_uid
|
2543
2547
|
default_dir_gid = default_gid
|
2544
2548
|
default_dir_perms = 0755
|
2545
2549
|
|
@@ -2565,7 +2569,7 @@ class Tpkg
|
|
2565
2569
|
if File.directory?(f)
|
2566
2570
|
File.chown(default_dir_uid, default_dir_gid, f)
|
2567
2571
|
else
|
2568
|
-
File.chown(default_uid, default_gid, f)
|
2572
|
+
File.chown(default_uid, default_gid, f)
|
2569
2573
|
end
|
2570
2574
|
rescue Errno::EPERM
|
2571
2575
|
raise if Process.euid == 0
|
@@ -2637,21 +2641,21 @@ class Tpkg
|
|
2637
2641
|
(1..3).each do | i |
|
2638
2642
|
begin
|
2639
2643
|
Tpkg::decrypt(metadata[:name], working_path, options[:passphrase], *([tpkgfile[:encrypt][:algorithm]].compact))
|
2640
|
-
break
|
2644
|
+
break
|
2641
2645
|
rescue OpenSSL::CipherError
|
2642
2646
|
@@passphrase = nil
|
2643
2647
|
if i == 3
|
2644
|
-
raise "Incorrect passphrase."
|
2648
|
+
raise "Incorrect passphrase."
|
2645
2649
|
else
|
2646
2650
|
puts "Incorrect passphrase. Try again."
|
2647
2651
|
end
|
2648
2652
|
end
|
2649
2653
|
end
|
2650
2654
|
|
2651
|
-
if File.file?(working_path)
|
2655
|
+
if File.file?(working_path)
|
2652
2656
|
digest = Digest::SHA256.hexdigest(File.read(working_path))
|
2653
2657
|
# get checksum for the decrypted file. Will be used for creating file_metadata
|
2654
|
-
checksums_of_decrypted_files[File.expand_path(tpkg_path)] = digest
|
2658
|
+
checksums_of_decrypted_files[File.expand_path(tpkg_path)] = digest
|
2655
2659
|
end
|
2656
2660
|
end
|
2657
2661
|
end
|
@@ -2786,11 +2790,14 @@ class Tpkg
|
|
2786
2790
|
|
2787
2791
|
def install_crontabs(metadata)
|
2788
2792
|
crontab_destinations(metadata).each do |crontab, destination|
|
2793
|
+
# FIXME: Besides the regex being ugly it is also only going to match on
|
2794
|
+
# Linux, need to figure out if there's a reason for that or if this can
|
2795
|
+
# be made more generic.
|
2789
2796
|
if !@sudo && (destination[destination.keys.first] =~ /\/var\/spool\/cron/)
|
2790
|
-
install_crontab_bycmd(metadata, crontab, destination)
|
2797
|
+
install_crontab_bycmd(metadata, crontab, destination)
|
2791
2798
|
next
|
2792
2799
|
end
|
2793
|
-
|
2800
|
+
|
2794
2801
|
begin
|
2795
2802
|
if destination[:link]
|
2796
2803
|
install_crontab_link(metadata, crontab, destination)
|
@@ -2844,6 +2851,7 @@ class Tpkg
|
|
2844
2851
|
end
|
2845
2852
|
end
|
2846
2853
|
end
|
2854
|
+
# FIXME: Can this be replaced by install_crontab_bycmd?
|
2847
2855
|
def install_crontab_file(metadata, crontab, destination)
|
2848
2856
|
if !File.exist?(File.dirname(destination[:file]))
|
2849
2857
|
FileUtils.mkdir_p(File.dirname(destination[:file]))
|
@@ -2892,18 +2900,21 @@ class Tpkg
|
|
2892
2900
|
tmpfh.write(oldcron) unless oldcron.empty?
|
2893
2901
|
tmpfh.puts "### TPKG START - #{@base} - #{File.basename(metadata[:filename].to_s)}"
|
2894
2902
|
tmpfh.write File.readlines(crontab)
|
2895
|
-
tmpfh.puts "### TPKG END - #{@base} - #{File.basename(metadata[:filename].to_s)}"
|
2903
|
+
tmpfh.puts "### TPKG END - #{@base} - #{File.basename(metadata[:filename].to_s)}"
|
2896
2904
|
tmpfh.close
|
2897
2905
|
`crontab #{tmpf}`
|
2898
2906
|
FileUtils.rm(tmpf)
|
2899
2907
|
end
|
2900
2908
|
def remove_crontabs(metadata)
|
2901
2909
|
crontab_destinations(metadata).each do |crontab, destination|
|
2910
|
+
# FIXME: Besides the regex being ugly it is also only going to match on
|
2911
|
+
# Linux, need to figure out if there's a reason for that or if this can
|
2912
|
+
# be made more generic.
|
2902
2913
|
if !@sudo && (destination[destination.keys.first] =~ /\/var\/spool\/cron/)
|
2903
|
-
remove_crontab_bycmd(metadata, crontab, destination)
|
2914
|
+
remove_crontab_bycmd(metadata, crontab, destination)
|
2904
2915
|
next
|
2905
2916
|
end
|
2906
|
-
|
2917
|
+
|
2907
2918
|
begin
|
2908
2919
|
if destination[:link]
|
2909
2920
|
remove_crontab_link(metadata, crontab, destination)
|
@@ -2935,6 +2946,7 @@ class Tpkg
|
|
2935
2946
|
end
|
2936
2947
|
end
|
2937
2948
|
end
|
2949
|
+
# FIXME: Can this be replaced by remove_crontab_bycmd?
|
2938
2950
|
def remove_crontab_file(metadata, crontab, destination)
|
2939
2951
|
if File.exist?(destination[:file])
|
2940
2952
|
tmpfile = Tempfile.new(File.basename(destination[:file]), File.dirname(destination[:file]))
|
@@ -3099,8 +3111,8 @@ class Tpkg
|
|
3099
3111
|
if file_metadata
|
3100
3112
|
file_metadata[:package_file] = File.basename(package_file)
|
3101
3113
|
file_metadata[:files].each do |file|
|
3102
|
-
# update file_metadata with user/group ownership and permission
|
3103
|
-
acl = files_info[file[:path]]
|
3114
|
+
# update file_metadata with user/group ownership and permission
|
3115
|
+
acl = files_info[file[:path]]
|
3104
3116
|
file.merge!(acl) unless acl.nil?
|
3105
3117
|
|
3106
3118
|
# update file_metadata with the checksums of decrypted files
|
@@ -3212,7 +3224,7 @@ class Tpkg
|
|
3212
3224
|
else # basic package specs ('foo' or 'foo=1.0')
|
3213
3225
|
puts "parse_requests request looks like package spec" if @@debug
|
3214
3226
|
|
3215
|
-
# Tpkg::parse_request is a class method and doesn't know where packages are installed.
|
3227
|
+
# Tpkg::parse_request is a class method and doesn't know where packages are installed.
|
3216
3228
|
# So we have to tell it ourselves.
|
3217
3229
|
req = Tpkg::parse_request(request, @installed_directory)
|
3218
3230
|
newreqs << req
|
@@ -3230,14 +3242,14 @@ class Tpkg
|
|
3230
3242
|
|
3231
3243
|
# After calling parse_request, we should call this method
|
3232
3244
|
# to check whether or not we can meet the requirements/dependencies
|
3233
|
-
# of the result packages
|
3245
|
+
# of the result packages
|
3234
3246
|
def check_requests(packages)
|
3235
3247
|
all_requests_satisfied = true # whether or not all requests can be satisfied
|
3236
3248
|
errors = [""]
|
3237
3249
|
packages.each do |name, pkgs|
|
3238
3250
|
if pkgs.empty?
|
3239
3251
|
errors << ["Unable to find any packages which satisfy #{name}"]
|
3240
|
-
all_requests_satisfied = false
|
3252
|
+
all_requests_satisfied = false
|
3241
3253
|
next
|
3242
3254
|
end
|
3243
3255
|
|
@@ -3268,7 +3280,7 @@ class Tpkg
|
|
3268
3280
|
if !request_satisfied
|
3269
3281
|
errors << ["Unable to find any packages which satisfy #{name}. Possible error(s):"]
|
3270
3282
|
errors << possible_errors
|
3271
|
-
all_requests_satisfied = false
|
3283
|
+
all_requests_satisfied = false
|
3272
3284
|
end
|
3273
3285
|
end
|
3274
3286
|
|
@@ -3373,12 +3385,12 @@ class Tpkg
|
|
3373
3385
|
puts "The package(s) you're trying to install conflict with the following package(s):"
|
3374
3386
|
conflicting_pkgs = conflicting_pkgs.collect{|pkg|pkg[:metadata][:filename]}
|
3375
3387
|
puts conflicting_pkgs.join("\n")
|
3376
|
-
if options[:force_replace]
|
3388
|
+
if options[:force_replace]
|
3377
3389
|
puts "Attemping to replace the conflicting packages."
|
3378
3390
|
success = remove(conflicting_pkgs)
|
3379
3391
|
return success
|
3380
3392
|
else
|
3381
|
-
puts "Try removing the conflicting package(s) first, or rerun tpkg with the --force-replace option."
|
3393
|
+
puts "Try removing the conflicting package(s) first, or rerun tpkg with the --force-replace option."
|
3382
3394
|
return false
|
3383
3395
|
end
|
3384
3396
|
end
|
@@ -3442,7 +3454,7 @@ class Tpkg
|
|
3442
3454
|
end
|
3443
3455
|
true
|
3444
3456
|
end
|
3445
|
-
|
3457
|
+
|
3446
3458
|
# See parse_requests for format of requests
|
3447
3459
|
def install(requests, passphrase=nil, options={})
|
3448
3460
|
ret_val = 0
|
@@ -3451,7 +3463,7 @@ class Tpkg
|
|
3451
3463
|
lock
|
3452
3464
|
parse_requests(requests, requirements, packages)
|
3453
3465
|
check_requests(packages)
|
3454
|
-
|
3466
|
+
|
3455
3467
|
core_packages = []
|
3456
3468
|
requirements.each do |req|
|
3457
3469
|
core_packages << req[:name] if !core_packages.include?(req[:name])
|
@@ -3461,7 +3473,6 @@ class Tpkg
|
|
3461
3473
|
puts "install requirements: #{requirements.inspect}" if @@debug
|
3462
3474
|
puts "install packages: #{packages.inspect}" if @@debug
|
3463
3475
|
puts "install core_packages: #{core_packages.inspect}" if @@debug
|
3464
|
-
#solution_packages = best_solution(requirements.dup, packages.dup)
|
3465
3476
|
solution_packages = best_solution(requirements, packages, core_packages)
|
3466
3477
|
if !solution_packages
|
3467
3478
|
raise "Unable to resolve dependencies. Try running with --debug for more info"
|
@@ -3469,26 +3480,26 @@ class Tpkg
|
|
3469
3480
|
|
3470
3481
|
success = handle_conflicting_pkgs(installed_packages, solution_packages, options)
|
3471
3482
|
return false if !success
|
3472
|
-
|
3483
|
+
|
3473
3484
|
if !prompt_for_install(solution_packages, 'installed')
|
3474
3485
|
unlock
|
3475
3486
|
return false
|
3476
3487
|
end
|
3477
|
-
|
3488
|
+
|
3478
3489
|
# Build an array of metadata of pkgs that are already installed
|
3479
3490
|
# We will use this later on to figure out what new packages have been installed/removed
|
3480
3491
|
# in order to report back to the server
|
3481
3492
|
already_installed_pkgs = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
3482
|
-
|
3493
|
+
|
3483
3494
|
# Create array of packages (names) we have installed so far
|
3484
3495
|
# We will use it later on to determine the order of how to install the packages
|
3485
|
-
installed_so_far = installed_packages.collect{|pkg| pkg[:metadata][:name]}
|
3486
|
-
|
3496
|
+
installed_so_far = installed_packages.collect{|pkg| pkg[:metadata][:name]}
|
3497
|
+
|
3487
3498
|
while pkg = solution_packages.shift
|
3488
3499
|
# get dependencies and make sure we install the packages in the correct order
|
3489
3500
|
# based on the dependencies
|
3490
3501
|
dependencies = nil
|
3491
|
-
if pkg[:metadata][:dependencies]
|
3502
|
+
if pkg[:metadata][:dependencies]
|
3492
3503
|
dependencies = pkg[:metadata][:dependencies].collect { |dep| dep[:name] }.compact
|
3493
3504
|
# don't install this pkg right now if its dependencies haven't been installed
|
3494
3505
|
if !dependencies.empty? && !dependencies.to_set.subset?(installed_so_far.to_set)
|
@@ -3496,7 +3507,7 @@ class Tpkg
|
|
3496
3507
|
next
|
3497
3508
|
end
|
3498
3509
|
end
|
3499
|
-
|
3510
|
+
|
3500
3511
|
if pkg[:source] == :currently_installed ||
|
3501
3512
|
pkg[:source] == :native_installed
|
3502
3513
|
# Nothing to do for packages currently installed
|
@@ -3583,17 +3594,17 @@ class Tpkg
|
|
3583
3594
|
end
|
3584
3595
|
end
|
3585
3596
|
end
|
3586
|
-
|
3587
|
-
# If we're down here, it means we have installed the package. So go ahead and
|
3597
|
+
|
3598
|
+
# If we're down here, it means we have installed the package. So go ahead and
|
3588
3599
|
# update the list of packages we installed so far
|
3589
3600
|
installed_so_far << pkg[:metadata][:name]
|
3590
3601
|
end # end while loop
|
3591
|
-
|
3602
|
+
|
3592
3603
|
# log changes
|
3593
3604
|
currently_installed = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
3594
3605
|
newly_installed = currently_installed - already_installed_pkgs
|
3595
3606
|
log_changes({:newly_installed => newly_installed})
|
3596
|
-
|
3607
|
+
|
3597
3608
|
# send udpate back to reporting server
|
3598
3609
|
unless @report_server.nil?
|
3599
3610
|
options = {:newly_installed => newly_installed, :currently_installed => currently_installed}
|
@@ -3602,7 +3613,7 @@ class Tpkg
|
|
3602
3613
|
unlock
|
3603
3614
|
return ret_val
|
3604
3615
|
end
|
3605
|
-
|
3616
|
+
|
3606
3617
|
# This method can also be used for doing downgrade
|
3607
3618
|
def upgrade(requests=nil, passphrase=nil, options={})
|
3608
3619
|
downgrade = options[:downgrade] || false
|
@@ -3626,14 +3637,14 @@ class Tpkg
|
|
3626
3637
|
additional_requirements = []
|
3627
3638
|
requirements.each do |req|
|
3628
3639
|
core_packages << req[:name] if !core_packages.include?(req[:name])
|
3629
|
-
|
3640
|
+
|
3630
3641
|
# When doing downgrade, we don't want to include the package being
|
3631
3642
|
# downgrade as the requirements. Otherwise, we won't be able to downgrade it
|
3632
|
-
unless downgrade
|
3643
|
+
unless downgrade
|
3633
3644
|
additional_requirements.concat(
|
3634
3645
|
requirements_for_currently_installed_package(req[:name]))
|
3635
3646
|
end
|
3636
|
-
|
3647
|
+
|
3637
3648
|
# Initialize the list of possible packages for this req
|
3638
3649
|
if !packages[req[:name]]
|
3639
3650
|
packages[req[:name]] = available_packages_that_meet_requirement(req)
|
@@ -3644,7 +3655,7 @@ class Tpkg
|
|
3644
3655
|
pkg[:prefer] = false
|
3645
3656
|
end
|
3646
3657
|
end
|
3647
|
-
|
3658
|
+
|
3648
3659
|
# Look for pkgs that might depend on the pkg we're upgrading,
|
3649
3660
|
# and add them to our list of requirements. We need to make sure that we can still
|
3650
3661
|
# satisfy the dependency requirements if we were to do the upgrade.
|
@@ -3682,16 +3693,15 @@ class Tpkg
|
|
3682
3693
|
puts "upgrade requirements: #{requirements.inspect}" if @@debug
|
3683
3694
|
puts "upgrade packages: #{packages.inspect}" if @@debug
|
3684
3695
|
puts "upgrade core_packages: #{core_packages.inspect}" if @@debug
|
3685
|
-
#solution_packages = best_solution(requirements.dup, packages.dup)
|
3686
3696
|
solution_packages = best_solution(requirements, packages, core_packages)
|
3687
|
-
|
3697
|
+
|
3688
3698
|
if solution_packages.nil?
|
3689
3699
|
raise "Unable to find solution for upgrading. Please verify that you specified the correct package(s) for upgrade. Try running with --debug for more info"
|
3690
3700
|
end
|
3691
|
-
|
3701
|
+
|
3692
3702
|
success = handle_conflicting_pkgs(installed_packages, solution_packages, options)
|
3693
3703
|
return false if !success
|
3694
|
-
|
3704
|
+
|
3695
3705
|
if downgrade
|
3696
3706
|
prompt_action = 'downgraded'
|
3697
3707
|
else
|
@@ -3701,7 +3711,7 @@ class Tpkg
|
|
3701
3711
|
unlock
|
3702
3712
|
return false
|
3703
3713
|
end
|
3704
|
-
|
3714
|
+
|
3705
3715
|
# Build an array of metadata of pkgs that are already installed
|
3706
3716
|
# We will use this later on to figure out what new packages have been installed/removed
|
3707
3717
|
# in order to report back to the server
|
@@ -3842,7 +3852,7 @@ class Tpkg
|
|
3842
3852
|
end
|
3843
3853
|
end
|
3844
3854
|
|
3845
|
-
# log changes
|
3855
|
+
# log changes
|
3846
3856
|
currently_installed = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
3847
3857
|
newly_installed = currently_installed - already_installed_pkgs
|
3848
3858
|
removed = already_installed_pkgs - currently_installed
|
@@ -3851,7 +3861,7 @@ class Tpkg
|
|
3851
3861
|
# send update back to reporting server
|
3852
3862
|
if !has_updates
|
3853
3863
|
puts "No updates available"
|
3854
|
-
elsif !@report_server.nil?
|
3864
|
+
elsif !@report_server.nil?
|
3855
3865
|
options = {:newly_installed => newly_installed, :removed => removed,
|
3856
3866
|
:currently_installed => currently_installed}
|
3857
3867
|
send_update_to_server(options)
|
@@ -3882,12 +3892,12 @@ class Tpkg
|
|
3882
3892
|
unlock
|
3883
3893
|
return false
|
3884
3894
|
end
|
3885
|
-
|
3895
|
+
|
3886
3896
|
# Build an array of metadata of pkgs that are already installed
|
3887
3897
|
# We will use this later on to figure out what new packages have been installed/removed
|
3888
3898
|
# in order to report back to the server
|
3889
3899
|
already_installed_pkgs = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
3890
|
-
|
3900
|
+
|
3891
3901
|
# If user want to remove all the dependent pkgs, then go ahead
|
3892
3902
|
# and include them in our array of things to remove
|
3893
3903
|
if options[:remove_all_dep]
|
@@ -3897,7 +3907,7 @@ class Tpkg
|
|
3897
3907
|
# Get list of dependency prerequisites
|
3898
3908
|
ptr = packages_to_remove | get_prerequisites(packages_to_remove)
|
3899
3909
|
pkg_files_to_remove = ptr.map { |pkg| pkg[:metadata][:filename] }
|
3900
|
-
|
3910
|
+
|
3901
3911
|
# see if any other packages depends on the ones we're about to remove
|
3902
3912
|
# If so, we can't remove that package + any of its prerequisites
|
3903
3913
|
non_removable_pkg_files = []
|
@@ -3959,7 +3969,7 @@ class Tpkg
|
|
3959
3969
|
end
|
3960
3970
|
unless Tpkg::confirm
|
3961
3971
|
unlock
|
3962
|
-
return false
|
3972
|
+
return false
|
3963
3973
|
end
|
3964
3974
|
end
|
3965
3975
|
|
@@ -3977,19 +3987,19 @@ class Tpkg
|
|
3977
3987
|
packages_to_remove.each do |pkg|
|
3978
3988
|
pkgname = pkg[:metadata][:name]
|
3979
3989
|
package_file = File.join(@installed_directory, pkg[:metadata][:filename])
|
3980
|
-
|
3990
|
+
|
3981
3991
|
topleveldir = Tpkg::package_toplevel_directory(package_file)
|
3982
3992
|
workdir = Tpkg::tempdir(topleveldir, @tmp_directory)
|
3983
3993
|
extract_tpkg_tar_command = Tpkg::cmd_to_extract_tpkg_tar(package_file, topleveldir)
|
3984
3994
|
system("#{extract_tpkg_tar_command} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
|
3985
|
-
|
3995
|
+
|
3986
3996
|
# Run preremove script
|
3987
3997
|
if File.exist?(File.join(workdir, 'tpkg', 'preremove'))
|
3988
3998
|
pwd = Dir.pwd
|
3989
3999
|
# chdir into the working directory so that the user can specify a
|
3990
4000
|
# relative path to their file/script.
|
3991
4001
|
Dir.chdir(File.join(workdir, 'tpkg'))
|
3992
|
-
|
4002
|
+
|
3993
4003
|
# Warn the user about non-executable files, as system will just
|
3994
4004
|
# silently fail and exit if that's the case.
|
3995
4005
|
if !File.executable?(File.join(workdir, 'tpkg', 'preremove'))
|
@@ -4000,7 +4010,7 @@ class Tpkg
|
|
4000
4010
|
else
|
4001
4011
|
system(File.join(workdir, 'tpkg', 'preremove')) || raise("Error: preremove for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
|
4002
4012
|
end
|
4003
|
-
|
4013
|
+
|
4004
4014
|
# Switch back to our previous directory
|
4005
4015
|
Dir.chdir(pwd)
|
4006
4016
|
end
|
@@ -4014,14 +4024,14 @@ class Tpkg
|
|
4014
4024
|
run_external(pkg[:metadata][:filename], :remove, external[:name], external[:data])
|
4015
4025
|
end
|
4016
4026
|
end if pkg[:metadata][:externals]
|
4017
|
-
|
4027
|
+
|
4018
4028
|
# determine which configuration files have been modified
|
4019
4029
|
modified_conf_files = []
|
4020
4030
|
file_metadata = file_metadata_for_installed_packages([pkg[:metadata][:filename]]).values[0]
|
4021
4031
|
file_metadata[:files].each do |file|
|
4022
4032
|
if file[:config]
|
4023
4033
|
# get expected checksum. For files that were encrypted, we're interested in the
|
4024
|
-
# checksum of the decrypted version
|
4034
|
+
# checksum of the decrypted version
|
4025
4035
|
chksum_expected = file[:checksum][:digests].first[:value]
|
4026
4036
|
file[:checksum][:digests].each do | digest |
|
4027
4037
|
if digest[:decrypted] == true
|
@@ -4035,7 +4045,7 @@ class Tpkg
|
|
4035
4045
|
end
|
4036
4046
|
end
|
4037
4047
|
end if file_metadata
|
4038
|
-
|
4048
|
+
|
4039
4049
|
# Remove files
|
4040
4050
|
files_to_remove = conflicting_files(package_file, CHECK_REMOVE)
|
4041
4051
|
# Reverse the order of the files, as directories will appear first
|
@@ -4065,14 +4075,14 @@ class Tpkg
|
|
4065
4075
|
warn "Failed to remove file #{file}."
|
4066
4076
|
end
|
4067
4077
|
end
|
4068
|
-
|
4078
|
+
|
4069
4079
|
# Run postremove script
|
4070
4080
|
if File.exist?(File.join(workdir, 'tpkg', 'postremove'))
|
4071
4081
|
pwd = Dir.pwd
|
4072
4082
|
# chdir into the working directory so that the user can specify a
|
4073
4083
|
# relative path to their file/script.
|
4074
4084
|
Dir.chdir(File.join(workdir, 'tpkg'))
|
4075
|
-
|
4085
|
+
|
4076
4086
|
# Warn the user about non-executable files, as system will just
|
4077
4087
|
# silently fail and exit if that's the case.
|
4078
4088
|
if !File.executable?(File.join(workdir, 'tpkg', 'postremove'))
|
@@ -4085,36 +4095,36 @@ class Tpkg
|
|
4085
4095
|
# desirable. We could reinstall the package's files and raise an
|
4086
4096
|
# exception, but this seems the best approach to me.
|
4087
4097
|
system(File.join(workdir, 'tpkg', 'postremove')) || warn("Warning: postremove for #{File.basename(package_file)} failed with exit value #{$?.exitstatus}")
|
4088
|
-
ret_val = POSTREMOVE_ERR if
|
4089
|
-
|
4098
|
+
ret_val = POSTREMOVE_ERR if !$?.success?
|
4099
|
+
|
4090
4100
|
# Switch back to our previous directory
|
4091
4101
|
Dir.chdir(pwd)
|
4092
4102
|
end
|
4093
|
-
|
4103
|
+
|
4094
4104
|
File.delete(package_file)
|
4095
|
-
|
4105
|
+
|
4096
4106
|
# delete metadata dir of this package
|
4097
4107
|
package_metadata_dir = File.join(@metadata_directory, File.basename(package_file, File.extname(package_file)))
|
4098
4108
|
FileUtils.rm_rf(package_metadata_dir)
|
4099
|
-
|
4109
|
+
|
4100
4110
|
# remove native dependency stub packages if needed
|
4101
4111
|
remove_native_stub_pkg(pkg)
|
4102
|
-
|
4112
|
+
|
4103
4113
|
# Cleanup
|
4104
4114
|
FileUtils.rm_rf(workdir)
|
4105
4115
|
end
|
4106
|
-
|
4107
|
-
# log changes
|
4116
|
+
|
4117
|
+
# log changes
|
4108
4118
|
currently_installed = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
4109
4119
|
removed = already_installed_pkgs - currently_installed
|
4110
4120
|
log_changes({:removed => removed})
|
4111
|
-
|
4121
|
+
|
4112
4122
|
# send update back to reporting server
|
4113
4123
|
unless @report_server.nil? || options[:upgrade]
|
4114
4124
|
options = {:removed => removed, :currently_installed => currently_installed}
|
4115
4125
|
send_update_to_server(options)
|
4116
4126
|
end
|
4117
|
-
|
4127
|
+
|
4118
4128
|
unlock
|
4119
4129
|
return ret_val
|
4120
4130
|
end
|
@@ -4126,16 +4136,16 @@ class Tpkg
|
|
4126
4136
|
requests.each do |request|
|
4127
4137
|
req = Tpkg::parse_request(request)
|
4128
4138
|
packages.concat(installed_packages_that_meet_requirement(req).collect { |pkg| pkg[:metadata][:filename] })
|
4129
|
-
end
|
4130
|
-
|
4139
|
+
end
|
4140
|
+
|
4131
4141
|
# loop through each package, and verify checksum, owner, group and perm of each file that was installed
|
4132
4142
|
packages.each do | package_file |
|
4133
4143
|
puts "Verifying #{package_file}"
|
4134
4144
|
package_full_name = File.basename(package_file, File.extname(package_file))
|
4135
|
-
|
4145
|
+
|
4136
4146
|
# Extract checksum.xml from the package
|
4137
4147
|
checksum_xml = nil
|
4138
|
-
|
4148
|
+
|
4139
4149
|
# get file_metadata from the installed package
|
4140
4150
|
file_metadata = FileMetadata::instantiate_from_dir(File.join(@metadata_directory, package_full_name))
|
4141
4151
|
if !file_metadata
|
@@ -4144,15 +4154,15 @@ class Tpkg
|
|
4144
4154
|
results[package_file] = errors
|
4145
4155
|
return results
|
4146
4156
|
end
|
4147
|
-
|
4148
|
-
# verify installed files match their checksum
|
4157
|
+
|
4158
|
+
# verify installed files match their checksum
|
4149
4159
|
file_metadata[:files].each do |file|
|
4150
4160
|
errors = []
|
4151
4161
|
gid_expected, uid_expected, perms_expected, chksum_expected = nil
|
4152
4162
|
fp = file[:path]
|
4153
|
-
|
4163
|
+
|
4154
4164
|
# get expected checksum. For files that were encrypted, we're interested in the
|
4155
|
-
# checksum of the decrypted version
|
4165
|
+
# checksum of the decrypted version
|
4156
4166
|
if file[:checksum]
|
4157
4167
|
chksum_expected = file[:checksum][:digests].first[:value]
|
4158
4168
|
file[:checksum][:digests].each do | digest |
|
@@ -4161,7 +4171,7 @@ class Tpkg
|
|
4161
4171
|
end
|
4162
4172
|
end
|
4163
4173
|
end
|
4164
|
-
|
4174
|
+
|
4165
4175
|
# get expected acl values
|
4166
4176
|
if file[:uid]
|
4167
4177
|
uid_expected = file[:uid].to_i
|
@@ -4171,30 +4181,30 @@ class Tpkg
|
|
4171
4181
|
end
|
4172
4182
|
if file[:perms]
|
4173
4183
|
perms_expected = file[:perms].to_s
|
4174
|
-
end
|
4175
|
-
|
4184
|
+
end
|
4185
|
+
|
4176
4186
|
fp = normalize_path(fp)
|
4177
|
-
|
4187
|
+
|
4178
4188
|
# can't handle symlink
|
4179
4189
|
if File.symlink?(fp)
|
4180
4190
|
next
|
4181
4191
|
end
|
4182
|
-
|
4192
|
+
|
4183
4193
|
# check if file exist
|
4184
4194
|
if !File.exists?(fp)
|
4185
4195
|
errors << "File is missing"
|
4186
|
-
else
|
4187
|
-
# get actual values
|
4196
|
+
else
|
4197
|
+
# get actual values
|
4188
4198
|
chksum_actual = Digest::SHA256.hexdigest(File.read(fp)) if File.file?(fp)
|
4189
4199
|
uid_actual = File.stat(fp).uid
|
4190
4200
|
gid_actual = File.stat(fp).gid
|
4191
4201
|
perms_actual = File.stat(fp).mode.to_s(8)
|
4192
4202
|
end
|
4193
|
-
|
4203
|
+
|
4194
4204
|
if !chksum_expected.nil? && !chksum_actual.nil? && chksum_expected != chksum_actual
|
4195
4205
|
errors << "Checksum doesn't match (Expected: #{chksum_expected}, Actual: #{chksum_actual}"
|
4196
|
-
end
|
4197
|
-
|
4206
|
+
end
|
4207
|
+
|
4198
4208
|
if !uid_expected.nil? && !uid_actual.nil? && uid_expected != uid_actual
|
4199
4209
|
errors << "uid doesn't match (Expected: #{uid_expected}, Actual: #{uid_actual}) "
|
4200
4210
|
end
|
@@ -4212,7 +4222,7 @@ class Tpkg
|
|
4212
4222
|
end
|
4213
4223
|
return results
|
4214
4224
|
end
|
4215
|
-
|
4225
|
+
|
4216
4226
|
def execute_init(options, *moreoptions)
|
4217
4227
|
ret_val = 0
|
4218
4228
|
packages_to_execute_on = []
|
@@ -4224,7 +4234,7 @@ class Tpkg
|
|
4224
4234
|
action = moreoptions[0]
|
4225
4235
|
requested_packages = options
|
4226
4236
|
end
|
4227
|
-
|
4237
|
+
|
4228
4238
|
# if user specified no packages, then assume all
|
4229
4239
|
if requested_packages.nil?
|
4230
4240
|
packages_to_execute_on = installed_packages_that_meet_requirement(nil)
|
@@ -4235,20 +4245,20 @@ class Tpkg
|
|
4235
4245
|
packages_to_execute_on.concat(installed_packages_that_meet_requirement(req))
|
4236
4246
|
end
|
4237
4247
|
end
|
4238
|
-
|
4248
|
+
|
4239
4249
|
if packages_to_execute_on.empty?
|
4240
4250
|
warn "Warning: Unable to find package(s) \"#{requested_packages.join(",")}\"."
|
4241
|
-
else
|
4251
|
+
else
|
4242
4252
|
packages_to_execute_on.each do |pkg|
|
4243
4253
|
ret_val |= execute_init_for_package(pkg, action, requested_init_scripts)
|
4244
|
-
end
|
4254
|
+
end
|
4245
4255
|
end
|
4246
4256
|
return ret_val
|
4247
4257
|
end
|
4248
|
-
|
4258
|
+
|
4249
4259
|
def execute_init_for_package(pkg, action, requested_init_scripts = nil)
|
4250
4260
|
ret_val = 0
|
4251
|
-
|
4261
|
+
|
4252
4262
|
# Get init scripts metadata for the given package
|
4253
4263
|
init_scripts_metadata = init_scripts(pkg[:metadata])
|
4254
4264
|
# warn if there's no init script and then return
|
@@ -4256,7 +4266,7 @@ class Tpkg
|
|
4256
4266
|
warn "Warning: There is no init script for #{pkg[:metadata][:name]}."
|
4257
4267
|
return 1
|
4258
4268
|
end
|
4259
|
-
|
4269
|
+
|
4260
4270
|
# convert the init scripts metadata to an array of { path => value, start => value}
|
4261
4271
|
# so that we can order them based on their start value. This is necessary because
|
4262
4272
|
# we need to execute the init scripts in correct order.
|
@@ -4265,30 +4275,30 @@ class Tpkg
|
|
4265
4275
|
init = {}
|
4266
4276
|
init[:path] = installed_path
|
4267
4277
|
init[:start] = init_info[:init][:start] || 0
|
4268
|
-
|
4278
|
+
|
4269
4279
|
# if user requests specific init scripts, then only include those
|
4270
|
-
if requested_init_scripts.nil? or
|
4280
|
+
if requested_init_scripts.nil? or
|
4271
4281
|
requested_init_scripts && requested_init_scripts.include?(File.basename(installed_path))
|
4272
|
-
init_scripts << init
|
4282
|
+
init_scripts << init
|
4273
4283
|
end
|
4274
4284
|
end
|
4275
|
-
|
4285
|
+
|
4276
4286
|
if requested_init_scripts && init_scripts.empty?
|
4277
4287
|
warn "Warning: There are no init scripts that satisfy your request: #{requested_init_scripts.inspect} for package #{pkg[:metadata][:name]}."
|
4278
4288
|
end
|
4279
4289
|
|
4280
|
-
# Reverse order if doing stop.
|
4290
|
+
# Reverse order if doing stop.
|
4281
4291
|
if action == "stop"
|
4282
4292
|
ordered_init_scripts = init_scripts.sort{ |a,b| b[:start] <=> a[:start] }
|
4283
4293
|
else
|
4284
4294
|
ordered_init_scripts = init_scripts.sort{ |a,b| a[:start] <=> b[:start] }
|
4285
4295
|
end
|
4286
|
-
|
4296
|
+
|
4287
4297
|
ordered_init_scripts.each do |init_script|
|
4288
4298
|
installed_path = init_script[:path]
|
4289
4299
|
system("#{installed_path} #{action}")
|
4290
|
-
ret_val = INITSCRIPT_ERR if
|
4291
|
-
end
|
4300
|
+
ret_val = INITSCRIPT_ERR if !$?.success?
|
4301
|
+
end
|
4292
4302
|
return ret_val
|
4293
4303
|
end
|
4294
4304
|
|
@@ -4324,7 +4334,7 @@ class Tpkg
|
|
4324
4334
|
File.open(@lock_pid_file) { |file| lockpid = file.gets.chomp }
|
4325
4335
|
rescue Errno::ENOENT
|
4326
4336
|
end
|
4327
|
-
|
4337
|
+
|
4328
4338
|
# check that the process is actually running
|
4329
4339
|
# if not, clean up old lock and attemp to obtain lock again
|
4330
4340
|
if Tpkg::process_running?(lockpid)
|
@@ -4335,7 +4345,7 @@ class Tpkg
|
|
4335
4345
|
end
|
4336
4346
|
end
|
4337
4347
|
end
|
4338
|
-
|
4348
|
+
|
4339
4349
|
def unlock
|
4340
4350
|
if @locks == 0
|
4341
4351
|
warn "unlock called but not locked, that probably shouldn't happen"
|
@@ -4346,16 +4356,16 @@ class Tpkg
|
|
4346
4356
|
FileUtils.rm_rf(@lock_directory)
|
4347
4357
|
end
|
4348
4358
|
end
|
4349
|
-
|
4359
|
+
|
4350
4360
|
# Build a dependency map of currently installed packages
|
4351
|
-
# For example, if we have pkgB and pkgC which depends on pkgA, then
|
4361
|
+
# For example, if we have pkgB and pkgC which depends on pkgA, then
|
4352
4362
|
# the dependency map would look like this:
|
4353
4363
|
# "pkgA.tpkg" => [{pkgB metadata}, {pkgC metadata}]
|
4354
4364
|
def get_dependency_mapping
|
4355
4365
|
dependency_mapping = {}
|
4356
4366
|
installed_packages.each do | pkg |
|
4357
4367
|
metadata = pkg[:metadata]
|
4358
|
-
|
4368
|
+
|
4359
4369
|
# Get list of pkgs that this pkg depends on
|
4360
4370
|
next if metadata[:dependencies].nil?
|
4361
4371
|
depended_on = []
|
@@ -4363,16 +4373,16 @@ class Tpkg
|
|
4363
4373
|
next if req[:type] == :native
|
4364
4374
|
depended_on |= installed_packages_that_meet_requirement(req)
|
4365
4375
|
end
|
4366
|
-
|
4376
|
+
|
4367
4377
|
# populate the depencency map
|
4368
4378
|
depended_on.each do | req_pkg |
|
4369
|
-
dependency_mapping[req_pkg[:metadata][:filename]] ||= []
|
4379
|
+
dependency_mapping[req_pkg[:metadata][:filename]] ||= []
|
4370
4380
|
dependency_mapping[req_pkg[:metadata][:filename]] << pkg
|
4371
4381
|
end
|
4372
4382
|
end
|
4373
4383
|
return dependency_mapping
|
4374
4384
|
end
|
4375
|
-
|
4385
|
+
|
4376
4386
|
# Given a list of packages, return a list of dependents packages
|
4377
4387
|
def get_dependents(pkgs)
|
4378
4388
|
dependents = []
|
@@ -4383,10 +4393,10 @@ class Tpkg
|
|
4383
4393
|
next if pkgs.nil?
|
4384
4394
|
dependents |= pkgs
|
4385
4395
|
to_check |= pkgs.map { |pkg| pkg[:metadata][:filename] }
|
4386
|
-
end
|
4396
|
+
end
|
4387
4397
|
return dependents
|
4388
4398
|
end
|
4389
|
-
|
4399
|
+
|
4390
4400
|
# Given a list of packages, return a list of all their prerequisite dependencies
|
4391
4401
|
# Example: If pkgA depends on pkgB, and pkgB depends on pkgC, then calling this
|
4392
4402
|
# method on pkgA will returns pkgB and pkgC
|
@@ -4404,7 +4414,7 @@ class Tpkg
|
|
4404
4414
|
end
|
4405
4415
|
return pre_reqs
|
4406
4416
|
end
|
4407
|
-
|
4417
|
+
|
4408
4418
|
# print out history packages installation/remove
|
4409
4419
|
def installation_history
|
4410
4420
|
if !File.exists?(File.join(@log_directory,'changes.log'))
|
@@ -4412,15 +4422,15 @@ class Tpkg
|
|
4412
4422
|
return GENERIC_ERR
|
4413
4423
|
end
|
4414
4424
|
IO.foreach(File.join(@log_directory,'changes.log')) do |line|
|
4415
|
-
puts line
|
4425
|
+
puts line
|
4416
4426
|
end
|
4417
4427
|
end
|
4418
|
-
|
4428
|
+
|
4419
4429
|
# Download packages that meet the requests specified by the user.
|
4420
4430
|
# Packages are downloaded into the current directory or into the directory
|
4421
4431
|
# specified in options[:out]
|
4422
4432
|
def download_pkgs(requests, options={})
|
4423
|
-
if options[:out]
|
4433
|
+
if options[:out]
|
4424
4434
|
if !File.exists?(options[:out])
|
4425
4435
|
FileUtils.mkdir_p(options[:out])
|
4426
4436
|
elsif !File.directory?(options[:out])
|
@@ -4429,32 +4439,32 @@ class Tpkg
|
|
4429
4439
|
end
|
4430
4440
|
end
|
4431
4441
|
output_dir = options[:out] || Dir.pwd
|
4432
|
-
|
4442
|
+
|
4433
4443
|
requirements = []
|
4434
4444
|
packages = {}
|
4435
4445
|
original_dir = Dir.pwd
|
4436
|
-
|
4446
|
+
|
4437
4447
|
workdir = Tpkg::tempdir("tpkg_download")
|
4438
4448
|
Dir.chdir(workdir)
|
4439
4449
|
parse_requests(requests, requirements, packages)
|
4440
4450
|
packages = packages.values.flatten
|
4441
4451
|
if packages.size < 1
|
4442
4452
|
puts "Unable to find any packages that satisfy your request. Try running with --debug for more info"
|
4443
|
-
Dir.chdir(original_dir)
|
4453
|
+
Dir.chdir(original_dir)
|
4444
4454
|
return GENERIC_ERR
|
4445
4455
|
end
|
4446
|
-
|
4456
|
+
|
4447
4457
|
# Confirm with user what packages will be downloaded
|
4448
4458
|
packages.delete_if{|pkg|pkg[:source] !~ /^http/}
|
4449
4459
|
puts "The following packages will be downloaded:"
|
4450
4460
|
packages.each do |pkg|
|
4451
4461
|
puts "#{pkg[:metadata][:filename]} (source: #{pkg[:source]})"
|
4452
4462
|
end
|
4453
|
-
if @@prompt && !Tpkg::confirm
|
4454
|
-
Dir.chdir(original_dir)
|
4463
|
+
if @@prompt && !Tpkg::confirm
|
4464
|
+
Dir.chdir(original_dir)
|
4455
4465
|
return 0
|
4456
4466
|
end
|
4457
|
-
|
4467
|
+
|
4458
4468
|
err_code = 0
|
4459
4469
|
puts "Downloading to #{output_dir}"
|
4460
4470
|
packages.each do |pkg|
|
@@ -4468,21 +4478,21 @@ class Tpkg
|
|
4468
4478
|
err_code = GENERIC_ERR
|
4469
4479
|
end
|
4470
4480
|
end
|
4471
|
-
|
4481
|
+
|
4472
4482
|
# clean up working directory
|
4473
4483
|
FileUtils.rm_rf(workdir)
|
4474
4484
|
|
4475
|
-
Dir.chdir(original_dir)
|
4485
|
+
Dir.chdir(original_dir)
|
4476
4486
|
return err_code
|
4477
4487
|
end
|
4478
|
-
|
4488
|
+
|
4479
4489
|
# TODO: figure out what other methods above can be turned into protected methods
|
4480
4490
|
protected
|
4481
4491
|
# log changes of pkgs that were installed/removed
|
4482
4492
|
def log_changes(options={})
|
4483
4493
|
msg = ""
|
4484
4494
|
user = Etc.getlogin || Etc.getpwuid(Process.uid).name
|
4485
|
-
newly_installed = removed = []
|
4495
|
+
newly_installed = removed = []
|
4486
4496
|
newly_installed = options[:newly_installed] if options[:newly_installed]
|
4487
4497
|
removed = options[:removed] if options[:removed]
|
4488
4498
|
removed.each do |pkg|
|
@@ -4497,7 +4507,7 @@ class Tpkg
|
|
4497
4507
|
File.open(File.join(@log_directory,'changes.log'), 'a') {|f| f.write(msg) }
|
4498
4508
|
end
|
4499
4509
|
end
|
4500
|
-
|
4510
|
+
|
4501
4511
|
def send_update_to_server(options={})
|
4502
4512
|
request = {"client"=>Facter['fqdn'].value}
|
4503
4513
|
request[:user] = Etc.getlogin || Etc.getpwuid(Process.uid).name
|
@@ -4518,12 +4528,12 @@ class Tpkg
|
|
4518
4528
|
already_installed = currently_installed
|
4519
4529
|
end
|
4520
4530
|
request[:already_installed] = URI.escape(YAML.dump(already_installed))
|
4521
|
-
|
4531
|
+
|
4522
4532
|
if options[:removed]
|
4523
4533
|
removed = options[:removed]
|
4524
4534
|
request[:removed] = URI.escape(YAML.dump(removed))
|
4525
4535
|
end
|
4526
|
-
|
4536
|
+
|
4527
4537
|
begin
|
4528
4538
|
response = nil
|
4529
4539
|
# Need to set timeout otherwise tpkg can hang for a long time when having
|
@@ -4531,12 +4541,12 @@ class Tpkg
|
|
4531
4541
|
# I can't seem get net-ssh timeout to work so we'll just handle the timeout ourselves
|
4532
4542
|
timeout(CONNECTION_TIMEOUT) do
|
4533
4543
|
update_uri = URI.parse("#{@report_server}")
|
4534
|
-
http =
|
4544
|
+
http = gethttp(update_uri)
|
4535
4545
|
post = Net::HTTP::Post.new(update_uri.path)
|
4536
4546
|
post.set_form_data(request)
|
4537
4547
|
response = http.request(post)
|
4538
4548
|
end
|
4539
|
-
|
4549
|
+
|
4540
4550
|
case response
|
4541
4551
|
when Net::HTTPSuccess
|
4542
4552
|
puts "Successfully send update to reporter server"
|
@@ -4548,24 +4558,24 @@ class Tpkg
|
|
4548
4558
|
end
|
4549
4559
|
rescue Timeout::Error
|
4550
4560
|
puts "Timed out when trying to send update to reporter server"
|
4551
|
-
rescue
|
4561
|
+
rescue
|
4552
4562
|
puts "Failed to send update to reporter server"
|
4553
4563
|
end
|
4554
4564
|
end
|
4555
|
-
|
4556
|
-
# create and install native stub package if needed
|
4557
|
-
# this stub package helps prevent user from removing native packages that
|
4558
|
-
# our tpkg packages depend on
|
4565
|
+
|
4566
|
+
# create and install native stub package if needed
|
4567
|
+
# this stub package helps prevent user from removing native packages that
|
4568
|
+
# our tpkg packages depend on
|
4559
4569
|
def stub_native_pkg(pkg)
|
4560
4570
|
# gather all of the native dependencies
|
4561
4571
|
native_deps = pkg[:metadata].get_native_deps
|
4562
|
-
|
4572
|
+
|
4563
4573
|
return if native_deps.nil? or native_deps.empty?
|
4564
4574
|
|
4565
4575
|
if Tpkg::get_os =~ /RedHat|CentOS|Fedora/
|
4566
4576
|
rpm = create_rpm("stub_for_#{pkg[:metadata][:name]}", native_deps)
|
4567
4577
|
return if rpm.nil?
|
4568
|
-
|
4578
|
+
|
4569
4579
|
# install the rpm
|
4570
4580
|
cmd = "rpm -i #{rpm}"
|
4571
4581
|
puts cmd if @@debug
|
@@ -4577,16 +4587,16 @@ class Tpkg
|
|
4577
4587
|
# TODO: support other OSes
|
4578
4588
|
end
|
4579
4589
|
end
|
4580
|
-
|
4590
|
+
|
4581
4591
|
# remove the native dependency stub packages if there's any
|
4582
4592
|
def remove_native_stub_pkg(pkg)
|
4583
4593
|
# Don't have to do anything if this package has no native dependencies
|
4584
4594
|
native_deps = pkg[:metadata].get_native_deps
|
4585
4595
|
return if native_deps.nil? or native_deps.empty?
|
4586
|
-
|
4596
|
+
|
4587
4597
|
# the convention is that stub package is named as "stub_for_pkgname"
|
4588
4598
|
stub_pkg_name = "stub_for_#{pkg[:metadata][:name]}"
|
4589
|
-
|
4599
|
+
|
4590
4600
|
if Tpkg::get_os =~ /RedHat|CentOS|Fedora/
|
4591
4601
|
cmd = "yum -y remove #{stub_pkg_name}"
|
4592
4602
|
puts cmd if @@debug
|
@@ -4598,16 +4608,16 @@ class Tpkg
|
|
4598
4608
|
# TODO: support other OSes
|
4599
4609
|
end
|
4600
4610
|
end
|
4601
|
-
|
4611
|
+
|
4602
4612
|
def create_rpm(name, deps=[])
|
4603
4613
|
# setup directories for rpmbuild
|
4604
4614
|
topdir = Tpkg::tempdir('rpmbuild')
|
4605
4615
|
%w[BUILD RPMS SOURCES SPECS SRPMS].each do |dir|
|
4606
4616
|
FileUtils.mkdir_p(File.join(topdir, dir))
|
4607
4617
|
end
|
4608
|
-
|
4618
|
+
|
4609
4619
|
dep_list = deps.collect{|dep|dep[:name]}.join(",")
|
4610
|
-
|
4620
|
+
|
4611
4621
|
# create rpm spec file
|
4612
4622
|
spec = <<-EOS.gsub(/^\s+/, "")
|
4613
4623
|
Name: #{name}
|
@@ -4627,14 +4637,14 @@ class Tpkg
|
|
4627
4637
|
File.open(spec_file, 'w') do |file|
|
4628
4638
|
file.puts(spec)
|
4629
4639
|
end
|
4630
|
-
|
4640
|
+
|
4631
4641
|
# run rpmbuild
|
4632
4642
|
system("rpmbuild -bb --define '_topdir #{topdir}' #{spec_file}")
|
4633
4643
|
if !$?.success?
|
4634
4644
|
warn "Warning: Failed to create native stub package for #{name}"
|
4635
4645
|
return nil
|
4636
4646
|
end
|
4637
|
-
|
4647
|
+
|
4638
4648
|
# copy result over to tmpfile
|
4639
4649
|
result = File.join(topdir, 'RPMS', 'noarch', "#{name}-1-1.noarch.rpm")
|
4640
4650
|
rpm = nil
|
@@ -4643,10 +4653,10 @@ class Tpkg
|
|
4643
4653
|
FileUtils.cp(result, tmpfile.path)
|
4644
4654
|
rpm = tmpfile.path
|
4645
4655
|
end
|
4646
|
-
|
4656
|
+
|
4647
4657
|
# cleanup
|
4648
4658
|
FileUtils.rm_rf(topdir)
|
4649
|
-
|
4659
|
+
|
4650
4660
|
return rpm
|
4651
4661
|
end
|
4652
4662
|
end
|