Subversion Repositories camp_sysinfo_client_3

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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
101 rodolico 25
 
132 rodolico 26
our $VERSION = '3.0';
27
 
28
# find our location and use it for searching for libraries
29
BEGIN {
30
   use FindBin;
31
   use File::Spec;
32
   use lib File::Spec->catdir($FindBin::Bin);
33
   eval( 'use YAML::Tiny' );
34
}
35
 
101 rodolico 36
my $sourceDir = File::Spec->catdir($FindBin::Bin);
37
 
132 rodolico 38
use sysinfoconf;
39
use Data::Dumper;
40
use File::Basename;
41
use Getopt::Long;
101 rodolico 42
 
132 rodolico 43
our %install;
44
our %operatingSystems;
45
our %libraries;
46
our %binaries;
47
 
48
do "$sourceDir/installer_config.pl";
49
 
50
Getopt::Long::Configure ("bundling"); # allow -vd --os='debian'
51
# $verbose can have the following values
52
# 0 - do everything
53
# 1 - Do everything except the install, display what would be done
54
# 2 - Be verbose to STDERR
55
# 3 - Be very verbose
56
my $verbose = 0; # if test mode, simply show what would be done
57
my $dryRun = 0;
58
my $os;
59
my $help = 0;
60
my $version = 0;
61
 
62
my @messages; # stores any messages we want to show up at the end
63
my @feedback; # store feedback when we execute command line actions
64
 
65
my %configuration;
66
 
67
# simple display if --help is passed
68
sub help {
69
   my $oses = join( ' ', keys %operatingSystems );
70
   print <<END
71
$0 --verbose=x --os="osname" --dryrun --help --version
72
 
73
--os      - osname is one of [$oses]
74
--dryrun  - do not actually do anything, just tell you what I'd do
75
--verbose - x is 0 (normal) to 3 (horrible)
76
END
101 rodolico 77
}
78
 
79
 
132 rodolico 80
# attempt to locate the operating system.
81
# if found, will set some defaults for it.
82
sub setUpOperatingSystemSpecific {
83
   my ( $install, $operatingSystems, $os, $installDir ) = @_;
84
   print "They passed $os in as the \$os\n" if $verbose > 2;
85
   if ( $os ) {
86
      # We found the OS, set up some defaults
87
      $$install{'os'} = $os;
88
      print "Setting keys for operating system\n" if $verbose > 2;
89
      # merge operatingSystems into install
90
      foreach my $key ( keys %{$operatingSystems->{$os}} ) {
91
         if ( $key eq 'files' ) {
92
            $install->{'files'} = { %{$install->{'files'}}, %{$operatingSystems->{$os}->{'files'}} }
93
         } else {
94
            $install->{$key} = $operatingSystems->{ $os }->{$key};
95
         }
96
      } # if it is a known OS
97
   } # if
98
   return $os;
99
} # getOperatingSystem
100
 
101
# validates the libraries needed exist
102
# simply eval's each library. If it doesn't exist, creates a list of
103
# commands to be executed to install it, and displays missing libraries
104
# offering to install them if possible.
105
sub validateLibraries {
106
   my ( $libraries, $os ) = @_;
107
   my %return;
108
   foreach my $lib ( keys %$libraries ) {
109
      print "Checking on libarary $lib\n" if $verbose;
110
      eval( "use $lib;" );
111
      if ( $@ ) {
112
         $return{ $lib } = $libraries->{$lib}->{$os} ? $libraries->{$lib}->{$os} : 'UNK';
113
      }
114
   }
115
   return \%return;
116
} # validateLibraries
117
 
118
 
119
# check for any required binaries
120
sub validateBinaries {
121
   my ( $binaries, $os ) = @_;
122
   my %return;
123
   foreach my $bin ( keys %$binaries ) {
124
      unless ( `which $bin` ) {
125
         $return{$bin} = $binaries->{$bin}->{$os} ? $binaries->{$bin}->{$os} : 'UNK';
126
      }
127
   }
128
   return \%return;
129
} # validateBinaries
130
 
131
# get some input from the user and decide how to install/upgrade/remove/whatever
132
sub getInstallActions {
133
   my $install = shift;
134
   if ( -d $install->{'confdir'} ) {
135
      $install->{'action'} = "upgrade";
136
   } else {
137
      $install->{'action'} = 'install';
138
   }
139
   $install->{'build config'} = 'Y';
140
   $install->{'setup cron'} = 'Y';
101 rodolico 141
}
142
 
132 rodolico 143
# locate all items in $hash which have one of the $placeholder elements in it
144
# and replace, ie <binddir> is replaced with the actual binary directory
145
sub doPlaceholderSubstitution {
146
   my ($hash, $placeholder) = @_;
147
   return if ref $hash ne 'HASH';
148
   foreach my $key ( keys %$hash ) {
149
      if ( ref( $$hash{$key} ) ) {
150
         &doPlaceholderSubstitution( $$hash{$key}, $placeholder );
151
      } else {
152
         foreach my $place ( keys %$placeholder ) {
153
            $$hash{$key} =~ s/$place/$$placeholder{$place}/;
154
         } # foreach
155
      } # if..else
156
   } # foreach
157
   return;
158
}
159
 
160
# This will go through and first, see if anything is a directory, in
161
# which case, we'll create new entries for all files in there.
162
# then, it will do keyword substitution of <bindir> and <confdir>
163
# to populate the target.
164
# When this is done, each file should have a source and target that is
165
# a fully qualified path and filename
166
sub populateSourceDir {
167
   my ( $install, $sourceDir ) = @_;
168
   my %placeHolders = 
169
      ( 
170
        '<bindir>' => $$install{'bindir'},
171
        '<confdir>' => $$install{'confdir'},
172
        '<default owner>' => $$install{'default owner'},
173
        '<default group>' => $$install{'default group'},
174
        '<default permission>' => $$install{'default permission'},
175
        '<installdir>' => $sourceDir
176
      );
101 rodolico 177
 
132 rodolico 178
   my $allFiles = $$install{'files'};
101 rodolico 179
 
132 rodolico 180
   # find all directory entries and load files in that directory into $$install{'files'}
181
   foreach my $dir ( keys %$allFiles ) {
182
      if ( defined( $$allFiles{$dir}{'type'} ) && $$allFiles{$dir}{'type'} eq 'directory' ) {
183
         print "Found directory $dir\n" if $verbose > 2;
184
         if ( opendir( my $dh, "$sourceDir/$dir" ) ) {
185
            my @files = map{ $dir . '/' . $_ } grep { ! /^\./ && -f "$sourceDir/$dir/$_" } readdir( $dh );
186
            print "\tFound files " . join( ' ', @files ) . "\n" if $verbose > 2;
187
            foreach my $file ( @files ) {
188
               $$allFiles{ $file }{'type'} = 'file';
189
               if ( $dir eq 'modules' ) {
190
                  $$allFiles{ $file }{'permission'} = ( $file =~ m/$$install{'modules'}/ ) ? '0700' : '0600';
191
               } else {
192
                  $$allFiles{ $file }{'permission'} = $$allFiles{ $dir }{'permission'};
193
               }
194
               $$allFiles{ $file }{'owner'} = $$allFiles{ $dir }{'owner'};
195
               $$allFiles{ $file }{'target'} = $$allFiles{ $dir }{'target'};
196
            } # foreach
197
            closedir $dh;
198
         } # if opendir
199
      } # if it is a directory
200
   } # foreach
201
   # find all files, and set the source directory, and add the filename to
202
   # the target
203
   foreach my $file ( keys %$allFiles ) {
204
      $$allFiles{$file}{'source'} = "$sourceDir/$file";
205
      $$allFiles{$file}{'target'} .= "/$file";
206
   } # foreach
101 rodolico 207
 
132 rodolico 208
   # finally, do place holder substitution. This recursively replaces all keys
209
   # in  %placeHolders with the values.
210
   &doPlaceholderSubstitution( $install, \%placeHolders );
101 rodolico 211
 
132 rodolico 212
   print Dump( $install ) if $verbose > 2;
101 rodolico 213
 
132 rodolico 214
   return 1;
215
} # populateSourceDir
216
 
217
sub GetPermission {
218
   my $install = shift;
219
   print "Ready to install, please verify the following\n";
220
   print "A. Operating System:  " . $install->{'os'} . "\n";
221
   print "B. Installation Type: " . $install->{'action'} . "\n";
222
   print "C. Using Seed file: " . $install->{'configuration seed file'} . "\n" if -e $install->{'configuration seed file'};
223
   print "D. Target Binary Directory: " . $install->{'bindir'} . "\n";
224
   print "E. Target Configuration Directory: " . $install->{'confdir'} . "\n";
225
   print "F. Automatic Running: " . ( $install->{'crontab'} ? $install->{'crontab'} : 'No' ) . "\n";
226
   print "G. Install Missing Perl Libraries\n";
227
   foreach my $task ( sort keys %{ $install->{'missing libraries'} } ) {
228
      print "\t$task -> " . $install->{'missing libraries'}->{$task} . "\n";
101 rodolico 229
   }
132 rodolico 230
   print "H. Install Missing Binaries\n";
231
   foreach my $task ( sort keys %{ $install->{'missing binaries'} } ) {
232
      print "\t$task -> " . $install->{'missing binaries'}->{$task} . "\n";
233
   }
234
   return &yesno( "Do you want to proceed?" );
101 rodolico 235
}
236
 
132 rodolico 237
# note, this fails badly if you stick non-numerics in the version number
238
# simply create a "number" from the version which may have an arbitrary
239
# number of digits separated by a period, for example
240
# 1.2.5
241
# while there are digits, divide current calculation by 100, then add the last
242
# digit popped of. So, 1.2.5 would become
243
# 1 + 2/100 + 5/10000, or 1.0205
244
# and 1.25.16 would become 1.2516
245
# 
246
# Will give invalid results if any set of digits is more than 99
247
sub dotVersion2Number {
248
   my $dotVersion = shift;
249
 
250
   my @t = split( '\.', $dotVersion );
251
   #print "\n$dotVersion\n" . join( "\n", @t ) . "\n";
252
   my $return = 0;
253
   while ( @t ) {
254
      $return /= 100;
255
      $return += pop @t;
256
   }
257
   #print "$return\n";
258
   return $return;
259
}
260
 
261
# there is a file named VERSIONS. We get the values out of the install
262
# directory and (if it exists) the target so we can decide what needs
263
# to be updated.
264
sub getVersions {
265
   my $install = shift;
266
   my $currentVersionFile = $install->{'files'}->{'VERSION'}->{'target'};
267
   my $newVersionFile = $install->{'files'}->{'VERSION'}->{'source'};
268
   if ( open FILE,"<$currentVersionFile" ) {
269
      while ( my $line = <FILE> ) {
270
         chomp $line;
271
         my ( $filename, $version, $checksum ) = split( "\t", $line );
272
         $install{'files'}->{$filename}->{'installed version'} = $version ? $version : '';
273
      }
274
      close FILE;
275
   }
276
   if ( open FILE,"<$newVersionFile" ) {
277
      while ( my $line = <FILE> ) {
278
         chomp $line;
279
         my ( $filename, $version, $checksum ) = split( "\t", $line );
280
         $install->{'files'}->{$filename}->{'new version'} = $version ? $version : '';
281
      }
282
      close FILE;
283
   }
284
   foreach my $file ( keys %{$$install{'files'}} ) {
285
      $install{'files'}->{$file}->{'installed version'} = -2 unless defined $install->{'files'}->{$file}->{'installed version'};
286
      $install{'files'}->{$file}->{'new version'} = -1 unless defined $install->{'files'}->{$file}->{'new version'};
287
   }
288
   return 1;
289
} # getVersions
290
 
291
 
292
# this actually does the installation, except for the configuration
293
sub doInstall {
294
   my $install = shift;
295
   my $fileList = $install->{'files'};
296
 
297
   &checkDirectoryExists( $install->{'bindir'} . '/', $install->{'default permission'}, $install->{'default owner'} . ':' . $install->{'default group'} );
298
   foreach my $file ( keys %$fileList ) {
299
      next unless ( $fileList->{$file}->{'type'} && $fileList->{$file}->{'type'} eq 'file' );
300
 
301
      next if $install->{'action'} eq 'upgrade' && ! defined( $fileList->{$file}->{'installed version'} )
302
              ||
303
              ( &dotVersion2Number( $fileList->{$file}->{'new version'} ) <= &dotVersion2Number($fileList->{$file}->{'installed version'} ) );
304
      &checkDirectoryExists( $fileList->{$file}->{'target'}, $install->{'default permission'}, $install->{'default owner'} . ':' . $install->{'default group'} );
305
      &runCommand( 
306
            "cp $fileList->{$file}->{'source'} $fileList->{$file}->{'target'}",
307
            "chmod $fileList->{$file}->{'permission'} $fileList->{$file}->{'target'}",
308
            "chown $fileList->{$file}->{'owner'} $fileList->{$file}->{'target'}"
309
            );
310
      # if there is a post action, run it and store any return in @feedback
311
      push @feedback, `$fileList->{$file}->{'post action'}` if defined $fileList->{$file}->{'post action'};
312
      # if there is a message to be passed, store it in @messages
313
      push @messages, $fileList->{$file}->{'meesage'} if defined $fileList->{$file}->{'message'};
314
   } # foreach file
315
   # set up crontab, if necessary
316
   &runCommand( $install->{'crontab'} ) if defined ( $install->{'crontab'} );
317
   return 1;
318
}
319
 
320
sub postInstall {
321
   my $install = shift;
322
 
323
   # seed configuration, if needed
324
   if ( $$install{'build config'} ) {
325
      my $config;
326
      my @fileList;
327
 
328
      # the order is important here as, if multiple files exist, latter ones can
329
      # overwrite values in the previous. We do a push so the first value pushed
330
      # is processed last, ie has higher priority.
331
      push @fileList, $install->{'configuration'}->{'old configuration file'};
332
      push @fileList, $install->{'configuration'}->{'configuration file'};
333
 
334
      my $seedFile = $install->{'configuration'}->{'configuration seed file'};
335
      if ( -e $install->{'configuration seed file'} ) {
336
         push @fileList, $install->{'configuration seed file'};
337
      } # if preload seed file
338
 
339
      $config = &makeConfig( @fileList );
340
      # if ScriptDirs and moduleDirs not populated, do so from our configuration
341
      if ( ! $$config{'scriptDirs'} || ! scalar @{ $$config{'scriptDirs'} }  ) {
342
         $config->{'scriptDirs'} = [ $install->{'files'}->{'scripts'}->{'target'} ];
343
      }
344
      if ( ! $$config{'moduleDirs'} || ! @{ $$config{'moduleDirs'} }  ) {
345
         $config->{'moduleDirs'} = [ $install->{'files'}->{'modules'}->{'target'} ];
346
      }
347
      my $content = &showConf( $config );
348
      return &writeConfig( '' , $content );
349
   } # if we are building/merging configuration
350
}
351
 
352
# installs binaries and libraries
353
sub installOSStuff {
354
   my $install = shift;
355
   my @actions = values( %{ $install->{'missing libraries'} } );
356
   push @actions, values( %{ $install->{'missing binaries'} } );
357
   foreach my $action ( @actions ) {
358
      print "$action\n";
359
      &runCommand( $action );
360
   }
361
}
362
 
363
 
364
################################################################
365
#               Main Code                                      #
366
################################################################
367
 
368
# handle any command line parameters that may have been passed in
369
 
370
GetOptions (
371
            "verbose|v=i" => \$verbose, # verbosity level, 0-9
372
            "os|o=s"      => \$os,      # pass in the operating system
373
            "dryrun|n"    => \$dryRun,  # do NOT actually do anything
374
            'help|h'      => \$help,
375
            'version|V'   => \$version
376
            ) or die "Error parsing command line\n";
377
 
378
if ( $help ) { &help() ; exit; }
379
if ( $version ) { print "$0 version $VERSION\n"; exit; }
380
 
381
$install{'os'} = &setUpOperatingSystemSpecific( \%install, \%operatingSystems, $os ? $os : `$sourceDir/determineOS`, $sourceDir );
382
$install{'missing libraries'} = &validateLibraries( \%libraries, $install{'os'} );
383
$install{'missing binaries'} = &validateBinaries( \%binaries, $install{'os'} );
384
&getInstallActions( \%install );
385
populateSourceDir( \%install, $sourceDir );
386
 
387
if ( &GetPermission( \%install ) ) {
388
   &installOSStuff( \%install );
389
   &getVersions( \%install ) unless $install{'action'} eq 'new';
390
#   print Dump( \%install );
391
#   <>;
392
   &doInstall( \%install );
393
} else {
394
   die "Please fix whatever needs to be done and try again\n";
395
}
396
 
397
my $filename = &postInstall( \%install );
398
my $confFileName = $install{'configuration'}{'configuration file'};
399
print "Running configure.pl with $filename, output to $confFileName\n";
400
 
401
exec( "$install{bindir}/configure.pl -f $filename -o $confFileName" );
402
 
403
 
404
########### ADD LOGGING