tpkg 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|