tpkg 1.18.2 → 1.19.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/bin/cpan2tpkg +12 -1
- data/bin/gem2tpkg +89 -46
- data/bin/tpkg +5 -1
- data/bin/tpkg_xml_to_yml +3 -0
- data/lib/tpkg.rb +458 -241
- data/lib/tpkg/metadata.rb +253 -47
- data/schema/schema-1.0.yml +84 -0
- data/schema/schema.yml +84 -0
- data/schema/tpkg-1.0.0.dtd +34 -0
- data/schema/tpkg-1.0.1.dtd +35 -0
- data/schema/tpkg-1.0.2.dtd +39 -0
- data/schema/tpkg-1.0.3.dtd +40 -0
- data/schema/tpkg-1.0.4.dtd +40 -0
- data/schema/tpkg-1.0.5.dtd +41 -0
- data/schema/tpkg.dtd +41 -0
- metadata +21 -2
data/Rakefile
CHANGED
@@ -4,7 +4,8 @@ spec = Gem::Specification.new do |s|
|
|
4
4
|
s.summary = 'tpkg Application Packaging & Deployment'
|
5
5
|
s.add_dependency('facter')
|
6
6
|
s.add_dependency('net-ssh')
|
7
|
-
s.
|
7
|
+
s.add_dependency('kwalify')
|
8
|
+
s.version = '1.19.2'
|
8
9
|
s.authors = ['Darren Dao', 'Jason Heiss']
|
9
10
|
s.email = 'tpkg-users@lists.sourceforge.net'
|
10
11
|
s.homepage = 'http://tpkg.sourceforge.net'
|
data/bin/cpan2tpkg
CHANGED
@@ -29,6 +29,8 @@ Usage: cpan2tpkg
|
|
29
29
|
Extra dependencies to add to the package
|
30
30
|
[--native-deps foo,1.0,1.9999,bar,4.5,4.5,blah,,,]
|
31
31
|
Native dependencies to add to the package
|
32
|
+
[--force]
|
33
|
+
Force the install and packaging even if tests fail
|
32
34
|
[--help]
|
33
35
|
Show this message
|
34
36
|
|
@@ -42,6 +44,7 @@ my %extradeps = ();
|
|
42
44
|
my $extradepsopts = '';
|
43
45
|
my %nativedeps = ();
|
44
46
|
my $nativedepsopts = '';
|
47
|
+
my $force;
|
45
48
|
my $help;
|
46
49
|
|
47
50
|
my $getopt = GetOptions(
|
@@ -49,6 +52,7 @@ my $getopt = GetOptions(
|
|
49
52
|
'package-version=s' => \$pkgver,
|
50
53
|
'extra-deps=s' => \$extradepsopts,
|
51
54
|
'native-deps=s' => \$nativedepsopts,
|
55
|
+
'force' => \$force,
|
52
56
|
'help' => \$help);
|
53
57
|
|
54
58
|
my $module = shift @ARGV;
|
@@ -131,7 +135,14 @@ CPAN::Shell->o('conf', 'mbuildpl_arg', "--destdir=$workdir");
|
|
131
135
|
#CPAN::Shell->o('conf', 'prerequisites_policy', "ask");
|
132
136
|
|
133
137
|
# Install the module
|
134
|
-
$
|
138
|
+
if ($force)
|
139
|
+
{
|
140
|
+
CPAN::Shell->force('install', $module);
|
141
|
+
}
|
142
|
+
else
|
143
|
+
{
|
144
|
+
$modobj->install;
|
145
|
+
}
|
135
146
|
|
136
147
|
# It is not nearly as straightforward as one might wish to get
|
137
148
|
# ExtUtils::Installed to inspect only a specified directory structure and not
|
data/bin/gem2tpkg
CHANGED
@@ -10,20 +10,9 @@ require 'shellwords'
|
|
10
10
|
require 'tpkg'
|
11
11
|
require 'facter'
|
12
12
|
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
RUBYDEPS = ['ruby']
|
17
|
-
# T_RUBY_BASE = "/home/t/ruby"
|
18
|
-
T_RUBY_BASE = "#{Tpkg::DEFAULT_BASE}/#{RUBYDEPS.first}".freeze
|
19
|
-
DEFAULT_GEM_COMMAND = "#{T_RUBY_BASE}/bin/gem"
|
20
|
-
|
21
|
-
# Figure out rubygems library from ruby.
|
22
|
-
# There might be several versions so get the latest one. We're assuming that
|
23
|
-
# the version directories are named appropriately under lib/ruby/site_ruby.
|
24
|
-
versions = Dir.glob(File.join("#{T_RUBY_BASE}", "lib", "ruby", "site_ruby", "*"))
|
25
|
-
versions.sort!
|
26
|
-
DEFAULT_RUBYGEMS_PATH = versions[-1]
|
13
|
+
# We don't want to just use the first gem command in the user's PATH by
|
14
|
+
# default, as that may not be a tpkg gem. I.e. /usr/bin/gem on Mac OS X.
|
15
|
+
DEFAULT_GEM_COMMAND = "#{Tpkg::DEFAULT_BASE}/ruby-1.8/bin/gem"
|
27
16
|
|
28
17
|
# Haven't found a Ruby method for creating temporary directories,
|
29
18
|
# so create a temporary file and replace it with a directory.
|
@@ -45,6 +34,7 @@ end
|
|
45
34
|
@buildopts = []
|
46
35
|
@gemcmd = DEFAULT_GEM_COMMAND
|
47
36
|
@rubygemspath = nil
|
37
|
+
@extrapkgname = nil
|
48
38
|
|
49
39
|
opts = OptionParser.new
|
50
40
|
opts.banner = 'Usage: gem2tpkg [options] GEMNAME1 GEMNAME2 ...'
|
@@ -92,33 +82,91 @@ end
|
|
92
82
|
opts.on('--rubygems-path', '=PATH', 'Path to rubygems library') do |opt|
|
93
83
|
@rubygemspath = opt
|
94
84
|
end
|
85
|
+
opts.on('--extra-name', '=EXTRANAME', 'Extra string to add to package name') do |opt|
|
86
|
+
@extrapkgname = opt
|
87
|
+
end
|
95
88
|
opts.on_tail("-h", "--help", "Show this message") do
|
96
89
|
puts opts
|
97
90
|
exit
|
98
91
|
end
|
99
92
|
|
100
|
-
#
|
93
|
+
# Allow user to specify multiple gems at the end of the command line
|
94
|
+
# arguments
|
101
95
|
leftover = opts.parse(ARGV)
|
102
96
|
@gems = leftover
|
103
|
-
#if leftover.length == 1
|
104
|
-
# @gem = leftover.shift
|
105
|
-
#else
|
106
|
-
# puts opts
|
107
|
-
# exit
|
108
|
-
#end
|
109
97
|
|
110
|
-
#
|
111
|
-
@
|
98
|
+
# Extract a few paths from gem
|
99
|
+
@geminstallpath = nil
|
100
|
+
@gembinpath = nil
|
101
|
+
@rubypath = nil
|
102
|
+
IO.popen("#{@gemcmd} environment") do |pipe|
|
103
|
+
pipe.each_line do |line|
|
104
|
+
if line =~ /INSTALLATION DIRECTORY:\s+(\S+)/
|
105
|
+
@geminstallpath = $1
|
106
|
+
end
|
107
|
+
if line =~ /EXECUTABLE DIRECTORY:\s+(\S+)/
|
108
|
+
@gembinpath = $1
|
109
|
+
end
|
110
|
+
if line =~ /RUBY EXECUTABLE:\s+(\S+)/
|
111
|
+
@rubypath = $1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
if !$?.success?
|
116
|
+
abort 'gem environment failed'
|
117
|
+
end
|
118
|
+
|
119
|
+
# Find the right rubygems library if the user didn't request a specific
|
120
|
+
# one
|
121
|
+
if !@rubygemspath
|
122
|
+
cmd = "#{@rubypath} -e 'puts $:'"
|
123
|
+
IO.popen(cmd) do |pipe|
|
124
|
+
pipe.each_line do |line|
|
125
|
+
line.chomp!
|
126
|
+
if File.exist?(File.join(line, 'rubygems.rb'))
|
127
|
+
@rubygemspath = line
|
128
|
+
break
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
if !$?.success?
|
133
|
+
abort "#{cmd} failed"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Require the correct rubygems based on what the user specifies
|
112
138
|
$:.unshift @rubygemspath unless @rubygemspath.nil?
|
113
139
|
require 'rubygems'
|
114
140
|
|
141
|
+
# Ask tpkg what package owns the gem executable that we're using so that
|
142
|
+
# we can add a dependency on that package to any packages we build.
|
143
|
+
@gemdep = []
|
144
|
+
# FIXME: Currently the reading of config files and whatnot is done in
|
145
|
+
# the tpkg executable. We should probably move that into the library so
|
146
|
+
# that other utilities like this one use an alternate base if the user
|
147
|
+
# has configured one, etc.
|
148
|
+
tpkg = Tpkg.new(:base => Tpkg::DEFAULT_BASE)
|
149
|
+
tpkg.files_for_installed_packages.each do |pkgfile, fip|
|
150
|
+
fip[:normalized].each do |file|
|
151
|
+
if file == @gemcmd
|
152
|
+
metadata = nil
|
153
|
+
# FIXME: Should add a metadata_for_installed_package method to the
|
154
|
+
# library
|
155
|
+
tpkg.metadata_for_installed_packages.each do |metadata|
|
156
|
+
if metadata[:filename] == pkgfile
|
157
|
+
@gemdep << metadata[:name]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
115
164
|
# Create the directory we want gem to install into
|
116
165
|
@gemdir = tempdir('gem2tpkg')
|
117
166
|
ENV['GEM_HOME'] = @gemdir
|
118
167
|
ENV['GEM_PATH'] = @gemdir
|
119
168
|
|
120
169
|
# Install the gem
|
121
|
-
#geminst = [@gemcmd, 'install', @gems.join(" "), '--no-rdoc', '--no-ri']
|
122
170
|
geminst = [@gemcmd, 'install', '--no-rdoc', '--no-ri'] | @gems
|
123
171
|
if @gemver
|
124
172
|
geminst << @gemver
|
@@ -147,7 +195,6 @@ def package(gem)
|
|
147
195
|
puts "Packaging #{gem}"
|
148
196
|
|
149
197
|
gemsdir = nil
|
150
|
-
#globdirs = Dir.glob(File.join(@gemdir, 'gems', "#{gem}-*"))
|
151
198
|
globdirs = Dir.glob(File.join(@gemdir, 'gems', "#{gem}-[0-9]*"))
|
152
199
|
if globdirs.length == 1
|
153
200
|
gemsdir = globdirs[0]
|
@@ -286,7 +333,7 @@ def package(gem)
|
|
286
333
|
|
287
334
|
# The directory where we make our package
|
288
335
|
pkgdir = tempdir('gem2tpkg')
|
289
|
-
pkgbasedir = File.join(pkgdir, "/root/#{
|
336
|
+
pkgbasedir = File.join(pkgdir, "/root/#{@geminstallpath}")
|
290
337
|
FileUtils.mkdir_p(pkgbasedir)
|
291
338
|
pkggemdir = File.join(pkgbasedir, 'gems')
|
292
339
|
FileUtils.mkdir_p(pkggemdir)
|
@@ -305,7 +352,7 @@ def package(gem)
|
|
305
352
|
binfiles << File.join(@gemdir, 'bin', exec)
|
306
353
|
end
|
307
354
|
if !binfiles.empty?
|
308
|
-
bindir = "#{pkgdir}/root/#{
|
355
|
+
bindir = "#{pkgdir}/root/#{@gembinpath}"
|
309
356
|
FileUtils.mkdir_p(bindir)
|
310
357
|
binfiles.each do |binfile|
|
311
358
|
FileUtils.cp(binfile, bindir, :preserve => true)
|
@@ -315,6 +362,15 @@ def package(gem)
|
|
315
362
|
# Copy over the gemspec file
|
316
363
|
FileUtils.cp(gemspecfile, pkgspecdir, :preserve => true)
|
317
364
|
|
365
|
+
pkgnamesuffix = ''
|
366
|
+
if @extrapkgname
|
367
|
+
pkgnamesuffix = '-' + @extrapkgname
|
368
|
+
elsif @gemcmd != DEFAULT_GEM_COMMAND
|
369
|
+
# If we're not using the default gem try to name the package in a way
|
370
|
+
# that indicates the alternate ruby/gem used
|
371
|
+
pkgnamesuffix = '-' + @gemdep.first.sub(/\W/, '')
|
372
|
+
end
|
373
|
+
|
318
374
|
# Add tpkg.xml
|
319
375
|
os = nil
|
320
376
|
arch = nil
|
@@ -322,7 +378,7 @@ def package(gem)
|
|
322
378
|
file.puts '<?xml version="1.0" encoding="UTF-8"?>'
|
323
379
|
file.puts '<!DOCTYPE tpkg SYSTEM "http://tpkg.sourceforge.net/tpkg-1.0.dtd">'
|
324
380
|
file.puts '<tpkg>'
|
325
|
-
file.puts " <name>gem-#{gem}</name>"
|
381
|
+
file.puts " <name>gem-#{gem}#{pkgnamesuffix}</name>"
|
326
382
|
file.puts " <version>#{gemspec.version.to_s}</version>"
|
327
383
|
file.puts " <package_version>#{@pkgver}</package_version>"
|
328
384
|
file.puts ' <maintainer>gem2tpkg</maintainer>'
|
@@ -341,11 +397,11 @@ def package(gem)
|
|
341
397
|
end
|
342
398
|
if !deps.empty? ||
|
343
399
|
!@extradeps.empty? || !@nativedeps.empty? ||
|
344
|
-
|
400
|
+
!@gemdep.empty?
|
345
401
|
file.puts ' <dependencies>'
|
346
402
|
deps.each do |depgem, depvers|
|
347
403
|
file.puts ' <dependency>'
|
348
|
-
file.puts " <name>gem-#{depgem}</name>"
|
404
|
+
file.puts " <name>gem-#{depgem}#{pkgnamesuffix}</name>"
|
349
405
|
if depvers[:minimum_version]
|
350
406
|
file.puts " <minimum_version>#{depvers[:minimum_version]}</minimum_version>"
|
351
407
|
end
|
@@ -377,9 +433,9 @@ def package(gem)
|
|
377
433
|
file.puts ' <native/>'
|
378
434
|
file.puts ' </dependency>'
|
379
435
|
end
|
380
|
-
|
436
|
+
@gemdep.each do |gemdep|
|
381
437
|
file.puts ' <dependency>'
|
382
|
-
file.puts " <name>#{
|
438
|
+
file.puts " <name>#{gemdep}</name>"
|
383
439
|
file.puts ' </dependency>'
|
384
440
|
end
|
385
441
|
file.puts ' </dependencies>'
|
@@ -389,24 +445,11 @@ def package(gem)
|
|
389
445
|
|
390
446
|
# Make package
|
391
447
|
pkgfile = Tpkg::make_package(pkgdir)
|
392
|
-
# If the package is OS-specific then rename the file to reflect that
|
393
|
-
if os
|
394
|
-
# Examples:
|
395
|
-
# FreeBSD-7 -> freebsd7
|
396
|
-
# RedHat-5 -> redhat5
|
397
|
-
# CentOS-5 -> redhat5
|
398
|
-
fileos = Tpkg::get_os.sub('CentOS', 'RedHat').downcase.sub('-', '')
|
399
|
-
newpkgfile = File.join(
|
400
|
-
File.dirname(pkgfile),
|
401
|
-
"#{File.basename(pkgfile, '.tpkg')}-#{fileos}-#{arch}.tpkg")
|
402
|
-
File.rename(pkgfile, newpkgfile)
|
403
|
-
pkgfile = newpkgfile
|
404
|
-
end
|
405
448
|
pkgfiles << pkgfile
|
406
449
|
|
407
450
|
# Cleanup
|
408
451
|
FileUtils.rm_rf(pkgdir)
|
409
|
-
|
452
|
+
|
410
453
|
@already_packaged << gem
|
411
454
|
pkgfiles
|
412
455
|
end
|
data/bin/tpkg
CHANGED
@@ -329,10 +329,14 @@ if @deploy
|
|
329
329
|
exit
|
330
330
|
end
|
331
331
|
|
332
|
+
if @action_value.is_a?(Array)
|
333
|
+
@action_value.uniq!
|
334
|
+
end
|
335
|
+
|
332
336
|
ret_val = 0
|
333
337
|
case @action
|
334
338
|
when :make
|
335
|
-
pkgfile = Tpkg::make_package(@action_value, passphrase_callback)
|
339
|
+
pkgfile = Tpkg::make_package(@action_value, passphrase_callback, {:force => @force})
|
336
340
|
if pkgfile
|
337
341
|
puts "Package is #{pkgfile}"
|
338
342
|
else
|
data/bin/tpkg_xml_to_yml
CHANGED
data/lib/tpkg.rb
CHANGED
@@ -21,6 +21,13 @@ if File.directory?(tpkglibdir)
|
|
21
21
|
$:.unshift(tpkglibdir)
|
22
22
|
end
|
23
23
|
|
24
|
+
# We store this gem in our thirdparty directory. So we need to add it
|
25
|
+
# it to the search path
|
26
|
+
# This one is for when everything is installed
|
27
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'thirdparty/kwalify-0.7.1/lib'))
|
28
|
+
# And this one for when we're in the svn directory structure
|
29
|
+
$:.unshift(File.join(File.dirname(File.dirname(__FILE__)), 'thirdparty/kwalify-0.7.1/lib'))
|
30
|
+
|
24
31
|
begin
|
25
32
|
# Try loading facter w/o gems first so that we don't introduce a
|
26
33
|
# dependency on gems if it is not needed.
|
@@ -45,16 +52,20 @@ require 'versiontype' # Version
|
|
45
52
|
require 'deployer'
|
46
53
|
require 'set'
|
47
54
|
require 'metadata'
|
55
|
+
require 'kwalify' # for validating yaml
|
48
56
|
|
49
57
|
class Tpkg
|
50
58
|
|
51
|
-
VERSION = '1.
|
59
|
+
VERSION = '1.19.2'
|
52
60
|
CONFIGDIR = '/etc'
|
53
|
-
|
61
|
+
|
62
|
+
GENERIC_ERR = 1
|
54
63
|
POSTINSTALL_ERR = 2
|
55
64
|
POSTREMOVE_ERR = 3
|
56
65
|
INITSCRIPT_ERR = 4
|
57
66
|
|
67
|
+
CONNECTION_TIMEOUT = 10
|
68
|
+
|
58
69
|
attr_reader :installed_directory
|
59
70
|
|
60
71
|
#
|
@@ -74,6 +85,8 @@ class Tpkg
|
|
74
85
|
# Find GNU tar or bsdtar in ENV['PATH']
|
75
86
|
# Raises an exception if a suitable tar cannot be found
|
76
87
|
@@tar = nil
|
88
|
+
@@taroptions = ""
|
89
|
+
@@tartype = nil
|
77
90
|
TARNAMES = ['tar', 'gtar', 'gnutar', 'bsdtar']
|
78
91
|
def self.find_tar
|
79
92
|
if !@@tar
|
@@ -83,7 +96,12 @@ class Tpkg
|
|
83
96
|
if File.executable?(File.join(path, tarname))
|
84
97
|
IO.popen("#{File.join(path, tarname)} --version 2>/dev/null") do |pipe|
|
85
98
|
pipe.each_line do |line|
|
86
|
-
if line.include?('GNU tar')
|
99
|
+
if line.include?('GNU tar')
|
100
|
+
@@tartype = 'gnu'
|
101
|
+
@@tar = File.join(path, tarname)
|
102
|
+
throw :tar_found
|
103
|
+
elsif line.include?('bsdtar')
|
104
|
+
@@tartype = 'bsd'
|
87
105
|
@@tar = File.join(path, tarname)
|
88
106
|
throw :tar_found
|
89
107
|
end
|
@@ -96,6 +114,15 @@ class Tpkg
|
|
96
114
|
raise "Unable to find GNU tar or bsdtar in PATH"
|
97
115
|
end
|
98
116
|
end
|
117
|
+
# bsdtar uses pax format by default. This format allows for vendor extensions, such
|
118
|
+
# as the SCHILY.* extensions which were introduced by star). bsdtar actually uses
|
119
|
+
# these extensions. These extension headers includde useful, but not vital information.
|
120
|
+
# gnu tar should just ignore them and gives a warning. This is what the latest gnu tar
|
121
|
+
# will do. However, on older gnu tar, it only threw an error at the end. The work
|
122
|
+
# around is to explicitly tell gnu tar to ignore those extensions.
|
123
|
+
if @@tartype == 'gnu'
|
124
|
+
@@taroptions = "--pax-option='delete=SCHILY.*'"
|
125
|
+
end
|
99
126
|
@@tar.dup
|
100
127
|
end
|
101
128
|
def self.clear_cached_tar
|
@@ -190,8 +217,7 @@ class Tpkg
|
|
190
217
|
end
|
191
218
|
|
192
219
|
# Makes a package from a directory containing the files to put into the package
|
193
|
-
|
194
|
-
def self.make_package(pkgsrcdir, passphrase=nil)
|
220
|
+
def self.make_package(pkgsrcdir, passphrase=nil, options = {})
|
195
221
|
pkgfile = nil
|
196
222
|
|
197
223
|
# Make a working directory
|
@@ -225,23 +251,40 @@ class Tpkg
|
|
225
251
|
# code (tar) ever touch the user's files.
|
226
252
|
system("#{find_tar} -C #{pkgsrcdir} -cf - . | #{find_tar} -C #{tpkgdir} -xpf -") || raise("Package content copy failed")
|
227
253
|
|
254
|
+
# check metadata file
|
255
|
+
errors = []
|
228
256
|
if File.exists?(File.join(tpkgdir, 'tpkg.yml'))
|
229
|
-
|
230
|
-
|
257
|
+
metadata_file = File.join(tpkgdir, 'tpkg.yml')
|
258
|
+
metadata_format = 'yml'
|
231
259
|
elsif File.exists?(File.join(tpkgdir, 'tpkg.xml'))
|
232
|
-
|
233
|
-
|
260
|
+
metadata_file = File.join(tpkgdir, 'tpkg.xml')
|
261
|
+
metadata_format = 'xml'
|
234
262
|
else
|
235
263
|
raise 'Your source directory does not contain the metadata configuration file.'
|
236
264
|
end
|
265
|
+
metadata_text = File.read(metadata_file)
|
266
|
+
metadata = Metadata.new(metadata_text, metadata_format)
|
237
267
|
|
238
|
-
|
268
|
+
# This is the directory where we put our dtd/schema for validating
|
269
|
+
# the metadata file
|
270
|
+
if File.exist?(File.join(CONFIGDIR, 'tpkg', 'schema'))
|
271
|
+
schema_dir = File.join(CONFIGDIR, 'tpkg', 'schema')
|
272
|
+
else # This is for when we're in developement mode or when installed as gem
|
273
|
+
schema_dir = File.join(File.dirname(File.dirname(__FILE__)), "schema")
|
274
|
+
end
|
275
|
+
errors = metadata.validate(schema_dir)
|
276
|
+
if errors && !errors.empty?
|
277
|
+
puts "Bad metadata file. Possible error(s):"
|
278
|
+
errors.each {|e| puts e }
|
279
|
+
raise "Failed to create package." unless options[:force]
|
280
|
+
end
|
239
281
|
|
240
282
|
# file_metadata.yml hold information for files that are installed
|
241
283
|
# by the package. For example, checksum, path, relocatable or not, etc.
|
242
284
|
File.open(File.join(tpkgdir, "file_metadata.bin"), "w") do |file|
|
243
285
|
filemetadata = get_filemetadata_from_directory(tpkgdir)
|
244
|
-
|
286
|
+
data = filemetadata.to_hash.recursively{|h| h.stringify_keys }
|
287
|
+
Marshal::dump(data, file)
|
245
288
|
# YAML::dump(filemetadata.to_hash, file)
|
246
289
|
end
|
247
290
|
|
@@ -259,6 +302,19 @@ class Tpkg
|
|
259
302
|
raise "File #{tpkg_path} referenced in tpkg.yml but not found"
|
260
303
|
end
|
261
304
|
|
305
|
+
# check permission/ownership of crontab files
|
306
|
+
if tpkgfile[:crontab]
|
307
|
+
data = {:actual_file => working_path, :metadata => metadata, :file_metadata => tpkgfile}
|
308
|
+
perms, uid, gid = predict_file_perms_and_ownership(data)
|
309
|
+
# crontab needs to be owned by root, and is not writable by group or others
|
310
|
+
if uid != 0
|
311
|
+
warn "Warning: Your cron jobs in \"#{tpkgfile[:path]}\" might fail to run because the file is not owned by root."
|
312
|
+
end
|
313
|
+
if (perms & 0022) != 0
|
314
|
+
warn "Warning: Your cron jobs in \"#{tpkgfile[:path]}\" might fail to run because the file is writable by group and/or others."
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
262
318
|
# Encrypt any files marked for encryption
|
263
319
|
if tpkgfile[:encrypt]
|
264
320
|
if tpkgfile[:encrypt] == 'precrypt'
|
@@ -323,8 +379,12 @@ class Tpkg
|
|
323
379
|
toplevel = nil
|
324
380
|
# FIXME: This is so lame, to read the whole package to get the
|
325
381
|
# first filename. Blech.
|
326
|
-
IO.popen("#{find_tar} -tf #{package_file}") do |pipe|
|
327
|
-
toplevel = pipe.gets
|
382
|
+
IO.popen("#{find_tar} -tf #{package_file} #{@@taroptions}") do |pipe|
|
383
|
+
toplevel = pipe.gets
|
384
|
+
if toplevel.nil?
|
385
|
+
raise "Package directory structure of #{package_file} unexpected. Unable to get top level."
|
386
|
+
end
|
387
|
+
toplevel.chomp!
|
328
388
|
# Avoid SIGPIPE, if we don't sink the rest of the output from tar
|
329
389
|
# then tar ends up getting SIGPIPE when it tries to write to the
|
330
390
|
# closed pipe and exits with error, which causes us to throw an
|
@@ -423,7 +483,7 @@ class Tpkg
|
|
423
483
|
topleveldir = package_toplevel_directory(package_file)
|
424
484
|
# Extract checksum.xml from the package
|
425
485
|
checksum_xml = nil
|
426
|
-
IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'checksum.xml')}") do |pipe|
|
486
|
+
IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'checksum.xml')} #{@@taroptions}") do |pipe|
|
427
487
|
checksum_xml = REXML::Document.new(pipe.read)
|
428
488
|
end
|
429
489
|
if !$?.success?
|
@@ -448,7 +508,7 @@ class Tpkg
|
|
448
508
|
raise("Unrecognized checksum algorithm #{checksum.elements['algorithm']}")
|
449
509
|
end
|
450
510
|
# Extract tpkg.tar from the package and digest it
|
451
|
-
IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')}") do |pipe|
|
511
|
+
IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}") do |pipe|
|
452
512
|
# Package files can be quite large, so we digest the package in
|
453
513
|
# chunks. A survey of the Internet turns up someone who tested
|
454
514
|
# various chunk sizes on various platforms and found 4k to be
|
@@ -880,7 +940,7 @@ class Tpkg
|
|
880
940
|
files[:root] = []
|
881
941
|
files[:reloc] = []
|
882
942
|
topleveldir = package_toplevel_directory(package_file)
|
883
|
-
IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{find_tar} -tf -") do |pipe|
|
943
|
+
IO.popen("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}| #{find_tar} #{@@taroptions} -tf -") do |pipe|
|
884
944
|
pipe.each do |file|
|
885
945
|
file.chomp!
|
886
946
|
if file =~ Regexp.new(File.join('tpkg', 'root'))
|
@@ -1007,6 +1067,7 @@ class Tpkg
|
|
1007
1067
|
#
|
1008
1068
|
# servers is an array or a callback that list the remote servers where we want to deploy to
|
1009
1069
|
def self.deploy(deploy_params, deploy_options, servers)
|
1070
|
+
servers.uniq!
|
1010
1071
|
deployer = Deployer.new(deploy_options)
|
1011
1072
|
deployer.deploy(deploy_params, servers)
|
1012
1073
|
end
|
@@ -1043,7 +1104,7 @@ class Tpkg
|
|
1043
1104
|
begin
|
1044
1105
|
topleveldir = Tpkg::package_toplevel_directory(package_file)
|
1045
1106
|
workdir = Tpkg::tempdir(topleveldir)
|
1046
|
-
system("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{find_tar} -C #{workdir} -xpf -")
|
1107
|
+
system("#{find_tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions}| #{find_tar} #{@@taroptions} -C #{workdir} -xpf -")
|
1047
1108
|
|
1048
1109
|
if File.exist?(File.join(workdir,"tpkg", "tpkg.yml"))
|
1049
1110
|
metadata_file = File.join(workdir,"tpkg", "tpkg.yml")
|
@@ -1060,6 +1121,47 @@ class Tpkg
|
|
1060
1121
|
end
|
1061
1122
|
return result
|
1062
1123
|
end
|
1124
|
+
|
1125
|
+
# The only restriction right now is that the file doesn't begin with "."
|
1126
|
+
def self.valid_pkg_filename?(filename)
|
1127
|
+
return File.basename(filename) !~ /^\./
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
# helper method for predicting the permissions and ownership of a file that
|
1131
|
+
# will be installed by tpkg. This is done by looking at:
|
1132
|
+
# 1) its current perms & ownership
|
1133
|
+
# 2) the file_defaults settings of the metadata file
|
1134
|
+
# 3) the explicitly defined settings in the corresponding file section of the metadata file
|
1135
|
+
def self.predict_file_perms_and_ownership(data)
|
1136
|
+
perms = nil
|
1137
|
+
uid = nil
|
1138
|
+
gid = nil
|
1139
|
+
|
1140
|
+
# get current permission and ownership
|
1141
|
+
if data[:actual_file]
|
1142
|
+
stat = File.stat(data[:actual_file])
|
1143
|
+
perms = stat.mode
|
1144
|
+
uid = stat.uid
|
1145
|
+
gid = stat.gid
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
# get default permission and ownership
|
1149
|
+
metadata = data[:metadata]
|
1150
|
+
if (metadata && metadata[:files] && metadata[:files][:file_defaults] && metadata[:files][:file_defaults][:posix])
|
1151
|
+
uid = Tpkg::lookup_uid(metadata[:files][:file_defaults][:posix][:owner]) if metadata[:files][:file_defaults][:posix][:owner]
|
1152
|
+
gid = Tpkg::lookup_uid(metadata[:files][:file_defaults][:posix][:group]) if metadata[:files][:file_defaults][:posix][:group]
|
1153
|
+
perms = metadata[:files][:file_defaults][:posix][:perms] if metadata[:files][:file_defaults][:posix][:perms]
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
# get explicitly defined permission and ownership
|
1157
|
+
file_metadata = data[:file_metadata]
|
1158
|
+
if file_metadata && file_metadata[:posix]
|
1159
|
+
uid = Tpkg::lookup_uid(file_metadata[:posix][:owner]) if file_metadata[:posix][:owner]
|
1160
|
+
gid = Tpkg::lookup_uid(file_metadata[:posix][:group]) if file_metadata[:posix][:group]
|
1161
|
+
perms = file_metadata[:posix][:perms] if file_metadata[:posix][:perms]
|
1162
|
+
end
|
1163
|
+
return perms, uid, gid
|
1164
|
+
end
|
1063
1165
|
|
1064
1166
|
#
|
1065
1167
|
# Instance methods
|
@@ -1598,7 +1700,7 @@ class Tpkg
|
|
1598
1700
|
metadata = {}
|
1599
1701
|
if File.directory?(@installed_directory)
|
1600
1702
|
Dir.foreach(@installed_directory) do |entry|
|
1601
|
-
next if entry == '.' || entry == '..' || entry == 'metadata'
|
1703
|
+
next if entry == '.' || entry == '..' || entry == 'metadata' || !Tpkg::valid_pkg_filename?(entry)
|
1602
1704
|
# Check the timestamp on the file to see if it is new or has
|
1603
1705
|
# changed since we last loaded data
|
1604
1706
|
timestamp = File.mtime(File.join(@installed_directory, entry))
|
@@ -1642,7 +1744,7 @@ class Tpkg
|
|
1642
1744
|
end
|
1643
1745
|
end
|
1644
1746
|
metadata[entry] = { :timestamp => timestamp,
|
1645
|
-
:metadata => m }
|
1747
|
+
:metadata => m } unless m.nil?
|
1646
1748
|
end
|
1647
1749
|
end
|
1648
1750
|
end
|
@@ -1652,13 +1754,15 @@ class Tpkg
|
|
1652
1754
|
end
|
1653
1755
|
|
1654
1756
|
# Convert metadata_for_installed_packages into pkg hashes
|
1655
|
-
def installed_packages
|
1757
|
+
def installed_packages(pkgname=nil)
|
1656
1758
|
instpkgs = []
|
1657
1759
|
metadata_for_installed_packages.each do |metadata|
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1760
|
+
if !pkgname || metadata[:name] == pkgname
|
1761
|
+
instpkgs << { :metadata => metadata,
|
1762
|
+
:source => :currently_installed,
|
1763
|
+
# It seems reasonable for this to default to true
|
1764
|
+
:prefer => true }
|
1765
|
+
end
|
1662
1766
|
end
|
1663
1767
|
instpkgs
|
1664
1768
|
end
|
@@ -1740,7 +1844,29 @@ class Tpkg
|
|
1740
1844
|
end
|
1741
1845
|
end
|
1742
1846
|
else
|
1743
|
-
|
1847
|
+
pkgname = nil
|
1848
|
+
if req && req[:name]
|
1849
|
+
pkgname = req[:name]
|
1850
|
+
end
|
1851
|
+
# Passing a package name if we have one to installed_packages serves
|
1852
|
+
# primarily to make following the debugging output of dependency
|
1853
|
+
# resolution easier. The dependency resolution process makes frequent
|
1854
|
+
# calls to available_packages_that_meet_requirement, which in turn calls
|
1855
|
+
# this method. For available packages we're able to pre-filter based on
|
1856
|
+
# package name before calling package_meets_requirement? because we
|
1857
|
+
# store available packages hashed based on package name.
|
1858
|
+
# package_meets_requirement? is fairly verbose in its debugging output,
|
1859
|
+
# so the user sees each package it checks against a given requirement.
|
1860
|
+
# It is therefore a bit disconcerting when trying to follow the
|
1861
|
+
# debugging output to see the fairly clean process of checking available
|
1862
|
+
# packages which have already been filtered to match the desired name,
|
1863
|
+
# and then available_packages_that_meet_requirement calls this method,
|
1864
|
+
# and the user starts to see every installed package checked against the
|
1865
|
+
# same requirement. It is not obvious to the someone why all of a
|
1866
|
+
# sudden packages that aren't even remotely close to the requirement
|
1867
|
+
# start getting checked. Doing a pre-filter based on package name here
|
1868
|
+
# makes the process more consistent and easier to follow.
|
1869
|
+
installed_packages(pkgname).each do |pkg|
|
1744
1870
|
if req
|
1745
1871
|
if Tpkg::package_meets_requirement?(pkg, req)
|
1746
1872
|
pkgs << pkg
|
@@ -1796,9 +1922,7 @@ class Tpkg
|
|
1796
1922
|
# will be an array of package specs.
|
1797
1923
|
MAX_POSSIBLE_SOLUTIONS_TO_CHECK = 10000
|
1798
1924
|
def best_solution(requirements, packages, core_packages)
|
1799
|
-
|
1800
|
-
# change them without potentially messing up our caller
|
1801
|
-
result = resolve_dependencies(requirements.dup, {:tpkg => packages.dup, :native => {}}, core_packages.dup)
|
1925
|
+
result = resolve_dependencies(requirements, {:tpkg => packages, :native => {}}, core_packages)
|
1802
1926
|
if @@debug
|
1803
1927
|
if result[:solution]
|
1804
1928
|
puts "bestsol picks: #{result[:solution].inspect}" if @@debug
|
@@ -1820,6 +1944,10 @@ class Tpkg
|
|
1820
1944
|
# the same name. This may be necessary if different dependencies of the
|
1821
1945
|
# core packages end up needing both.
|
1822
1946
|
def resolve_dependencies(requirements, packages, core_packages, number_of_possible_solutions_checked=0)
|
1947
|
+
# We're probably going to make changes to packages, dup it now so
|
1948
|
+
# that we don't mess up the caller's state.
|
1949
|
+
packages = {:tpkg => packages[:tpkg].dup, :native => packages[:native].dup}
|
1950
|
+
|
1823
1951
|
# Make sure we have populated package lists for all requirements.
|
1824
1952
|
# Filter the package lists against the requirements and
|
1825
1953
|
# ensure we can at least satisfy the initial requirements.
|
@@ -1849,6 +1977,13 @@ class Tpkg
|
|
1849
1977
|
return {:number_of_possible_solutions_checked => number_of_possible_solutions_checked}
|
1850
1978
|
end
|
1851
1979
|
end
|
1980
|
+
|
1981
|
+
# FIXME: Should we weed out any entries in packages that don't correspond
|
1982
|
+
# to something in requirements? We operate later on the assumption that
|
1983
|
+
# there are no such entries. Because we dup packages at the right points
|
1984
|
+
# I believe we'll never accidently end up with orphaned entries, but maybe
|
1985
|
+
# it would be worth the compute cycles to make sure?
|
1986
|
+
|
1852
1987
|
# Sort the packages
|
1853
1988
|
[:tpkg, :native].each do |type|
|
1854
1989
|
packages[type].each do |pkgname, pkgs|
|
@@ -1857,7 +1992,8 @@ class Tpkg
|
|
1857
1992
|
# Anything else can score 1 at best. This ensures
|
1858
1993
|
# that we prefer the solution which leaves the most
|
1859
1994
|
# currently installed packages alone.
|
1860
|
-
if pkgs[0]
|
1995
|
+
if pkgs[0] &&
|
1996
|
+
pkgs[0][:source] != :currently_installed &&
|
1861
1997
|
pkgs[0][:source] != :native_installed
|
1862
1998
|
pkgs.unshift(nil)
|
1863
1999
|
end
|
@@ -2001,6 +2137,7 @@ class Tpkg
|
|
2001
2137
|
if sol[:remaining_noncoredepth] == 0
|
2002
2138
|
result = check_solution(sol, requirements, packages, core_packages, number_of_possible_solutions_checked)
|
2003
2139
|
if result[:solution]
|
2140
|
+
puts "resolvdeps returning successful solution" if @@debug
|
2004
2141
|
return result
|
2005
2142
|
else
|
2006
2143
|
number_of_possible_solutions_checked = result[:number_of_possible_solutions_checked]
|
@@ -2024,6 +2161,7 @@ class Tpkg
|
|
2024
2161
|
end
|
2025
2162
|
end
|
2026
2163
|
# No solutions found
|
2164
|
+
puts "resolvedeps returning failure" if @@debug
|
2027
2165
|
return {:number_of_possible_solutions_checked => number_of_possible_solutions_checked}
|
2028
2166
|
end
|
2029
2167
|
|
@@ -2036,7 +2174,7 @@ class Tpkg
|
|
2036
2174
|
end
|
2037
2175
|
|
2038
2176
|
if @@debug
|
2039
|
-
puts "checksol checking #{solution.inspect}"
|
2177
|
+
puts "checksol checking sol #{solution.inspect}"
|
2040
2178
|
end
|
2041
2179
|
|
2042
2180
|
# Extract dependencies from each package in the solution
|
@@ -2064,7 +2202,7 @@ class Tpkg
|
|
2064
2202
|
if packages[newreq[:type]][newreq[:name]]
|
2065
2203
|
pkg = solution[:pkgs].find{|solpkg| solpkg[:metadata][:name] == newreq[:name]}
|
2066
2204
|
puts "checksol newreq pkg: #{pkg.inspect}" if @@debug
|
2067
|
-
if Tpkg::package_meets_requirement?(pkg, newreq)
|
2205
|
+
if pkg && Tpkg::package_meets_requirement?(pkg, newreq)
|
2068
2206
|
# No change to solution needed
|
2069
2207
|
else
|
2070
2208
|
# Solution no longer works
|
@@ -2082,14 +2220,16 @@ class Tpkg
|
|
2082
2220
|
return {:solution => solution[:pkgs]}
|
2083
2221
|
else
|
2084
2222
|
puts "checksol newreqs need packages, calling resolvedeps" if @@debug
|
2085
|
-
result = resolve_dependencies(requirements+newreqs_that_need_packages, packages
|
2223
|
+
result = resolve_dependencies(requirements+newreqs_that_need_packages, packages, core_packages, number_of_possible_solutions_checked)
|
2086
2224
|
if result[:solution]
|
2225
|
+
puts "checksol returning successful solution" if @@debug
|
2087
2226
|
return result
|
2088
2227
|
else
|
2089
2228
|
number_of_possible_solutions_checked = result[:number_of_possible_solutions_checked]
|
2090
2229
|
end
|
2091
2230
|
end
|
2092
2231
|
end
|
2232
|
+
puts "checksol returning failure" if @@debug
|
2093
2233
|
return {:number_of_possible_solutions_checked => number_of_possible_solutions_checked}
|
2094
2234
|
end
|
2095
2235
|
|
@@ -2320,7 +2460,7 @@ class Tpkg
|
|
2320
2460
|
def unpack(package_file, passphrase=nil, options={})
|
2321
2461
|
ret_val = 0
|
2322
2462
|
metadata = Tpkg::metadata_from_package(package_file)
|
2323
|
-
|
2463
|
+
|
2324
2464
|
# Unpack files in a temporary directory
|
2325
2465
|
# I'd prefer to unpack on the fly so that the user doesn't need to
|
2326
2466
|
# have disk space to hold three copies of the package (the package
|
@@ -2330,7 +2470,7 @@ class Tpkg
|
|
2330
2470
|
# directory structure in the package.
|
2331
2471
|
topleveldir = Tpkg::package_toplevel_directory(package_file)
|
2332
2472
|
workdir = Tpkg::tempdir(topleveldir, @tmp_directory)
|
2333
|
-
system("#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{@tar} -C #{workdir} -xpf -")
|
2473
|
+
system("#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
|
2334
2474
|
files_info = {} # store perms, uid, gid, etc. for files
|
2335
2475
|
checksums_of_decrypted_files = {}
|
2336
2476
|
root_dir = File.join(workdir, 'tpkg', 'root')
|
@@ -2341,7 +2481,7 @@ class Tpkg
|
|
2341
2481
|
# Get list of conflicting files/directories & store their perm/ownership. That way, we can
|
2342
2482
|
# set them to the correct values later on in order to preserve them.
|
2343
2483
|
# TODO: verify this command works on all platforms
|
2344
|
-
files = `#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{@tar} -tf -`
|
2484
|
+
files = `#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions} | #{@tar} #{@@taroptions} -tf -`
|
2345
2485
|
files = files.split("\n")
|
2346
2486
|
conflicting_files = {}
|
2347
2487
|
files.each do | file |
|
@@ -2590,119 +2730,8 @@ class Tpkg
|
|
2590
2730
|
system("#{@tar} -C #{File.join(workdir, 'tpkg', 'reloc')} -cf - . | #{@tar} -C #{@base} -xpf -")
|
2591
2731
|
end
|
2592
2732
|
|
2593
|
-
|
2594
|
-
|
2595
|
-
# We don't have to any anything if there's already symlink to our init script.
|
2596
|
-
# This can happen if user removes pkg manually without removing
|
2597
|
-
# init symlink
|
2598
|
-
next if File.symlink?(link) && File.readlink(link) == init_script
|
2599
|
-
begin
|
2600
|
-
if !File.exist?(File.dirname(link))
|
2601
|
-
FileUtils.mkdir_p(File.dirname(link))
|
2602
|
-
end
|
2603
|
-
begin
|
2604
|
-
File.symlink(init_script, link)
|
2605
|
-
rescue Errno::EEXIST
|
2606
|
-
# The link name that init_links provides is not guaranteed to
|
2607
|
-
# be unique. It might collide with a base system init script
|
2608
|
-
# or an init script from another tpkg. If the link name
|
2609
|
-
# supplied by init_links results in EEXIST then try appending
|
2610
|
-
# a number to the end of the link name.
|
2611
|
-
catch :init_link_done do
|
2612
|
-
1.upto(9) do |i|
|
2613
|
-
begin
|
2614
|
-
File.symlink(init_script, link + i.to_s)
|
2615
|
-
throw :init_link_done
|
2616
|
-
rescue Errno::EEXIST
|
2617
|
-
end
|
2618
|
-
end
|
2619
|
-
# If we get here (i.e. we never reached the throw) then we
|
2620
|
-
# failed to create any of the possible link names.
|
2621
|
-
raise "Failed to install init script #{init_script} -> #{link} for #{File.basename(package_file)}"
|
2622
|
-
end
|
2623
|
-
end
|
2624
|
-
rescue Errno::EPERM
|
2625
|
-
# If creating the link fails due to permission problems and
|
2626
|
-
# we're not running as root just warn the user, allowing folks
|
2627
|
-
# to run tpkg as a non-root user with reduced functionality.
|
2628
|
-
if Process.euid == 0
|
2629
|
-
raise
|
2630
|
-
else
|
2631
|
-
warn "Failed to install init script for #{File.basename(package_file)}, probably due to lack of root privileges"
|
2632
|
-
end
|
2633
|
-
end
|
2634
|
-
end
|
2635
|
-
|
2636
|
-
# Install any crontabs
|
2637
|
-
crontab_destinations(metadata).each do |crontab, destination|
|
2638
|
-
begin
|
2639
|
-
if destination[:link]
|
2640
|
-
next if File.symlink?(destination[:link]) && File.readlink(destination[:link]) == crontab
|
2641
|
-
if !File.exist?(File.dirname(destination[:link]))
|
2642
|
-
FileUtils.mkdir_p(File.dirname(destination[:link]))
|
2643
|
-
end
|
2644
|
-
begin
|
2645
|
-
File.symlink(crontab, destination[:link])
|
2646
|
-
rescue Errno::EEXIST
|
2647
|
-
# The link name that crontab_destinations provides is not
|
2648
|
-
# guaranteed to be unique. It might collide with a base
|
2649
|
-
# system crontab or a crontab from another tpkg. If the
|
2650
|
-
# link name supplied by crontab_destinations results in
|
2651
|
-
# EEXIST then try appending a number to the end of the link
|
2652
|
-
# name.
|
2653
|
-
catch :crontab_link_done do
|
2654
|
-
1.upto(9) do |i|
|
2655
|
-
begin
|
2656
|
-
File.symlink(crontab, destination[:link] + i.to_s)
|
2657
|
-
throw :crontab_link_done
|
2658
|
-
rescue Errno::EEXIST
|
2659
|
-
end
|
2660
|
-
end
|
2661
|
-
# If we get here (i.e. we never reached the throw) then we
|
2662
|
-
# failed to create any of the possible link names.
|
2663
|
-
raise "Failed to install crontab #{crontab} -> #{destination[:link]} for #{File.basename(package_file)}"
|
2664
|
-
end
|
2665
|
-
end
|
2666
|
-
elsif destination[:file]
|
2667
|
-
if !File.exist?(File.dirname(destination[:file]))
|
2668
|
-
FileUtils.mkdir_p(File.dirname(destination[:file]))
|
2669
|
-
end
|
2670
|
-
tmpfile = Tempfile.new(File.basename(destination[:file]), File.dirname(destination[:file]))
|
2671
|
-
if File.exist?(destination[:file])
|
2672
|
-
# Match permissions and ownership of current crontab
|
2673
|
-
st = File.stat(destination[:file])
|
2674
|
-
File.chmod(st.mode & 07777, tmpfile.path)
|
2675
|
-
File.chown(st.uid, st.gid, tmpfile.path)
|
2676
|
-
# Insert the contents of the current crontab file
|
2677
|
-
File.open(destination[:file]) { |file| tmpfile.write(file.read) }
|
2678
|
-
end
|
2679
|
-
# Insert a header line so we can find this section to remove later
|
2680
|
-
tmpfile.puts "### TPKG START - #{@base} - #{File.basename(package_file)}"
|
2681
|
-
# Insert the package crontab contents
|
2682
|
-
crontab_contents = IO.read(crontab)
|
2683
|
-
tmpfile.write(crontab_contents)
|
2684
|
-
# Insert a newline if the crontab doesn't end with one
|
2685
|
-
if crontab_contents.chomp == crontab_contents
|
2686
|
-
tmpfile.puts
|
2687
|
-
end
|
2688
|
-
# Insert a footer line
|
2689
|
-
tmpfile.puts "### TPKG END - #{@base} - #{File.basename(package_file)}"
|
2690
|
-
tmpfile.close
|
2691
|
-
File.rename(tmpfile.path, destination[:file])
|
2692
|
-
# FIXME: On Solaris we should bounce cron or use the crontab
|
2693
|
-
# command, otherwise cron won't pick up the changes
|
2694
|
-
end
|
2695
|
-
rescue Errno::EPERM
|
2696
|
-
# If installing the crontab fails due to permission problems and
|
2697
|
-
# we're not running as root just warn the user, allowing folks
|
2698
|
-
# to run tpkg as a non-root user with reduced functionality.
|
2699
|
-
if Process.euid == 0
|
2700
|
-
raise
|
2701
|
-
else
|
2702
|
-
warn "Failed to install crontab for #{File.basename(package_file)}, probably due to lack of root privileges"
|
2703
|
-
end
|
2704
|
-
end
|
2705
|
-
end
|
2733
|
+
install_init_scripts(metadata)
|
2734
|
+
install_crontabs(metadata)
|
2706
2735
|
|
2707
2736
|
# Run postinstall script
|
2708
2737
|
if File.exist?(File.join(workdir, 'tpkg', 'postinstall'))
|
@@ -2773,6 +2802,243 @@ class Tpkg
|
|
2773
2802
|
return ret_val
|
2774
2803
|
end
|
2775
2804
|
|
2805
|
+
def install_init_scripts(metadata)
|
2806
|
+
init_links(metadata).each do |link, init_script|
|
2807
|
+
# We don't have to do anything if there's already symlink to our init
|
2808
|
+
# script. This can happen if the user removes a package manually without
|
2809
|
+
# removing the init symlink
|
2810
|
+
next if File.symlink?(link) && File.readlink(link) == init_script
|
2811
|
+
begin
|
2812
|
+
if !File.exist?(File.dirname(link))
|
2813
|
+
FileUtils.mkdir_p(File.dirname(link))
|
2814
|
+
end
|
2815
|
+
begin
|
2816
|
+
File.symlink(init_script, link)
|
2817
|
+
rescue Errno::EEXIST
|
2818
|
+
# The link name that init_links provides is not guaranteed to
|
2819
|
+
# be unique. It might collide with a base system init script
|
2820
|
+
# or an init script from another tpkg. If the link name
|
2821
|
+
# supplied by init_links results in EEXIST then try appending
|
2822
|
+
# a number to the end of the link name.
|
2823
|
+
catch :init_link_done do
|
2824
|
+
1.upto(9) do |i|
|
2825
|
+
begin
|
2826
|
+
File.symlink(init_script, link + i.to_s)
|
2827
|
+
throw :init_link_done
|
2828
|
+
rescue Errno::EEXIST
|
2829
|
+
end
|
2830
|
+
end
|
2831
|
+
# If we get here (i.e. we never reached the throw) then we
|
2832
|
+
# failed to create any of the possible link names.
|
2833
|
+
raise "Failed to install init script #{init_script} -> #{link} for #{File.basename(metadata[:filename].to_s)}, too many overlapping filenames"
|
2834
|
+
end
|
2835
|
+
end
|
2836
|
+
# EACCES for file/directory permissions issues
|
2837
|
+
rescue Errno::EACCES => e
|
2838
|
+
# If creating the link fails due to permission problems and
|
2839
|
+
# we're not running as root just warn the user, allowing folks
|
2840
|
+
# to run tpkg as a non-root user with reduced functionality.
|
2841
|
+
if Process.euid != 0
|
2842
|
+
warn "Failed to install init script for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
2843
|
+
else
|
2844
|
+
raise e
|
2845
|
+
end
|
2846
|
+
end
|
2847
|
+
end
|
2848
|
+
end
|
2849
|
+
def remove_init_scripts(metadata)
|
2850
|
+
init_links(metadata).each do |link, init_script|
|
2851
|
+
# The link we ended up making when we unpacked the package could be any
|
2852
|
+
# of a series (see the code in install_init_scripts for the reasoning),
|
2853
|
+
# we need to check them all.
|
2854
|
+
links = [link]
|
2855
|
+
links.concat((1..9).to_a.map { |i| link + i.to_s })
|
2856
|
+
links.each do |l|
|
2857
|
+
if File.symlink?(l) && File.readlink(l) == init_script
|
2858
|
+
begin
|
2859
|
+
File.delete(l)
|
2860
|
+
# EACCES for file/directory permissions issues
|
2861
|
+
rescue Errno::EACCES => e
|
2862
|
+
# If removing the link fails due to permission problems and
|
2863
|
+
# we're not running as root just warn the user, allowing folks
|
2864
|
+
# to run tpkg as a non-root user with reduced functionality.
|
2865
|
+
if Process.euid != 0
|
2866
|
+
warn "Failed to remove init script for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
2867
|
+
else
|
2868
|
+
raise e
|
2869
|
+
end
|
2870
|
+
end
|
2871
|
+
end
|
2872
|
+
end
|
2873
|
+
end
|
2874
|
+
end
|
2875
|
+
|
2876
|
+
def install_crontabs(metadata)
|
2877
|
+
crontab_destinations(metadata).each do |crontab, destination|
|
2878
|
+
begin
|
2879
|
+
if destination[:link]
|
2880
|
+
install_crontab_link(metadata, crontab, destination)
|
2881
|
+
elsif destination[:file]
|
2882
|
+
install_crontab_file(metadata, crontab, destination)
|
2883
|
+
end
|
2884
|
+
# EACCES for file/directory permissions issues
|
2885
|
+
rescue Errno::EACCES => e
|
2886
|
+
# If installing the crontab fails due to permission problems and
|
2887
|
+
# we're not running as root just warn the user, allowing folks
|
2888
|
+
# to run tpkg as a non-root user with reduced functionality.
|
2889
|
+
if Process.euid != 0
|
2890
|
+
warn "Failed to install crontab for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
2891
|
+
else
|
2892
|
+
raise e
|
2893
|
+
end
|
2894
|
+
rescue RuntimeError => e
|
2895
|
+
if e.message.include?('cannot generate tempfile') && Process.euid != 0
|
2896
|
+
warn "Failed to install crontab for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
2897
|
+
else
|
2898
|
+
raise e
|
2899
|
+
end
|
2900
|
+
end
|
2901
|
+
end
|
2902
|
+
end
|
2903
|
+
def install_crontab_link(metadata, crontab, destination)
|
2904
|
+
return if File.symlink?(destination[:link]) && File.readlink(destination[:link]) == crontab
|
2905
|
+
if !File.exist?(File.dirname(destination[:link]))
|
2906
|
+
FileUtils.mkdir_p(File.dirname(destination[:link]))
|
2907
|
+
end
|
2908
|
+
begin
|
2909
|
+
File.symlink(crontab, destination[:link])
|
2910
|
+
rescue Errno::EEXIST
|
2911
|
+
# The link name that crontab_destinations provides is not
|
2912
|
+
# guaranteed to be unique. It might collide with a base
|
2913
|
+
# system crontab or a crontab from another tpkg. If the
|
2914
|
+
# link name supplied by crontab_destinations results in
|
2915
|
+
# EEXIST then try appending a number to the end of the link
|
2916
|
+
# name.
|
2917
|
+
catch :crontab_link_done do
|
2918
|
+
1.upto(9) do |i|
|
2919
|
+
begin
|
2920
|
+
File.symlink(crontab, destination[:link] + i.to_s)
|
2921
|
+
throw :crontab_link_done
|
2922
|
+
rescue Errno::EEXIST
|
2923
|
+
end
|
2924
|
+
end
|
2925
|
+
# If we get here (i.e. we never reached the throw) then we
|
2926
|
+
# failed to create any of the possible link names.
|
2927
|
+
raise "Failed to install crontab #{crontab} -> #{destination[:link]} for #{File.basename(metadata[:filename].to_s)}, too many overlapping filenames"
|
2928
|
+
end
|
2929
|
+
end
|
2930
|
+
end
|
2931
|
+
def install_crontab_file(metadata, crontab, destination)
|
2932
|
+
if !File.exist?(File.dirname(destination[:file]))
|
2933
|
+
FileUtils.mkdir_p(File.dirname(destination[:file]))
|
2934
|
+
end
|
2935
|
+
tmpfile = Tempfile.new(File.basename(destination[:file]), File.dirname(destination[:file]))
|
2936
|
+
if File.exist?(destination[:file])
|
2937
|
+
# Match permissions and ownership of current crontab
|
2938
|
+
st = File.stat(destination[:file])
|
2939
|
+
begin
|
2940
|
+
File.chmod(st.mode & 07777, tmpfile.path)
|
2941
|
+
File.chown(st.uid, st.gid, tmpfile.path)
|
2942
|
+
# EPERM for attempts to chown/chmod as non-root user
|
2943
|
+
rescue Errno::EPERM => e
|
2944
|
+
# If installing the crontab fails due to permission problems and
|
2945
|
+
# we're not running as root just warn the user, allowing folks
|
2946
|
+
# to run tpkg as a non-root user with reduced functionality.
|
2947
|
+
if Process.euid != 0
|
2948
|
+
warn "Failed to install crontab for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
2949
|
+
else
|
2950
|
+
raise e
|
2951
|
+
end
|
2952
|
+
end
|
2953
|
+
# Insert the contents of the current crontab file
|
2954
|
+
File.open(destination[:file]) { |file| tmpfile.write(file.read) }
|
2955
|
+
end
|
2956
|
+
# Insert a header line so we can find this section to remove later
|
2957
|
+
tmpfile.puts "### TPKG START - #{@base} - #{File.basename(metadata[:filename].to_s)}"
|
2958
|
+
# Insert the package crontab contents
|
2959
|
+
crontab_contents = IO.read(crontab)
|
2960
|
+
tmpfile.write(crontab_contents)
|
2961
|
+
# Insert a newline if the crontab doesn't end with one
|
2962
|
+
if crontab_contents.chomp == crontab_contents
|
2963
|
+
tmpfile.puts
|
2964
|
+
end
|
2965
|
+
# Insert a footer line
|
2966
|
+
tmpfile.puts "### TPKG END - #{@base} - #{File.basename(metadata[:filename].to_s)}"
|
2967
|
+
tmpfile.close
|
2968
|
+
File.rename(tmpfile.path, destination[:file])
|
2969
|
+
# FIXME: On Solaris we should bounce cron or use the crontab
|
2970
|
+
# command, otherwise cron won't pick up the changes
|
2971
|
+
end
|
2972
|
+
def remove_crontabs(metadata)
|
2973
|
+
crontab_destinations(metadata).each do |crontab, destination|
|
2974
|
+
begin
|
2975
|
+
if destination[:link]
|
2976
|
+
remove_crontab_link(metadata, crontab, destination)
|
2977
|
+
elsif destination[:file]
|
2978
|
+
remove_crontab_file(metadata, crontab, destination)
|
2979
|
+
end
|
2980
|
+
# EACCES for file/directory permissions issues
|
2981
|
+
rescue Errno::EACCES => e
|
2982
|
+
# If removing the crontab fails due to permission problems and
|
2983
|
+
# we're not running as root just warn the user, allowing folks
|
2984
|
+
# to run tpkg as a non-root user with reduced functionality.
|
2985
|
+
if Process.euid != 0
|
2986
|
+
warn "Failed to remove crontab for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
2987
|
+
else
|
2988
|
+
raise e
|
2989
|
+
end
|
2990
|
+
end
|
2991
|
+
end
|
2992
|
+
end
|
2993
|
+
def remove_crontab_link(metadata, crontab, destination)
|
2994
|
+
# The link we ended up making when we unpacked the package could
|
2995
|
+
# be any of a series (see the code in unpack for the reasoning),
|
2996
|
+
# we need to check them all.
|
2997
|
+
links = [destination[:link]]
|
2998
|
+
links.concat((1..9).to_a.map { |i| destination[:link] + i.to_s })
|
2999
|
+
links.each do |l|
|
3000
|
+
if File.symlink?(l) && File.readlink(l) == crontab
|
3001
|
+
File.delete(l)
|
3002
|
+
end
|
3003
|
+
end
|
3004
|
+
end
|
3005
|
+
def remove_crontab_file(metadata, crontab, destination)
|
3006
|
+
if File.exist?(destination[:file])
|
3007
|
+
tmpfile = Tempfile.new(File.basename(destination[:file]), File.dirname(destination[:file]))
|
3008
|
+
# Match permissions and ownership of current crontab
|
3009
|
+
st = File.stat(destination[:file])
|
3010
|
+
begin
|
3011
|
+
File.chmod(st.mode & 07777, tmpfile.path)
|
3012
|
+
File.chown(st.uid, st.gid, tmpfile.path)
|
3013
|
+
# EPERM for attempts to chown/chmod as non-root user
|
3014
|
+
rescue Errno::EPERM => e
|
3015
|
+
# If installing the crontab fails due to permission problems and
|
3016
|
+
# we're not running as root just warn the user, allowing folks
|
3017
|
+
# to run tpkg as a non-root user with reduced functionality.
|
3018
|
+
if Process.euid != 0
|
3019
|
+
warn "Failed to install crontab for #{File.basename(metadata[:filename].to_s)}, probably due to lack of root privileges: #{e.message}"
|
3020
|
+
else
|
3021
|
+
raise
|
3022
|
+
end
|
3023
|
+
end
|
3024
|
+
# Remove section associated with this package
|
3025
|
+
skip = false
|
3026
|
+
IO.foreach(destination[:file]) do |line|
|
3027
|
+
if line == "### TPKG START - #{@base} - #{File.basename(metadata[:filename].to_s)}\n"
|
3028
|
+
skip = true
|
3029
|
+
elsif line == "### TPKG END - #{@base} - #{File.basename(metadata[:filename].to_s)}\n"
|
3030
|
+
skip = false
|
3031
|
+
elsif !skip
|
3032
|
+
tmpfile.write(line)
|
3033
|
+
end
|
3034
|
+
end
|
3035
|
+
tmpfile.close
|
3036
|
+
File.rename(tmpfile.path, destination[:file])
|
3037
|
+
# FIXME: On Solaris we should bounce cron or use the crontab
|
3038
|
+
# command, otherwise cron won't pick up the changes
|
3039
|
+
end
|
3040
|
+
end
|
3041
|
+
|
2776
3042
|
def requirements_for_currently_installed_package(pkgname=nil)
|
2777
3043
|
requirements = []
|
2778
3044
|
metadata_for_installed_packages.each do |metadata|
|
@@ -2833,7 +3099,7 @@ class Tpkg
|
|
2833
3099
|
req = Tpkg::parse_request(request, @installed_directory)
|
2834
3100
|
newreqs << req
|
2835
3101
|
|
2836
|
-
|
3102
|
+
puts "Initializing the list of possible packages for this req" if @@debug
|
2837
3103
|
if !packages[req[:name]]
|
2838
3104
|
packages[req[:name]] = available_packages_that_meet_requirement(req)
|
2839
3105
|
end
|
@@ -2843,7 +3109,14 @@ class Tpkg
|
|
2843
3109
|
source = nil
|
2844
3110
|
localpath = nil
|
2845
3111
|
if File.file?(request)
|
3112
|
+
raise "Invalid package filename #{request}" if !Tpkg::valid_pkg_filename?(request)
|
3113
|
+
|
2846
3114
|
puts "parse_requests treating request as a file" if @@debug
|
3115
|
+
|
3116
|
+
if request !~ /\.tpkg$/
|
3117
|
+
warn "Warning: Attempting to perform the request on #{File.expand_path(request)}. This might not be a valid package file."
|
3118
|
+
end
|
3119
|
+
|
2847
3120
|
localpath = request
|
2848
3121
|
metadata = Tpkg::metadata_from_package(request)
|
2849
3122
|
source = request
|
@@ -2892,7 +3165,8 @@ class Tpkg
|
|
2892
3165
|
good_package = true
|
2893
3166
|
metadata = pkg[:metadata]
|
2894
3167
|
req = { :name => metadata[:name], :type => :tpkg }
|
2895
|
-
# Quick sanity check that the package can be installed on this machine.
|
3168
|
+
# Quick sanity check that the package can be installed on this machine.
|
3169
|
+
puts "check_requests checking that available package for request works on this machine: #{pkg.inspect}" if @@debug
|
2896
3170
|
if !Tpkg::package_meets_requirement?(pkg, req)
|
2897
3171
|
possible_errors << " Requested package #{metadata[:filename]} doesn't match this machine's OS or architecture"
|
2898
3172
|
good_package = false
|
@@ -2901,6 +3175,7 @@ class Tpkg
|
|
2901
3175
|
# a sanity check that there is at least one package
|
2902
3176
|
# available for each dependency of this package
|
2903
3177
|
metadata[:dependencies].each do |depreq|
|
3178
|
+
puts "check_requests checking for available packages to satisfy dependency: #{depreq.inspect}" if @@debug
|
2904
3179
|
if available_packages_that_meet_requirement(depreq).empty? && !Tpkg::packages_meet_requirement?(packages.values.flatten, depreq)
|
2905
3180
|
possible_errors << " Requested package #{metadata[:filename]} depends on #{depreq.inspect}, no packages that satisfy that dependency are available"
|
2906
3181
|
good_package = false
|
@@ -3386,6 +3661,11 @@ class Tpkg
|
|
3386
3661
|
else
|
3387
3662
|
pkgfile = download(pkg[:source], pkg[:metadata][:filename])
|
3388
3663
|
end
|
3664
|
+
|
3665
|
+
if !Tpkg::valid_pkg_filename?(pkgfile)
|
3666
|
+
raise "Invalid package filename: #{pkgfile}"
|
3667
|
+
end
|
3668
|
+
|
3389
3669
|
if prompt_for_conflicting_files(pkgfile, CHECK_UPGRADE)
|
3390
3670
|
# If the old and new packages have overlapping externals flag them
|
3391
3671
|
# to be skipped so that the external isn't removed and then
|
@@ -3399,7 +3679,7 @@ class Tpkg
|
|
3399
3679
|
end if pkg[:metadata][:externals]
|
3400
3680
|
|
3401
3681
|
# Remove the old package if we haven't done so
|
3402
|
-
unless removed_pkgs.include?(pkg[:metadata][:name])
|
3682
|
+
unless oldpkgs.nil? or oldpkgs.empty? or removed_pkgs.include?(pkg[:metadata][:name])
|
3403
3683
|
remove([pkg[:metadata][:name]], :upgrade => true, :externals_to_skip => externals_to_skip)
|
3404
3684
|
removed_pkgs << pkg[:metadata][:name]
|
3405
3685
|
end
|
@@ -3441,6 +3721,7 @@ class Tpkg
|
|
3441
3721
|
|
3442
3722
|
packages_to_remove = nil
|
3443
3723
|
if requests
|
3724
|
+
requests.uniq! if requests.is_a?(Array)
|
3444
3725
|
packages_to_remove = []
|
3445
3726
|
requests.each do |request|
|
3446
3727
|
req = Tpkg::parse_request(request, @installed_directory)
|
@@ -3548,7 +3829,7 @@ class Tpkg
|
|
3548
3829
|
|
3549
3830
|
topleveldir = Tpkg::package_toplevel_directory(package_file)
|
3550
3831
|
workdir = Tpkg::tempdir(topleveldir, @tmp_directory)
|
3551
|
-
system("#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} | #{@tar} -C #{workdir} -xpf -")
|
3832
|
+
system("#{@tar} -xf #{package_file} -O #{File.join(topleveldir, 'tpkg.tar')} #{@@taroptions} | #{@tar} #{@@taroptions} -C #{workdir} -xpf -")
|
3552
3833
|
|
3553
3834
|
# Run preremove script
|
3554
3835
|
if File.exist?(File.join(workdir, 'tpkg', 'preremove'))
|
@@ -3571,86 +3852,9 @@ class Tpkg
|
|
3571
3852
|
# Switch back to our previous directory
|
3572
3853
|
Dir.chdir(pwd)
|
3573
3854
|
end
|
3574
|
-
|
3575
|
-
|
3576
|
-
|
3577
|
-
# The link we ended up making when we unpacked the package could
|
3578
|
-
# be any of a series (see the code in unpack for the reasoning),
|
3579
|
-
# we need to check them all.
|
3580
|
-
links = [link]
|
3581
|
-
links.concat((1..9).to_a.map { |i| link + i.to_s })
|
3582
|
-
links.each do |l|
|
3583
|
-
if File.symlink?(l) && File.readlink(l) == init_script
|
3584
|
-
begin
|
3585
|
-
File.delete(l)
|
3586
|
-
rescue Errno::EPERM
|
3587
|
-
if Process.euid == 0
|
3588
|
-
raise
|
3589
|
-
else
|
3590
|
-
warn "Failed to remove init script for #{File.basename(package_file)}, probably due to lack of root privileges"
|
3591
|
-
end
|
3592
|
-
end
|
3593
|
-
end
|
3594
|
-
end
|
3595
|
-
end
|
3596
|
-
|
3597
|
-
# Remove any crontabs
|
3598
|
-
crontab_destinations(pkg[:metadata]).each do |crontab, destination|
|
3599
|
-
begin
|
3600
|
-
if destination[:link]
|
3601
|
-
# The link we ended up making when we unpacked the package could
|
3602
|
-
# be any of a series (see the code in unpack for the reasoning),
|
3603
|
-
# we need to check them all.
|
3604
|
-
links = [destination[:link]]
|
3605
|
-
links.concat((1..9).to_a.map { |i| destination[:link] + i.to_s })
|
3606
|
-
links.each do |l|
|
3607
|
-
if File.symlink?(l) && File.readlink(l) == crontab
|
3608
|
-
begin
|
3609
|
-
File.delete(l)
|
3610
|
-
rescue Errno::EPERM
|
3611
|
-
if Process.euid == 0
|
3612
|
-
raise
|
3613
|
-
else
|
3614
|
-
warn "Failed to remove crontab for #{File.basename(package_file)}, probably due to lack of root privileges"
|
3615
|
-
end
|
3616
|
-
end
|
3617
|
-
end
|
3618
|
-
end
|
3619
|
-
elsif destination[:file]
|
3620
|
-
if File.exist?(destination[:file])
|
3621
|
-
tmpfile = Tempfile.new(File.basename(destination[:file]), File.dirname(destination[:file]))
|
3622
|
-
# Match permissions and ownership of current crontab
|
3623
|
-
st = File.stat(destination[:file])
|
3624
|
-
File.chmod(st.mode & 07777, tmpfile.path)
|
3625
|
-
File.chown(st.uid, st.gid, tmpfile.path)
|
3626
|
-
# Remove section associated with this package
|
3627
|
-
skip = false
|
3628
|
-
IO.foreach(destination[:file]) do |line|
|
3629
|
-
if line == "### TPKG START - #{@base} - #{File.basename(package_file)}\n"
|
3630
|
-
skip = true
|
3631
|
-
elsif line == "### TPKG END - #{@base} - #{File.basename(package_file)}\n"
|
3632
|
-
skip = false
|
3633
|
-
elsif !skip
|
3634
|
-
tmpfile.write(line)
|
3635
|
-
end
|
3636
|
-
end
|
3637
|
-
tmpfile.close
|
3638
|
-
File.rename(tmpfile.path, destination[:file])
|
3639
|
-
# FIXME: On Solaris we should bounce cron or use the crontab
|
3640
|
-
# command, otherwise cron won't pick up the changes
|
3641
|
-
end
|
3642
|
-
end
|
3643
|
-
rescue Errno::EPERM
|
3644
|
-
# If removing the crontab fails due to permission problems and
|
3645
|
-
# we're not running as root just warn the user, allowing folks
|
3646
|
-
# to run tpkg as a non-root user with reduced functionality.
|
3647
|
-
if Process.euid == 0
|
3648
|
-
raise
|
3649
|
-
else
|
3650
|
-
warn "Failed to remove crontab for #{File.basename(package_file)}, probably due to lack of root privileges"
|
3651
|
-
end
|
3652
|
-
end
|
3653
|
-
end
|
3855
|
+
|
3856
|
+
remove_init_scripts(pkg[:metadata])
|
3857
|
+
remove_crontabs(pkg[:metadata])
|
3654
3858
|
|
3655
3859
|
# Run any externals
|
3656
3860
|
pkg[:metadata][:externals].each do |external|
|
@@ -3678,6 +3882,12 @@ class Tpkg
|
|
3678
3882
|
end
|
3679
3883
|
rescue Errno::ENOENT
|
3680
3884
|
warn "File #{file} from package #{File.basename(package_file)} missing during remove"
|
3885
|
+
# I know it's bad to have a generic rescue for all exceptions, but in this case, there
|
3886
|
+
# can be many things that might go wrong when removing a file. We don't want tpkg
|
3887
|
+
# to crash and leave the packages in a bad state. It's better to catch
|
3888
|
+
# all exceptions and give the user some warnings.
|
3889
|
+
rescue
|
3890
|
+
warn "Failed to remove file #{file}."
|
3681
3891
|
end
|
3682
3892
|
end
|
3683
3893
|
|
@@ -3845,6 +4055,7 @@ class Tpkg
|
|
3845
4055
|
if requested_packages.nil?
|
3846
4056
|
packages_to_execute_on = installed_packages_that_meet_requirement(nil)
|
3847
4057
|
else
|
4058
|
+
requested_packages.uniq!
|
3848
4059
|
requested_packages.each do |request|
|
3849
4060
|
req = Tpkg::parse_request(request)
|
3850
4061
|
packages_to_execute_on.concat(installed_packages_that_meet_requirement(req))
|
@@ -3958,21 +4169,25 @@ class Tpkg
|
|
3958
4169
|
end
|
3959
4170
|
end
|
3960
4171
|
|
3961
|
-
# TODO: update server side to accept yaml data
|
3962
4172
|
def send_update_to_server
|
3963
4173
|
metadata = metadata_for_installed_packages.collect{|metadata| metadata.to_hash}
|
3964
4174
|
yml = YAML.dump(metadata)
|
3965
4175
|
begin
|
3966
|
-
|
3967
|
-
|
3968
|
-
|
3969
|
-
|
3970
|
-
|
3971
|
-
|
4176
|
+
response = nil
|
4177
|
+
# Need to set timeout otherwise tpkg can hang for a long time when having
|
4178
|
+
# problem talking to the reporter server.
|
4179
|
+
# I can't seem get net-ssh timeout to work so we'll just handle the timeout ourselves
|
4180
|
+
timeout(CONNECTION_TIMEOUT) do
|
4181
|
+
update_uri = URI.parse("#{@report_server}")
|
4182
|
+
http = Tpkg::gethttp(update_uri)
|
4183
|
+
request = {"yml"=>URI.escape(yml), "client"=>Facter['fqdn'].value}
|
4184
|
+
post = Net::HTTP::Post.new(update_uri.path)
|
4185
|
+
post.set_form_data(request)
|
4186
|
+
response = http.request(post)
|
4187
|
+
end
|
3972
4188
|
|
3973
4189
|
case response
|
3974
4190
|
when Net::HTTPSuccess
|
3975
|
-
# puts "Response from server:\n'#{response.body}'"
|
3976
4191
|
puts "Successfully send update to reporter server"
|
3977
4192
|
else
|
3978
4193
|
$stderr.puts response.body
|
@@ -3980,10 +4195,12 @@ class Tpkg
|
|
3980
4195
|
# just ignore error and give user warning
|
3981
4196
|
puts "Failed to send update to reporter server"
|
3982
4197
|
end
|
4198
|
+
rescue Timeout::Error
|
4199
|
+
puts "Timed out when trying to send update to reporter server"
|
3983
4200
|
rescue
|
3984
4201
|
puts "Failed to send update to reporter server"
|
3985
4202
|
end
|
3986
|
-
end
|
4203
|
+
end
|
3987
4204
|
|
3988
4205
|
# Build a dependency map of currently installed packages
|
3989
4206
|
# For example, if we have pkgB and pkgC which depends on pkgA, then
|