Subversion Repositories camp_sysinfo_client_3

Rev

Details | Last modification | View Log | RSS feed

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