101 |
rodolico |
1 |
#! /usr/bin/env perl
|
|
|
2 |
|
|
|
3 |
use strict;
|
|
|
4 |
use warnings;
|
|
|
5 |
|
132 |
rodolico |
6 |
# install.pl
|
|
|
7 |
#
|
|
|
8 |
# installer for perl script, in this case, sysinfo
|
|
|
9 |
#
|
|
|
10 |
# Revision history
|
|
|
11 |
#
|
|
|
12 |
# Version 1.1.7 20161010 RWR
|
|
|
13 |
# Added ability to validate required libraries are installed
|
|
|
14 |
#
|
|
|
15 |
# version 1.2 20170327 RWR
|
|
|
16 |
# did some major modifications to correctly work on BSD systems also
|
|
|
17 |
#
|
|
|
18 |
# version 2.0 20190330 RWR
|
|
|
19 |
# changed it so all configs are YAML
|
|
|
20 |
#
|
|
|
21 |
# version 3.0 20191105 RWR
|
|
|
22 |
# set up so all options are presented on initial screen
|
|
|
23 |
# user can choose options to edit
|
|
|
24 |
# rest of install is automatic
|
138 |
rodolico |
25 |
#
|
|
|
26 |
# version 3.1 20191112 RWR
|
|
|
27 |
# Added logging. Log file will be written to the install directory with
|
|
|
28 |
# the application name.log as the name (application name taken from installer_config.pl)
|
144 |
rodolico |
29 |
#
|
|
|
30 |
# version 3.1.1 20191117 RWR
|
|
|
31 |
# Added the ability to verify cpan is installed and configured on systems which need it
|
|
|
32 |
#
|
156 |
rodolico |
33 |
# version 3.1.2 20200215 RWR
|
|
|
34 |
# Adding version comparisons
|
210 |
rodolico |
35 |
#
|
|
|
36 |
# version 3.3.0 20230308 RWR
|
211 |
rodolico |
37 |
# major revision. Allows --quiet (don't ask questions). Correctly handles when the source has been installed via a
|
210 |
rodolico |
38 |
# separate process. Mainly designed to allow checkout from svn repository.
|
101 |
rodolico |
39 |
|
132 |
rodolico |
40 |
# find our location and use it for searching for libraries
|
|
|
41 |
BEGIN {
|
|
|
42 |
use FindBin;
|
|
|
43 |
use File::Spec;
|
203 |
rodolico |
44 |
# use libraries from the directory this script is in
|
132 |
rodolico |
45 |
use lib File::Spec->catdir($FindBin::Bin);
|
203 |
rodolico |
46 |
# and its parent
|
|
|
47 |
use lib File::Spec->catdir( $FindBin::Bin . '/../' );
|
132 |
rodolico |
48 |
eval( 'use YAML::Tiny' );
|
|
|
49 |
}
|
|
|
50 |
|
211 |
rodolico |
51 |
use Cwd qw(abs_path);
|
101 |
rodolico |
52 |
|
211 |
rodolico |
53 |
my $installerDir = abs_path(File::Spec->catdir($FindBin::Bin) );
|
|
|
54 |
my $sourceDir = abs_path($installerDir . '/..');
|
|
|
55 |
|
156 |
rodolico |
56 |
use Digest::MD5 qw(md5_hex);
|
208 |
rodolico |
57 |
use File::Copy;
|
156 |
rodolico |
58 |
|
|
|
59 |
# define the version number
|
|
|
60 |
# see https://metacpan.org/pod/release/JPEACOCK/version-0.97/lib/version.pod
|
|
|
61 |
use version;
|
210 |
rodolico |
62 |
our $VERSION = version->declare("v3.003.000");
|
156 |
rodolico |
63 |
|
|
|
64 |
|
132 |
rodolico |
65 |
use Data::Dumper;
|
|
|
66 |
use File::Basename;
|
|
|
67 |
use Getopt::Long;
|
101 |
rodolico |
68 |
|
132 |
rodolico |
69 |
our %install;
|
|
|
70 |
our %operatingSystems;
|
|
|
71 |
our %libraries;
|
|
|
72 |
our %binaries;
|
206 |
rodolico |
73 |
# load the definitions from installer_config.pl
|
211 |
rodolico |
74 |
do "$installerDir/installer_config.pl";
|
138 |
rodolico |
75 |
#
|
|
|
76 |
# set up log file
|
|
|
77 |
my $logFile = $install{'application name'};
|
|
|
78 |
$logFile =~ s/ /_/g;
|
211 |
rodolico |
79 |
$logFile = "$installerDir/$logFile.log";
|
132 |
rodolico |
80 |
|
|
|
81 |
Getopt::Long::Configure ("bundling"); # allow -vd --os='debian'
|
|
|
82 |
my $dryRun = 0;
|
|
|
83 |
my $os;
|
|
|
84 |
my $help = 0;
|
|
|
85 |
my $version = 0;
|
206 |
rodolico |
86 |
my $quiet = 0;
|
214 |
rodolico |
87 |
my $seedFileName = '';
|
132 |
rodolico |
88 |
|
|
|
89 |
my @messages; # stores any messages we want to show up at the end
|
|
|
90 |
my @feedback; # store feedback when we execute command line actions
|
|
|
91 |
|
|
|
92 |
my %configuration;
|
|
|
93 |
|
|
|
94 |
# simple display if --help is passed
|
|
|
95 |
sub help {
|
|
|
96 |
my $oses = join( ' ', keys %operatingSystems );
|
156 |
rodolico |
97 |
use File::Basename;
|
|
|
98 |
print basename($0) . " $VERSION\n";
|
132 |
rodolico |
99 |
print <<END
|
156 |
rodolico |
100 |
$0 [options]
|
|
|
101 |
Options:
|
|
|
102 |
--os - osname is one of [$oses]
|
|
|
103 |
--dryrun - do not actually do anything, just tell you what I'd do
|
|
|
104 |
--version - display version and exit
|
206 |
rodolico |
105 |
--quiet - Do not ask questions, just do stuff
|
|
|
106 |
|
|
|
107 |
For an inplace upgrade, it is assumed the source code has been
|
|
|
108 |
downloaded and installed into the correct directories already
|
132 |
rodolico |
109 |
END
|
101 |
rodolico |
110 |
}
|
|
|
111 |
|
138 |
rodolico |
112 |
#######################################################
|
144 |
rodolico |
113 |
#
|
|
|
114 |
# timeStamp
|
|
|
115 |
#
|
|
|
116 |
# return current system date as YYYY-MM-DD HH:MM:SS
|
|
|
117 |
#
|
|
|
118 |
#######################################################
|
|
|
119 |
sub timeStamp {
|
|
|
120 |
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
|
|
|
121 |
return sprintf "%4d-%02d-%02d %02d:%02d:%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec;
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
sub yesno {
|
|
|
125 |
my ( $prompt, $default ) = @_;
|
|
|
126 |
$default = 'yes' unless $default;
|
|
|
127 |
my $answer = &getAnswer( $prompt, $default eq 'yes' ? ('yes','no' ) : ('no', 'yes') );
|
|
|
128 |
return lc( substr( $answer, 0, 1 ) ) eq 'y';
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
# prompt the user for a response, then allow them to enter it
|
|
|
132 |
# the first response is considered the default and is printed
|
|
|
133 |
# in all caps if more than one exists
|
|
|
134 |
# first answer is used if they simply press the Enter
|
|
|
135 |
# key. The response is returned all lower case if more than one
|
|
|
136 |
# exists.
|
|
|
137 |
# it is assumed
|
|
|
138 |
sub getAnswer {
|
|
|
139 |
my ( $prompt, @answers ) = @_;
|
|
|
140 |
$answers[0] = '' unless defined( $answers[0] );
|
|
|
141 |
my $default = $answers[0];
|
|
|
142 |
my $onlyOneAnswer = scalar( @answers ) == 1;
|
|
|
143 |
print $prompt . '[ ';
|
|
|
144 |
$answers[0] = uc $answers[0] unless $onlyOneAnswer;
|
|
|
145 |
print join( ' | ', @answers ) . ' ]: ';
|
|
|
146 |
my $thisAnswer = <>;
|
|
|
147 |
chomp $thisAnswer;
|
|
|
148 |
$thisAnswer = $default unless $thisAnswer;
|
|
|
149 |
return $thisAnswer;
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
|
|
|
153 |
#######################################################
|
138 |
rodolico |
154 |
# function to simply log things
|
|
|
155 |
# first parameter is the priority, if <= $logDef->{'log level'} will print
|
|
|
156 |
# all subsequent parameters assumed to be strings to sent to the log
|
|
|
157 |
# returns 0 on failure
|
|
|
158 |
# 1 on success
|
|
|
159 |
# 2 if priority > log level
|
|
|
160 |
# -1 if $logDef is unset
|
|
|
161 |
# currently, only logs to a file
|
|
|
162 |
#######################################################
|
|
|
163 |
sub logIt {
|
140 |
rodolico |
164 |
open LOG, ">>$logFile" or die "Could not append to $logFile: $!\n";
|
138 |
rodolico |
165 |
while ( my $t = shift ) {
|
|
|
166 |
print LOG &timeStamp() . "\t$t\n";
|
|
|
167 |
}
|
|
|
168 |
close LOG;
|
|
|
169 |
return 1;
|
|
|
170 |
}
|
101 |
rodolico |
171 |
|
138 |
rodolico |
172 |
|
132 |
rodolico |
173 |
# attempt to locate the operating system.
|
|
|
174 |
# if found, will set some defaults for it.
|
|
|
175 |
sub setUpOperatingSystemSpecific {
|
|
|
176 |
my ( $install, $operatingSystems, $os, $installDir ) = @_;
|
138 |
rodolico |
177 |
&logIt( 'Entering setUpOperatingSystemSpecific' );
|
|
|
178 |
&logIt( "They passed $os in as the \$os" );
|
132 |
rodolico |
179 |
if ( $os ) {
|
|
|
180 |
# We found the OS, set up some defaults
|
|
|
181 |
$$install{'os'} = $os;
|
138 |
rodolico |
182 |
&logIt( "Setting keys for operating system" );
|
132 |
rodolico |
183 |
# merge operatingSystems into install
|
|
|
184 |
foreach my $key ( keys %{$operatingSystems->{$os}} ) {
|
|
|
185 |
if ( $key eq 'files' ) {
|
|
|
186 |
$install->{'files'} = { %{$install->{'files'}}, %{$operatingSystems->{$os}->{'files'}} }
|
|
|
187 |
} else {
|
|
|
188 |
$install->{$key} = $operatingSystems->{ $os }->{$key};
|
|
|
189 |
}
|
|
|
190 |
} # if it is a known OS
|
|
|
191 |
} # if
|
|
|
192 |
return $os;
|
|
|
193 |
} # getOperatingSystem
|
|
|
194 |
|
|
|
195 |
# validates the libraries needed exist
|
|
|
196 |
# simply eval's each library. If it doesn't exist, creates a list of
|
|
|
197 |
# commands to be executed to install it, and displays missing libraries
|
|
|
198 |
# offering to install them if possible.
|
|
|
199 |
sub validateLibraries {
|
|
|
200 |
my ( $libraries, $os ) = @_;
|
138 |
rodolico |
201 |
&logIt( 'Entering validateLibraries' );
|
132 |
rodolico |
202 |
my %return;
|
|
|
203 |
foreach my $lib ( keys %$libraries ) {
|
138 |
rodolico |
204 |
&logIt( "Checking on library $lib" );
|
132 |
rodolico |
205 |
eval( "use $lib;" );
|
|
|
206 |
if ( $@ ) {
|
|
|
207 |
$return{ $lib } = $libraries->{$lib}->{$os} ? $libraries->{$lib}->{$os} : 'UNK';
|
|
|
208 |
}
|
|
|
209 |
}
|
|
|
210 |
return \%return;
|
|
|
211 |
} # validateLibraries
|
|
|
212 |
|
|
|
213 |
|
|
|
214 |
# check for any required binaries
|
|
|
215 |
sub validateBinaries {
|
|
|
216 |
my ( $binaries, $os ) = @_;
|
138 |
rodolico |
217 |
&logIt( 'Entering validateBinaries' );
|
132 |
rodolico |
218 |
my %return;
|
|
|
219 |
foreach my $bin ( keys %$binaries ) {
|
|
|
220 |
unless ( `which $bin` ) {
|
|
|
221 |
$return{$bin} = $binaries->{$bin}->{$os} ? $binaries->{$bin}->{$os} : 'UNK';
|
|
|
222 |
}
|
|
|
223 |
}
|
|
|
224 |
return \%return;
|
|
|
225 |
} # validateBinaries
|
|
|
226 |
|
|
|
227 |
# get some input from the user and decide how to install/upgrade/remove/whatever
|
|
|
228 |
sub getInstallActions {
|
|
|
229 |
my $install = shift;
|
138 |
rodolico |
230 |
&logIt( 'Entering getInstallActions' );
|
132 |
rodolico |
231 |
if ( -d $install->{'confdir'} ) {
|
|
|
232 |
$install->{'action'} = "upgrade";
|
|
|
233 |
} else {
|
|
|
234 |
$install->{'action'} = 'install';
|
|
|
235 |
}
|
|
|
236 |
$install->{'build config'} = 'Y';
|
|
|
237 |
$install->{'setup cron'} = 'Y';
|
101 |
rodolico |
238 |
}
|
|
|
239 |
|
132 |
rodolico |
240 |
# locate all items in $hash which have one of the $placeholder elements in it
|
|
|
241 |
# and replace, ie <binddir> is replaced with the actual binary directory
|
|
|
242 |
sub doPlaceholderSubstitution {
|
|
|
243 |
my ($hash, $placeholder) = @_;
|
138 |
rodolico |
244 |
&logIt( 'Entering doPlaceholderSubstitution' );
|
132 |
rodolico |
245 |
return if ref $hash ne 'HASH';
|
|
|
246 |
foreach my $key ( keys %$hash ) {
|
|
|
247 |
if ( ref( $$hash{$key} ) ) {
|
|
|
248 |
&doPlaceholderSubstitution( $$hash{$key}, $placeholder );
|
|
|
249 |
} else {
|
|
|
250 |
foreach my $place ( keys %$placeholder ) {
|
|
|
251 |
$$hash{$key} =~ s/$place/$$placeholder{$place}/;
|
|
|
252 |
} # foreach
|
|
|
253 |
} # if..else
|
|
|
254 |
} # foreach
|
|
|
255 |
return;
|
|
|
256 |
}
|
|
|
257 |
|
|
|
258 |
# This will go through and first, see if anything is a directory, in
|
|
|
259 |
# which case, we'll create new entries for all files in there.
|
|
|
260 |
# then, it will do keyword substitution of <bindir> and <confdir>
|
|
|
261 |
# to populate the target.
|
|
|
262 |
# When this is done, each file should have a source and target that is
|
|
|
263 |
# a fully qualified path and filename
|
208 |
rodolico |
264 |
sub massageInstallValues {
|
132 |
rodolico |
265 |
my ( $install, $sourceDir ) = @_;
|
211 |
rodolico |
266 |
&logIt( 'Entering massageInstallValues' );
|
132 |
rodolico |
267 |
my %placeHolders =
|
|
|
268 |
(
|
|
|
269 |
'<bindir>' => $$install{'bindir'},
|
|
|
270 |
'<confdir>' => $$install{'confdir'},
|
|
|
271 |
'<default owner>' => $$install{'default owner'},
|
|
|
272 |
'<default group>' => $$install{'default group'},
|
|
|
273 |
'<default permission>' => $$install{'default permission'},
|
|
|
274 |
'<installdir>' => $sourceDir
|
|
|
275 |
);
|
101 |
rodolico |
276 |
|
132 |
rodolico |
277 |
my $allFiles = $$install{'files'};
|
101 |
rodolico |
278 |
|
132 |
rodolico |
279 |
# find all directory entries and load files in that directory into $$install{'files'}
|
|
|
280 |
foreach my $dir ( keys %$allFiles ) {
|
|
|
281 |
if ( defined( $$allFiles{$dir}{'type'} ) && $$allFiles{$dir}{'type'} eq 'directory' ) {
|
138 |
rodolico |
282 |
&logIt( "Found directory $dir" );
|
132 |
rodolico |
283 |
if ( opendir( my $dh, "$sourceDir/$dir" ) ) {
|
|
|
284 |
my @files = map{ $dir . '/' . $_ } grep { ! /^\./ && -f "$sourceDir/$dir/$_" } readdir( $dh );
|
138 |
rodolico |
285 |
&logIt( "\tFound files " . join( ' ', @files ) );
|
132 |
rodolico |
286 |
foreach my $file ( @files ) {
|
|
|
287 |
$$allFiles{ $file }{'type'} = 'file';
|
|
|
288 |
if ( $dir eq 'modules' ) {
|
|
|
289 |
$$allFiles{ $file }{'permission'} = ( $file =~ m/$$install{'modules'}/ ) ? '0700' : '0600';
|
|
|
290 |
} else {
|
|
|
291 |
$$allFiles{ $file }{'permission'} = $$allFiles{ $dir }{'permission'};
|
|
|
292 |
}
|
|
|
293 |
$$allFiles{ $file }{'owner'} = $$allFiles{ $dir }{'owner'};
|
|
|
294 |
$$allFiles{ $file }{'target'} = $$allFiles{ $dir }{'target'};
|
|
|
295 |
} # foreach
|
|
|
296 |
closedir $dh;
|
|
|
297 |
} # if opendir
|
|
|
298 |
} # if it is a directory
|
|
|
299 |
} # foreach
|
|
|
300 |
# find all files, and set the source directory, and add the filename to
|
|
|
301 |
# the target
|
|
|
302 |
foreach my $file ( keys %$allFiles ) {
|
|
|
303 |
$$allFiles{$file}{'source'} = "$sourceDir/$file";
|
|
|
304 |
$$allFiles{$file}{'target'} .= "/$file";
|
|
|
305 |
} # foreach
|
101 |
rodolico |
306 |
|
132 |
rodolico |
307 |
# finally, do place holder substitution. This recursively replaces all keys
|
|
|
308 |
# in %placeHolders with the values.
|
|
|
309 |
&doPlaceholderSubstitution( $install, \%placeHolders );
|
101 |
rodolico |
310 |
|
144 |
rodolico |
311 |
&logIt( "Exiting populateSourceDir with values\n" . Dumper( $install ) ) ;
|
101 |
rodolico |
312 |
|
132 |
rodolico |
313 |
return 1;
|
|
|
314 |
} # populateSourceDir
|
|
|
315 |
|
|
|
316 |
sub GetPermission {
|
|
|
317 |
my $install = shift;
|
138 |
rodolico |
318 |
&logIt( 'Entering GetPermission' );
|
132 |
rodolico |
319 |
print "Ready to install, please verify the following\n";
|
|
|
320 |
print "A. Operating System: " . $install->{'os'} . "\n";
|
|
|
321 |
print "B. Installation Type: " . $install->{'action'} . "\n";
|
|
|
322 |
print "C. Using Seed file: " . $install->{'configuration seed file'} . "\n" if -e $install->{'configuration seed file'};
|
|
|
323 |
print "D. Target Binary Directory: " . $install->{'bindir'} . "\n";
|
|
|
324 |
print "E. Target Configuration Directory: " . $install->{'confdir'} . "\n";
|
|
|
325 |
print "F. Automatic Running: " . ( $install->{'crontab'} ? $install->{'crontab'} : 'No' ) . "\n";
|
|
|
326 |
print "G. Install Missing Perl Libraries\n";
|
|
|
327 |
foreach my $task ( sort keys %{ $install->{'missing libraries'} } ) {
|
144 |
rodolico |
328 |
print "\t$task -> " . $install->{'missing libraries'}->{$task}->{'command'} . ' ' . $install->{'missing libraries'}->{$task}->{'parameter'} . "\n";
|
101 |
rodolico |
329 |
}
|
132 |
rodolico |
330 |
print "H. Install Missing Binaries\n";
|
|
|
331 |
foreach my $task ( sort keys %{ $install->{'missing binaries'} } ) {
|
144 |
rodolico |
332 |
print "\t$task -> " . $install->{'missing binaries'}->{$task}->{'command'} . ' ' . $install->{'missing binaries'}->{$task}->{'parameter'} . "\n";
|
132 |
rodolico |
333 |
}
|
|
|
334 |
return &yesno( "Do you want to proceed?" );
|
101 |
rodolico |
335 |
}
|
|
|
336 |
|
132 |
rodolico |
337 |
# note, this fails badly if you stick non-numerics in the version number
|
|
|
338 |
# simply create a "number" from the version which may have an arbitrary
|
|
|
339 |
# number of digits separated by a period, for example
|
|
|
340 |
# 1.2.5
|
|
|
341 |
# while there are digits, divide current calculation by 100, then add the last
|
|
|
342 |
# digit popped of. So, 1.2.5 would become
|
|
|
343 |
# 1 + 2/100 + 5/10000, or 1.0205
|
|
|
344 |
# and 1.25.16 would become 1.2516
|
|
|
345 |
#
|
|
|
346 |
# Will give invalid results if any set of digits is more than 99
|
|
|
347 |
sub dotVersion2Number {
|
|
|
348 |
my $dotVersion = shift;
|
|
|
349 |
|
|
|
350 |
my @t = split( '\.', $dotVersion );
|
|
|
351 |
#print "\n$dotVersion\n" . join( "\n", @t ) . "\n";
|
|
|
352 |
my $return = 0;
|
|
|
353 |
while ( @t ) {
|
|
|
354 |
$return /= 100;
|
|
|
355 |
$return += pop @t;
|
|
|
356 |
}
|
|
|
357 |
#print "$return\n";
|
|
|
358 |
return $return;
|
|
|
359 |
}
|
|
|
360 |
|
|
|
361 |
# there is a file named VERSIONS. We get the values out of the install
|
|
|
362 |
# directory and (if it exists) the target so we can decide what needs
|
|
|
363 |
# to be updated.
|
|
|
364 |
sub getVersions {
|
|
|
365 |
my $install = shift;
|
138 |
rodolico |
366 |
&logIt( 'Entering getVersions' );
|
132 |
rodolico |
367 |
my $currentVersionFile = $install->{'files'}->{'VERSION'}->{'target'};
|
|
|
368 |
my $newVersionFile = $install->{'files'}->{'VERSION'}->{'source'};
|
|
|
369 |
if ( open FILE,"<$currentVersionFile" ) {
|
|
|
370 |
while ( my $line = <FILE> ) {
|
|
|
371 |
chomp $line;
|
|
|
372 |
my ( $filename, $version, $checksum ) = split( "\t", $line );
|
|
|
373 |
$install{'files'}->{$filename}->{'installed version'} = $version ? $version : '';
|
156 |
rodolico |
374 |
$install{'files'}->{$filename}->{'installed checksum'} = $checksum ? $checksum : '';
|
132 |
rodolico |
375 |
}
|
|
|
376 |
close FILE;
|
|
|
377 |
}
|
|
|
378 |
if ( open FILE,"<$newVersionFile" ) {
|
|
|
379 |
while ( my $line = <FILE> ) {
|
|
|
380 |
chomp $line;
|
|
|
381 |
my ( $filename, $version, $checksum ) = split( "\t", $line );
|
|
|
382 |
$install->{'files'}->{$filename}->{'new version'} = $version ? $version : '';
|
156 |
rodolico |
383 |
$install->{'files'}->{$filename}->{'new checksum'} = $checksum ? $checksum : '';
|
132 |
rodolico |
384 |
}
|
|
|
385 |
close FILE;
|
|
|
386 |
}
|
|
|
387 |
foreach my $file ( keys %{$$install{'files'}} ) {
|
156 |
rodolico |
388 |
$install{'files'}->{$file}->{'installed version'} = '' unless defined $install->{'files'}->{$file}->{'installed version'};
|
|
|
389 |
$install{'files'}->{$file}->{'new version'} = '' unless defined $install->{'files'}->{$file}->{'new version'};
|
132 |
rodolico |
390 |
}
|
|
|
391 |
return 1;
|
|
|
392 |
} # getVersions
|
|
|
393 |
|
144 |
rodolico |
394 |
# checks if a directory exists and, if not, creates it
|
|
|
395 |
my %directories; # holds list of directories already created so no need to do an I/O
|
132 |
rodolico |
396 |
|
144 |
rodolico |
397 |
sub checkDirectoryExists {
|
|
|
398 |
my ( $filename,$mod,$owner ) = @_;
|
|
|
399 |
$mod = "0700" unless $mod;
|
|
|
400 |
$owner = "root:root" unless $owner;
|
|
|
401 |
&logIt( "Checking Directory for $filename with $mod and $owner" );
|
|
|
402 |
my ($fn, $dirname) = fileparse( $filename );
|
|
|
403 |
logIt( "\tParsing out $dirname and $filename" );
|
|
|
404 |
return '' if exists $directories{$dirname};
|
|
|
405 |
if ( -d $dirname ) {
|
|
|
406 |
$directories{$dirname} = 1;
|
|
|
407 |
return '';
|
|
|
408 |
}
|
|
|
409 |
if ( &runCommand( "mkdir -p $dirname", "chmod $mod $dirname", "chown $owner $dirname" ) ) {
|
|
|
410 |
$directories{$dirname} = 1;
|
|
|
411 |
}
|
|
|
412 |
return '';
|
|
|
413 |
}
|
|
|
414 |
|
|
|
415 |
# runs a system command. Also, if in testing mode, simply shows what
|
|
|
416 |
# would have been done.
|
|
|
417 |
sub runCommand {
|
|
|
418 |
while ( my $command = shift ) {
|
214 |
rodolico |
419 |
print "Running command $command\n";
|
144 |
rodolico |
420 |
if ( $dryRun ) {
|
|
|
421 |
print "$command\n";
|
|
|
422 |
} else {
|
|
|
423 |
`$command`;
|
|
|
424 |
}
|
|
|
425 |
}
|
|
|
426 |
return 1;
|
|
|
427 |
} # runCommand
|
|
|
428 |
|
132 |
rodolico |
429 |
# this actually does the installation, except for the configuration
|
|
|
430 |
sub doInstall {
|
|
|
431 |
my $install = shift;
|
138 |
rodolico |
432 |
&logIt( 'Entering doInstall' );
|
132 |
rodolico |
433 |
my $fileList = $install->{'files'};
|
|
|
434 |
|
|
|
435 |
&checkDirectoryExists( $install->{'bindir'} . '/', $install->{'default permission'}, $install->{'default owner'} . ':' . $install->{'default group'} );
|
|
|
436 |
foreach my $file ( keys %$fileList ) {
|
|
|
437 |
next unless ( $fileList->{$file}->{'type'} && $fileList->{$file}->{'type'} eq 'file' );
|
|
|
438 |
|
156 |
rodolico |
439 |
# *********** Removed error checking to get this working; should reenable later
|
|
|
440 |
# if ( version->parse( $fileList->{$file}->{'installed version'} ) && version->parse( $fileList->{$file}->{'installed version'} ) < version->parse( $fileList->{$file}->{'new version'} ) ) {
|
|
|
441 |
# # we have a new version, so overwrite it
|
|
|
442 |
# } elsif ( $fileList->{$file}->{'installed checksum'} eq $fileList->{$file}->{'installed checksum'} ) { # has file been modified
|
|
|
443 |
# }
|
|
|
444 |
#
|
|
|
445 |
#
|
|
|
446 |
# next if $install->{'action'} eq 'upgrade' && ! defined( $fileList->{$file}->{'installed version'} )
|
|
|
447 |
# ||
|
|
|
448 |
# ( &dotVersion2Number( $fileList->{$file}->{'new version'} ) <= &dotVersion2Number($fileList->{$file}->{'installed version'} ) );
|
208 |
rodolico |
449 |
# check the directory and permissions, creating directory and setting permissions if necessary
|
132 |
rodolico |
450 |
&checkDirectoryExists( $fileList->{$file}->{'target'}, $install->{'default permission'}, $install->{'default owner'} . ':' . $install->{'default group'} );
|
208 |
rodolico |
451 |
# copy the files to the new directory
|
211 |
rodolico |
452 |
&runCommand( "cp $fileList->{$file}->{'source'} $fileList->{$file}->{'target'}" ) unless $fileList->{$file}->{'source'} eq $fileList->{$file}->{'target'};
|
|
|
453 |
&runCommand( "chmod $fileList->{$file}->{'permission'} $fileList->{$file}->{'target'}",
|
|
|
454 |
"chown $fileList->{$file}->{'owner'} $fileList->{$file}->{'target'}"
|
|
|
455 |
) if -e $fileList->{$file}->{'target'};
|
132 |
rodolico |
456 |
# if there is a post action, run it and store any return in @feedback
|
|
|
457 |
push @feedback, `$fileList->{$file}->{'post action'}` if defined $fileList->{$file}->{'post action'};
|
|
|
458 |
# if there is a message to be passed, store it in @messages
|
156 |
rodolico |
459 |
push @messages, $fileList->{$file}->{'message'} if defined $fileList->{$file}->{'message'};
|
132 |
rodolico |
460 |
} # foreach file
|
|
|
461 |
return 1;
|
|
|
462 |
}
|
|
|
463 |
|
|
|
464 |
|
|
|
465 |
# installs binaries and libraries
|
|
|
466 |
sub installOSStuff {
|
|
|
467 |
my $install = shift;
|
144 |
rodolico |
468 |
my %commands;
|
132 |
rodolico |
469 |
my @actions = values( %{ $install->{'missing libraries'} } );
|
|
|
470 |
push @actions, values( %{ $install->{'missing binaries'} } );
|
|
|
471 |
foreach my $action ( @actions ) {
|
144 |
rodolico |
472 |
$commands{$action->{'command'}} .= ' ' . $action->{'parameter'};
|
211 |
rodolico |
473 |
#&logIt( $action );
|
|
|
474 |
#&runCommand( $action );
|
132 |
rodolico |
475 |
}
|
144 |
rodolico |
476 |
foreach my $command ( keys %commands ) {
|
208 |
rodolico |
477 |
&logIt( $command );
|
|
|
478 |
print "Running command $command $commands{$command}\n" unless $quiet;
|
144 |
rodolico |
479 |
`$command $commands{$command}`;
|
|
|
480 |
}
|
132 |
rodolico |
481 |
}
|
144 |
rodolico |
482 |
|
|
|
483 |
################################################################
|
|
|
484 |
# validateCPAN
|
|
|
485 |
#
|
|
|
486 |
# some of the systems will need to run cpan to get some perl modules.
|
|
|
487 |
# this will go through each of them and see if command starts with cpan
|
|
|
488 |
# and, if so, will check that cpan is installed and configured for root.
|
|
|
489 |
#
|
|
|
490 |
# when cpan is installed, it requires one manual run as root from the cli
|
|
|
491 |
# to determine where to get the files. If that is not done, cpan can not
|
|
|
492 |
# be controlled by a program. We check to see if cpan is installed, then
|
|
|
493 |
# verify /root/.cpan has been created by the configuration tool
|
|
|
494 |
################################################################
|
|
|
495 |
|
|
|
496 |
|
|
|
497 |
sub validateCPAN {
|
|
|
498 |
my $libraries = shift;
|
|
|
499 |
my $needCPAN = 0;
|
|
|
500 |
foreach my $app ( keys %$libraries ) {
|
|
|
501 |
if ( $libraries->{$app}->{'command'} =~ m/^cpan/ ) {
|
|
|
502 |
$needCPAN = 1;
|
|
|
503 |
last;
|
|
|
504 |
}
|
|
|
505 |
}
|
|
|
506 |
return unless $needCPAN;
|
|
|
507 |
if ( `which cpan` ) {
|
|
|
508 |
die "****ERROR****\nWe need cpan, and it is installed, but not configured\nrun cpan as root one time to configure it\n" unless -d '/root/.cpan';
|
|
|
509 |
} else {
|
|
|
510 |
die 'In order to install on this OS, we need cpan, which should have been installed with perl.' .
|
|
|
511 |
" Can not continue until cpan is installed and configured\n";
|
|
|
512 |
}
|
208 |
rodolico |
513 |
}
|
|
|
514 |
|
|
|
515 |
# set up the config file.
|
|
|
516 |
# at worst, we'll have an empty configuration file
|
|
|
517 |
sub checkConfig {
|
|
|
518 |
my $install = shift;
|
|
|
519 |
# if the configuration file does not exist, create one
|
|
|
520 |
if ( not -e $install->{'configuration'}->{'configuration file'} ) {
|
|
|
521 |
&logIt( "Configuration file " . $install->{'configuration'}->{'configuration file'} . " does not exist, creating" );
|
|
|
522 |
&checkDirectoryExists(
|
|
|
523 |
$install->{'configuration'}->{'configuration file'},
|
|
|
524 |
$install->{'configuration'}->{'permission'},
|
|
|
525 |
$install->{'configuration'}->{'owner'}
|
|
|
526 |
);
|
|
|
527 |
if ( -e $install->{'configuration seed file'} ) {
|
|
|
528 |
&logIt( "Seed file " . $install->{'configuration seed file'} . " exists, using as default" );
|
|
|
529 |
copy( $install->{'configuration seed file'}, $install->{'configuration'}->{'configuration file'} );
|
|
|
530 |
} else { # we don't have a seed, and we don't have a configuration file, so just give it an empty one
|
|
|
531 |
&logIt( "No seed file or configuration file, creating an empty YAML" );
|
|
|
532 |
open YAML, '>' . $install->{'configuration'}->{'configuration file'}
|
|
|
533 |
or die "Could not create configuration file " . $install->{'configuration'}->{'configuration file'} . ": $!\n";
|
|
|
534 |
print YAML "# This is an empty configuration file\n---\n";
|
|
|
535 |
close YAML;
|
|
|
536 |
}
|
|
|
537 |
}
|
|
|
538 |
}
|
|
|
539 |
|
|
|
540 |
# create a cron entry if necessary
|
|
|
541 |
sub cronTab {
|
|
|
542 |
my $install = shift;
|
|
|
543 |
# set up crontab, if necessary
|
|
|
544 |
&logIt("Setting up crontab as " . $install->{'crontab'} );
|
|
|
545 |
&runCommand( $install->{'crontab'} ) if defined ( $install->{'crontab'} );
|
144 |
rodolico |
546 |
}
|
211 |
rodolico |
547 |
|
132 |
rodolico |
548 |
|
|
|
549 |
################################################################
|
|
|
550 |
# Main Code #
|
|
|
551 |
################################################################
|
|
|
552 |
|
|
|
553 |
# handle any command line parameters that may have been passed in
|
|
|
554 |
|
|
|
555 |
GetOptions (
|
|
|
556 |
"os|o=s" => \$os, # pass in the operating system
|
|
|
557 |
"dryrun|n" => \$dryRun, # do NOT actually do anything
|
214 |
rodolico |
558 |
"seed|s=s" => \$seedFileName,
|
132 |
rodolico |
559 |
'help|h' => \$help,
|
206 |
rodolico |
560 |
'version|v' => \$version,
|
211 |
rodolico |
561 |
'quiet|q' => \$quiet
|
132 |
rodolico |
562 |
) or die "Error parsing command line\n";
|
|
|
563 |
|
|
|
564 |
if ( $help ) { &help() ; exit; }
|
156 |
rodolico |
565 |
if ( $version ) { use File::Basename; print basename($0) . " $VERSION\n"; exit; }
|
132 |
rodolico |
566 |
|
211 |
rodolico |
567 |
|
156 |
rodolico |
568 |
print "Logging to $logFile\n";
|
211 |
rodolico |
569 |
|
138 |
rodolico |
570 |
&logIt( 'Beginning installation' );
|
|
|
571 |
|
208 |
rodolico |
572 |
# determine the operating system and set up installer hash with correct values
|
211 |
rodolico |
573 |
$install{'os'} = &setUpOperatingSystemSpecific( \%install, \%operatingSystems, $os ? $os : `$installerDir/determineOS`, $sourceDir );
|
138 |
rodolico |
574 |
&logIt( "Operating System is $install{'os'}" );
|
|
|
575 |
|
208 |
rodolico |
576 |
# check libraries necessary are loaded, and if not, track the ones we need
|
132 |
rodolico |
577 |
$install{'missing libraries'} = &validateLibraries( \%libraries, $install{'os'} );
|
144 |
rodolico |
578 |
&logIt( "Missing Libraries\n" . Dumper( $install{'missing libraries'} ) );
|
138 |
rodolico |
579 |
|
208 |
rodolico |
580 |
# Check that required binaries are installed and create list of the ones we need
|
132 |
rodolico |
581 |
$install{'missing binaries'} = &validateBinaries( \%binaries, $install{'os'} );
|
144 |
rodolico |
582 |
&logIt( "Missing binaries\n" . Dumper( $install{'missing binaries'} ) );
|
138 |
rodolico |
583 |
|
208 |
rodolico |
584 |
# if we need libraries and the os needs to use CPAN, validate CPAN
|
|
|
585 |
&validateCPAN( $install{'missing libraries'} ) if ( $install{'missing libraries'} );
|
144 |
rodolico |
586 |
|
208 |
rodolico |
587 |
# determine if it is an upgrade or fresh install and default to creating conf and cron
|
132 |
rodolico |
588 |
&getInstallActions( \%install );
|
144 |
rodolico |
589 |
&logIt( "Completed getInstallActions\n" . Dumper( \%install ) );
|
138 |
rodolico |
590 |
|
208 |
rodolico |
591 |
# go through %install and replace all variables with correct values
|
|
|
592 |
# moving directories as needed in the %install
|
|
|
593 |
&massageInstallValues( \%install, $sourceDir );
|
132 |
rodolico |
594 |
|
208 |
rodolico |
595 |
#print Dumper( \%install ) ; die;
|
|
|
596 |
|
|
|
597 |
# ask permission if $quiet not set
|
206 |
rodolico |
598 |
if ( $quiet || &GetPermission( \%install ) ) {
|
208 |
rodolico |
599 |
# install binaries and libraries as needed
|
132 |
rodolico |
600 |
&installOSStuff( \%install );
|
208 |
rodolico |
601 |
# do a version comparison unless we already have it in place, or it is a new install
|
211 |
rodolico |
602 |
&getVersions( \%install );
|
208 |
rodolico |
603 |
# move the binaries in place unless they are already there
|
211 |
rodolico |
604 |
&doInstall( \%install );
|
132 |
rodolico |
605 |
} else {
|
|
|
606 |
die "Please fix whatever needs to be done and try again\n";
|
138 |
rodolico |
607 |
&logIt( "User chose to kill process" );
|
132 |
rodolico |
608 |
}
|
|
|
609 |
|
210 |
rodolico |
610 |
# hack to set permissions.
|
|
|
611 |
`chown root:root $install{bindir}`;
|
|
|
612 |
`chmod 700 $install{bindir}`;
|
|
|
613 |
|
208 |
rodolico |
614 |
# set up automatic running
|
|
|
615 |
&cronTab( \%install );
|
|
|
616 |
# make sure something is in the configuration file
|
|
|
617 |
&checkConfig( \%install );
|
|
|
618 |
|
144 |
rodolico |
619 |
&logIt( "Installation done, running \&postInstall if it exists" );
|
132 |
rodolico |
620 |
|
217 |
rodolico |
621 |
my $configCommand = $install{'bindir'} . '/configure.pl -t -q -o ' . $install{'configuration'}{'configuration file'};
|
216 |
rodolico |
622 |
$configCommand .= " -f $seedFileName" if ( $seedFileName );
|
214 |
rodolico |
623 |
|
|
|
624 |
#my $configCommand = &postInstall( \%install, $quiet ) if defined( &postInstall );
|
216 |
rodolico |
625 |
|
211 |
rodolico |
626 |
print "$configCommand\n";
|
144 |
rodolico |
627 |
|
|
|
628 |
1;
|