Subversion Repositories camp_sysinfo_client_3

Rev

Rev 122 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 122 Rev 132
Line 1... Line 1...
1
#! /usr/bin/env perl
1
#! /usr/bin/env perl
2
 
2
 
3
our $VERSION = '1.0';
-
 
4
 
-
 
5
use strict;
3
use strict;
6
use warnings;
4
use warnings;
7
 
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 {
8
use FindBin;
30
   use FindBin;
9
use File::Spec;
31
   use File::Spec;
10
use lib File::Spec->catdir($FindBin::Bin);
32
   use lib File::Spec->catdir($FindBin::Bin);
-
 
33
   eval( 'use YAML::Tiny' );
-
 
34
}
11
 
35
 
12
my $sourceDir = File::Spec->catdir($FindBin::Bin);
36
my $sourceDir = File::Spec->catdir($FindBin::Bin);
13
chdir $sourceDir;
-
 
14
 
37
 
-
 
38
use sysinfoconf;
-
 
39
use Data::Dumper;
-
 
40
use File::Basename;
-
 
41
use Getopt::Long;
-
 
42
 
-
 
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;
15
my $verbose = 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
-
 
77
}
-
 
78
 
-
 
79
 
-
 
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
 
16
 
118
 
17
# prompt the user for a response, then allow them to enter it
119
# check for any required binaries
-
 
120
sub validateBinaries {
18
# the first response is considered the default and is printed
121
   my ( $binaries, $os ) = @_;
-
 
122
   my %return;
19
# in all caps if more than one exists
123
   foreach my $bin ( keys %$binaries ) {
20
# first answer is used if they simply press the Enter
124
      unless ( `which $bin` ) {
21
# key. The response is returned all lower case if more than one
125
         $return{$bin} = $binaries->{$bin}->{$os} ? $binaries->{$bin}->{$os} : 'UNK';
22
# exists.
126
      }
23
# it is assumed 
127
   }
24
sub getAnswer {
128
   return \%return;
25
   my ( $prompt, @answers ) = @_;
129
} # validateBinaries
-
 
130
   
26
   $answers[0] = '' unless defined( $answers[0] );
131
# get some input from the user and decide how to install/upgrade/remove/whatever
27
   my $default = $answers[0];
132
sub getInstallActions {
28
   my $onlyOneAnswer = scalar( @answers ) == 1;
-
 
29
   print $prompt . '[ ';
133
   my $install = shift;
30
   $answers[0] = uc $answers[0] unless $onlyOneAnswer;
134
   if ( -d $install->{'confdir'} ) {
31
   print join( ' | ', @answers ) . ' ]: ';
135
      $install->{'action'} = "upgrade";
32
   my $thisAnswer = <>;
136
   } else {
33
   chomp $thisAnswer;
137
      $install->{'action'} = 'install';
-
 
138
   }
34
   $thisAnswer = $default unless $thisAnswer;
139
   $install->{'build config'} = 'Y';
35
   return $thisAnswer;
140
   $install->{'setup cron'} = 'Y';
36
}
141
}
37
 
142
 
-
 
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
      );
-
 
177
 
-
 
178
   my $allFiles = $$install{'files'};
-
 
179
 
-
 
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
-
 
207
 
-
 
208
   # finally, do place holder substitution. This recursively replaces all keys
-
 
209
   # in  %placeHolders with the values.
-
 
210
   &doPlaceholderSubstitution( $install, \%placeHolders );
-
 
211
 
-
 
212
   print Dump( $install ) if $verbose > 2;
-
 
213
 
-
 
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";
-
 
229
   }
-
 
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?" );
-
 
235
}
38
 
236
 
-
 
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
39
sub yesno {
247
sub dotVersion2Number {
40
   my ( $prompt, $default ) = @_;
248
   my $dotVersion = shift;
-
 
249
 
41
   $default = 'yes' unless $default;
250
   my @t = split( '\.', $dotVersion );
42
   my $answer = &getAnswer( $prompt, $default eq 'yes' ? ('yes','no' ) : ('no', 'yes') );
251
   #print "\n$dotVersion\n" . join( "\n", @t ) . "\n";
-
 
252
   my $return = 0;
-
 
253
   while ( @t ) {
-
 
254
      $return /= 100;
43
   return lc( substr( $answer, 0, 1 ) ) eq 'y';
255
      $return += pop @t;
-
 
256
   }
-
 
257
   #print "$return\n";
-
 
258
   return $return;
44
}
259
}
45
 
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
46
 
290
 
47
# hash to set up os specific rules
-
 
48
my %operatingSystems = (
-
 
49
                  'debian' => {
-
 
50
                     'regex'  => '(debian|mx|devuan)',
-
 
51
                     'install' => 'apt-get -y install libyaml-tiny-perl',
-
 
52
                  },
-
 
53
                  'ipfire' => {
-
 
54
                     'regex'  => 'ipfire',
-
 
55
                     'install' => 'gunzip Tiny.pm.gz ; mkdir YAML ; cp Tiny.pm YAML',
-
 
56
                  },
-
 
57
                  'freebsd' => {
-
 
58
                     'regex' => 'freebsd',
-
 
59
                     'install' => 'echo y | pkg install p5-YAML-Tiny',
-
 
60
                  },
-
 
61
                  'opnsense' => {
-
 
62
                     'fileexists' => '/conf/config.xml',
-
 
63
                     'install' => 'gunzip Tiny.pm.gz ; mkdir YAML ; cp Tiny.pm YAML',
-
 
64
                  },
-
 
65
                  
-
 
66
                );
-
 
67
 
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
}
68
 
319
 
-
 
320
sub postInstall {
69
my $os = `./determineOS`;
321
   my $install = shift;
70
 
322
 
-
 
323
   # seed configuration, if needed
-
 
324
   if ( $$install{'build config'} ) {
-
 
325
      my $config;
-
 
326
      my @fileList;
-
 
327
 
71
die "Could not determine operating system, manual installation required\n" unless $os;
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
}
72
 
351
 
-
 
352
# installs binaries and libraries
73
eval( 'use YAML::Tiny' );
353
sub installOSStuff {
74
if ( $@ ) { # we need to install YAML::Tiny
354
   my $install = shift;
75
   if ( &yesno( "You need YAML::Tiny but it is not installed\nI can install it with the command\n$operatingSystems{$os}{install}\nOk" ) ) {
355
   my @actions = values( %{ $install->{'missing libraries'} } );
76
      print "Please wait while I do the installation\nIt will take a minute\n";
356
   push @actions, values( %{ $install->{'missing binaries'} } );
-
 
357
   foreach my $action ( @actions ) {
77
      `$operatingSystems{$os}{'install'}`;
358
      print "$action\n";
-
 
359
      &runCommand( $action );
78
   }
360
   }
79
}
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
 
80
 
403
 
81
#print "perl install-sysinfo-client.pl --os=$os\n";
404
########### ADD LOGGING
82
exec "perl install-sysinfo-client.pl --os=$os";
-