| 33 | rodolico | 1 | #! /usr/bin/env perl
 | 
        
           |  |  | 2 |   | 
        
           | 42 | rodolico | 3 |   | 
        
           | 33 | rodolico | 4 | use strict;
 | 
        
           |  |  | 5 | use warnings;
 | 
        
           |  |  | 6 |   | 
        
           | 42 | rodolico | 7 | # install.pl
 | 
        
           |  |  | 8 | #
 | 
        
           |  |  | 9 | # installer for perl script, in this case, sysinfo
 | 
        
           |  |  | 10 | #
 | 
        
           |  |  | 11 | # Revision history
 | 
        
           |  |  | 12 | #
 | 
        
           |  |  | 13 | # Version 1.1.7 20161010 RWR
 | 
        
           |  |  | 14 | # Added ability to validate required libraries are installed
 | 
        
           |  |  | 15 | #
 | 
        
           | 50 | rodolico | 16 | # version 1.2 20170327 RWR
 | 
        
           |  |  | 17 | # did some major modifications to correctly work on BSD systems also
 | 
        
           | 33 | rodolico | 18 |   | 
        
           | 50 | rodolico | 19 | our $VERSION = '1.2';
 | 
        
           | 42 | rodolico | 20 |   | 
        
           | 33 | rodolico | 21 | # find our location and use it for searching for libraries
 | 
        
           |  |  | 22 | BEGIN {
 | 
        
           |  |  | 23 |    use FindBin;
 | 
        
           |  |  | 24 |    use File::Spec;
 | 
        
           |  |  | 25 |    use lib File::Spec->catdir($FindBin::Bin);
 | 
        
           |  |  | 26 | }
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 | use sysinfoconf;
 | 
        
           |  |  | 29 | use File::Basename;
 | 
        
           | 35 | rodolico | 30 | use Getopt::Long;
 | 
        
           |  |  | 31 | Getopt::Long::Configure ("bundling"); # allow -vd --os='debian'
 | 
        
           | 33 | rodolico | 32 |   | 
        
           |  |  | 33 | use Data::Dumper;
 | 
        
           |  |  | 34 |   | 
        
           | 35 | rodolico | 35 | # $verbose can have the following values
 | 
        
           | 33 | rodolico | 36 | # 0 - do everything
 | 
        
           |  |  | 37 | # 1 - Do everything except the install, display what would be done
 | 
        
           |  |  | 38 | # 2 - Be verbose to STDERR
 | 
        
           |  |  | 39 | # 3 - Be very verbose
 | 
        
           | 35 | rodolico | 40 | my $verbose = 0; # if test mode, simply show what would be done
 | 
        
           |  |  | 41 | my $dryRun = 0;
 | 
        
           |  |  | 42 | my $os;
 | 
        
           |  |  | 43 | my $help = 0;
 | 
        
           |  |  | 44 | my $version = 0;
 | 
        
           | 33 | rodolico | 45 |   | 
        
           |  |  | 46 | my $status; # exit status of the processes
 | 
        
           |  |  | 47 | my $sourceDir = File::Spec->catdir($FindBin::Bin);
 | 
        
           | 34 | rodolico | 48 | my $installType;
 | 
        
           | 33 | rodolico | 49 |   | 
        
           |  |  | 50 | my %install = (  'bindir' => '/opt/camp/sysinfo-client',
 | 
        
           |  |  | 51 |                  'confdir' => '/etc/camp/sysinfo-client',
 | 
        
           | 34 | rodolico | 52 |                  'application name' => 'sysinfo client',
 | 
        
           | 50 | rodolico | 53 |                  'default group' => 'root',
 | 
        
           |  |  | 54 |                  'default owner' => 'root',
 | 
        
           |  |  | 55 |                  'default permission' => '0700',
 | 
        
           | 35 | rodolico | 56 |                  'configuration' => {
 | 
        
           |  |  | 57 |                           'configurator' => '<bindir>/configure.pl',
 | 
        
           |  |  | 58 |                           'configuration file' => '<confdir>/sysinfo-client.conf',
 | 
        
           |  |  | 59 |                           'configuration seed file' => 'sysinfo-client.seed',
 | 
        
           |  |  | 60 |                           'permission' => '700',
 | 
        
           | 50 | rodolico | 61 |                           'owner'      => '<default owner>',
 | 
        
           | 35 | rodolico | 62 |                           'target'     => '<confdir>'
 | 
        
           |  |  | 63 |                             },
 | 
        
           | 33 | rodolico | 64 |                  'files' => {
 | 
        
           |  |  | 65 |                            'configure.pl' => { 
 | 
        
           |  |  | 66 |                                  'type' => 'file',
 | 
        
           |  |  | 67 |                                  'permission' => '700', 
 | 
        
           | 50 | rodolico | 68 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 69 |                                  'target' =>  '<bindir>'
 | 
        
           |  |  | 70 |                               },
 | 
        
           |  |  | 71 |                            'sysinfo-client' => { 
 | 
        
           |  |  | 72 |                                  'type' => 'file',
 | 
        
           |  |  | 73 |                                  'permission' => '700',
 | 
        
           | 50 | rodolico | 74 |                                  'owner' => '<default owner>:<default group>',
 | 
        
           | 33 | rodolico | 75 |                                  'target' =>  '<bindir>'
 | 
        
           |  |  | 76 |                               },
 | 
        
           |  |  | 77 |                            'sysinfoconf.pm' => {
 | 
        
           |  |  | 78 |                                  'type' => 'file',
 | 
        
           |  |  | 79 |                                  'permission' => '600',
 | 
        
           | 50 | rodolico | 80 |                                  'owner' => '<default owner>:<default group>',
 | 
        
           | 33 | rodolico | 81 |                                  'target' =>  '<bindir>'
 | 
        
           |  |  | 82 |                               },
 | 
        
           |  |  | 83 |                            'notes' => { 
 | 
        
           |  |  | 84 |                                  'type' => 'file',
 | 
        
           |  |  | 85 |                                  'permission' => '600', 
 | 
        
           | 50 | rodolico | 86 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 87 |                                  'target' =>  '<bindir>'
 | 
        
           |  |  | 88 |                               },
 | 
        
           |  |  | 89 |                            'sysinfo-client.conf.template' => { 
 | 
        
           |  |  | 90 |                                  'type' => 'file',
 | 
        
           |  |  | 91 |                                  'permission' => '600', 
 | 
        
           | 50 | rodolico | 92 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 93 |                                  'target' =>  '<bindir>' 
 | 
        
           |  |  | 94 |                               },
 | 
        
           |  |  | 95 |                            'getSendEmail.pl' => { 
 | 
        
           |  |  | 96 |                                  'type' => 'file',
 | 
        
           |  |  | 97 |                                  'permission' => '700', 
 | 
        
           | 50 | rodolico | 98 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 99 |                                  'target' =>  '<bindir>' 
 | 
        
           |  |  | 100 |                               },
 | 
        
           |  |  | 101 |                            'install.pl' => {
 | 
        
           |  |  | 102 |                                  'type' => 'file',
 | 
        
           |  |  | 103 |                                  'permission' => '700', 
 | 
        
           | 50 | rodolico | 104 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 105 |                                  'target' =>  '<bindir>' 
 | 
        
           |  |  | 106 |                               },
 | 
        
           |  |  | 107 |                            'MANIFEST' => {
 | 
        
           |  |  | 108 |                                  'type' => 'file',
 | 
        
           |  |  | 109 |                                  'permission' => '600', 
 | 
        
           | 50 | rodolico | 110 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 111 |                                  'target' =>  '<bindir>' 
 | 
        
           |  |  | 112 |                               },
 | 
        
           |  |  | 113 |                            'sysinfo-client.seed.example' => { 
 | 
        
           |  |  | 114 |                                  'type' => 'file',
 | 
        
           |  |  | 115 |                                  'permission' => '600', 
 | 
        
           | 50 | rodolico | 116 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 117 |                                  'target' =>  '<bindir>' 
 | 
        
           |  |  | 118 |                               },
 | 
        
           |  |  | 119 |                            'VERSION' => { 
 | 
        
           |  |  | 120 |                                  'type' => 'file',
 | 
        
           |  |  | 121 |                                  'permission' => '600', 
 | 
        
           | 50 | rodolico | 122 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 123 |                                  'target' =>  '<bindir>' 
 | 
        
           |  |  | 124 |                               },
 | 
        
           |  |  | 125 |                               'modules' => {
 | 
        
           |  |  | 126 |                                  'type' => 'directory',
 | 
        
           |  |  | 127 |                                  'permission' => '700', 
 | 
        
           | 50 | rodolico | 128 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 129 |                                  'target' =>  '<bindir>',
 | 
        
           |  |  | 130 |                                  'action' => 'chmod 700 *'
 | 
        
           |  |  | 131 |                               },
 | 
        
           |  |  | 132 |                               'scripts' => {
 | 
        
           |  |  | 133 |                                  'type' => 'directory',
 | 
        
           |  |  | 134 |                                  'permission' => '700', 
 | 
        
           | 50 | rodolico | 135 |                                  'owner' => '<default owner>:<default group>', 
 | 
        
           | 33 | rodolico | 136 |                                  'target' =>  '<bindir>',
 | 
        
           |  |  | 137 |                                  'action' => 'chmod 700 *'
 | 
        
           |  |  | 138 |                               },
 | 
        
           |  |  | 139 |                      }
 | 
        
           |  |  | 140 |                   );
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 | # hash to set up os specific rules                  
 | 
        
           |  |  | 143 | my %operatingSystems = (
 | 
        
           |  |  | 144 |                   'debian' => {
 | 
        
           |  |  | 145 |                      'bindir' => '/opt/camp/sysinfo-client',
 | 
        
           |  |  | 146 |                      'confdir' => '/etc/camp/sysinfo-client',
 | 
        
           | 35 | rodolico | 147 |                      'crontab' => 'ln -s <bindir>/sysinfo-client /etc/cron.daily/sysinfo-client',
 | 
        
           | 37 | rodolico | 148 |                      'modules' => '((dpkg)|(unix)|(ipmi)|(xen))',
 | 
        
           | 33 | rodolico | 149 |                   },
 | 
        
           |  |  | 150 |                   'ipfire' => {
 | 
        
           |  |  | 151 |                      'bindir' => '/opt/camp/sysinfo-client',
 | 
        
           |  |  | 152 |                      'confdir' => '/etc/camp/sysinfo-client',
 | 
        
           | 50 | rodolico | 153 |                      'crontab' => 'ln -s <bindir>/sysinfo-client /etc/fcron.daily/sysinfo-client.fcron',
 | 
        
           | 37 | rodolico | 154 |                      'modules' => '((ipfire)|(unix))',
 | 
        
           | 43 | rodolico | 155 |                   },
 | 
        
           |  |  | 156 |                   'freebsd' => {
 | 
        
           |  |  | 157 |                      'bindir' => '/usr/local/opt/camp/sysinfo-client',
 | 
        
           |  |  | 158 |                      'confdir' => '/usr/local/etc/camp/sysinfo-client',
 | 
        
           | 50 | rodolico | 159 |                      'crontab' => 'ln -s <bindir>/sysinfo-client /etc/periodic/daily/sysinfo-client',
 | 
        
           | 43 | rodolico | 160 |                      'modules' => '((bsd)|(unix))',
 | 
        
           | 50 | rodolico | 161 |                     'default group' => 'wheel',
 | 
        
           |  |  | 162 |                     'default owner' => 'root',
 | 
        
           | 43 | rodolico | 163 |                   },
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 |                 );
 | 
        
           | 42 | rodolico | 166 |   | 
        
           |  |  | 167 | # list of libraries used by the system. We will offer to install them if
 | 
        
           |  |  | 168 | # we know how. NOTE: I have chosen to put the full installation command
 | 
        
           |  |  | 169 | # for each library. This allows us to use the package selector OR a different
 | 
        
           |  |  | 170 | # piece of code on a per-library basis, but results in something like
 | 
        
           |  |  | 171 | #      apt-get -y install perl-modules
 | 
        
           |  |  | 172 | #      apt-get -y install libwww-perl
 | 
        
           |  |  | 173 | # instead of
 | 
        
           |  |  | 174 | #      apt-get -y install libwww-perl perl-modules
 | 
        
           |  |  | 175 | # flexibility vs efficiency in this case.
 | 
        
           |  |  | 176 | my %libraries = ( 
 | 
        
           |  |  | 177 |                   'File::Basename' => { 'debian' => 'apt-get -y install perl-modules' },
 | 
        
           |  |  | 178 |                   'Exporter' => { 'debian' => 'apt-get -y install perl-base' },
 | 
        
           | 50 | rodolico | 179 |                   'LWP' => { 
 | 
        
           |  |  | 180 |                              'debian' => 'apt-get -y install libwww-perl',
 | 
        
           |  |  | 181 |                              'freebsd' => 'pkg install p5-libwww'
 | 
        
           |  |  | 182 |                            },
 | 
        
           | 42 | rodolico | 183 |                 );
 | 
        
           |  |  | 184 |   | 
        
           | 43 | rodolico | 185 | # utilities md5sum
 | 
        
           |  |  | 186 | # freebsd isomd5sum
 | 
        
           |  |  | 187 |   | 
        
           | 42 | rodolico | 188 | # validates the libraries needed exist
 | 
        
           |  |  | 189 | # simply eval's each library. If it doesn't exist, creates a list of
 | 
        
           |  |  | 190 | # commands to be executed to install it, and displays missing libraries
 | 
        
           |  |  | 191 | # offering to install them if possible.
 | 
        
           |  |  | 192 | sub validateLibraries {
 | 
        
           |  |  | 193 |    my ( $libraries, $os ) = @_;
 | 
        
           |  |  | 194 |    my @command;
 | 
        
           |  |  | 195 |    my @missingLibs;
 | 
        
           |  |  | 196 |    foreach my $lib ( keys %$libraries ) {
 | 
        
           |  |  | 197 |       eval( "use $lib;" );
 | 
        
           |  |  | 198 |       if ( $@ ) {
 | 
        
           |  |  | 199 |          push @command,$$libraries{$lib}{$os} if $$libraries{$lib}{$os};
 | 
        
           |  |  | 200 |          push @missingLibs, $lib;
 | 
        
           |  |  | 201 |       }
 | 
        
           |  |  | 202 |    }
 | 
        
           |  |  | 203 |    if ( @missingLibs ) { # we have missing libraries
 | 
        
           |  |  | 204 |       if ( @command ) {
 | 
        
           |  |  | 205 |          &runCommand( join( "\n", @command ) )
 | 
        
           |  |  | 206 |             if &yesno(
 | 
        
           |  |  | 207 |                         'Following libraries need to be installed: ' . 
 | 
        
           |  |  | 208 |                         join( ' ', @missingLibs ) . "\n" .
 | 
        
           |  |  | 209 |                         "I can install them with the following command(s)\n" . 
 | 
        
           |  |  | 210 |                         join( "\n", @command ) . "\nDo you want me to do this?\n"
 | 
        
           |  |  | 211 |                      );
 | 
        
           |  |  | 212 |       } else {
 | 
        
           |  |  | 213 |          die unless &yesno( 'Following libraries need to be installed: ' . 
 | 
        
           |  |  | 214 |                             join( ' ', @missingLibs ) . 
 | 
        
           |  |  | 215 |                             "\nand I don't know how to do this. Abort?" );
 | 
        
           |  |  | 216 |       }
 | 
        
           |  |  | 217 |    } # if
 | 
        
           |  |  | 218 | } # validateLibraries
 | 
        
           | 33 | rodolico | 219 |   | 
        
           |  |  | 220 |   | 
        
           |  |  | 221 | # attempt to locate the operating system.
 | 
        
           |  |  | 222 | # if found, will set some defaults for it.
 | 
        
           |  |  | 223 | sub getOS {
 | 
        
           | 35 | rodolico | 224 |    my ( $install, $operatingSystems, $os ) = @_;
 | 
        
           |  |  | 225 |    if ( ! $os ) { # we don't know, so we must try to figure it out
 | 
        
           |  |  | 226 |       my $osString = `uname -a`;
 | 
        
           |  |  | 227 |       foreach my $osType ( keys %$operatingSystems ) {
 | 
        
           |  |  | 228 |          print "Checking if OS is $osType in $osString\n" if $verbose > 2;
 | 
        
           |  |  | 229 |          next unless $osString =~ m/$osType/i;
 | 
        
           |  |  | 230 |          print "Yes, it is $osType\n" if $verbose > 2;
 | 
        
           |  |  | 231 |          # We found the OS, set up some defaults
 | 
        
           |  |  | 232 |          $os = $osType;
 | 
        
           |  |  | 233 |       } # foreach
 | 
        
           |  |  | 234 |    }
 | 
        
           |  |  | 235 |    if ( $os ) {
 | 
        
           |  |  | 236 |       $$install{'os'} = $os;
 | 
        
           |  |  | 237 |       print "Setting keys for operating system\n" if $verbose > 2;
 | 
        
           | 42 | rodolico | 238 |       foreach my $key ( keys %{$$operatingSystems{ $os }} ) {
 | 
        
           | 35 | rodolico | 239 |          $$install{$key} = $operatingSystems{ $os }{$key};
 | 
        
           | 33 | rodolico | 240 |       } # if it is a known OS
 | 
        
           | 35 | rodolico | 241 |    } # if
 | 
        
           |  |  | 242 |    return $os;
 | 
        
           | 33 | rodolico | 243 | } # getOperatingSystem
 | 
        
           |  |  | 244 |   | 
        
           | 37 | rodolico | 245 |   | 
        
           | 34 | rodolico | 246 | # get some input from the user and decide how to install/upgrade/remove/whatever
 | 
        
           |  |  | 247 | sub getInstallActions {
 | 
        
           |  |  | 248 |    my $install = shift;
 | 
        
           |  |  | 249 |    if ( ! &yesno( "This looks like a $$install{'os'} machine, correct?" ) ) {
 | 
        
           |  |  | 250 |       die "User Aborted\n" if &yesno( "If we continue, I will set this up like a $$install{'os'} system. Abort?" );
 | 
        
           |  |  | 251 |    }
 | 
        
           |  |  | 252 |    if ( -d $$install{'confdir'} ) {
 | 
        
           |  |  | 253 |       $$install{'action'} = &getAnswer( "It looks like $$install{'application name'} is already installed, what do you want to do?", 
 | 
        
           |  |  | 254 |                             ( "upgrade","remove", "overwrite" )
 | 
        
           |  |  | 255 |                           );
 | 
        
           |  |  | 256 |    } else {
 | 
        
           |  |  | 257 |       if ( &yesno( "This looks like a fresh install, correct?" ) ) {
 | 
        
           |  |  | 258 |          $$install{'action'} = 'install';
 | 
        
           |  |  | 259 |          $$install{'preseed config'} = &yesno( "Preseed the configuration file?" );
 | 
        
           |  |  | 260 |       } else {
 | 
        
           |  |  | 261 |          die "Can not continue at this time: Do not understand your system\n";
 | 
        
           |  |  | 262 |       }
 | 
        
           |  |  | 263 |    }
 | 
        
           |  |  | 264 |    $$install{'build config'} = &yesno( "Edit config file when done?" );
 | 
        
           |  |  | 265 |    $$install{'setup cron'} = &yesno( "Set up for automatic running via crontab?" );
 | 
        
           |  |  | 266 | }
 | 
        
           | 33 | rodolico | 267 |   | 
        
           | 34 | rodolico | 268 | sub showWork { 
 | 
        
           |  |  | 269 |    my $install = shift;
 | 
        
           |  |  | 270 |    print Dumper( \%install );
 | 
        
           |  |  | 271 | }
 | 
        
           | 35 | rodolico | 272 |   | 
        
           |  |  | 273 |   | 
        
           |  |  | 274 | sub doPlaceholderSubstitution {
 | 
        
           |  |  | 275 |    my ($hash, $placeholder) = @_;
 | 
        
           |  |  | 276 |    return if ref $hash ne 'HASH';
 | 
        
           |  |  | 277 |    foreach my $key ( keys %$hash ) {
 | 
        
           |  |  | 278 |       if ( ref( $$hash{$key} ) ) {
 | 
        
           |  |  | 279 |          &doPlaceholderSubstitution( $$hash{$key}, $placeholder );
 | 
        
           |  |  | 280 |       } else {
 | 
        
           |  |  | 281 |          foreach my $place ( keys %$placeholder ) {
 | 
        
           |  |  | 282 |             $$hash{$key} =~ s/$place/$$placeholder{$place}/;
 | 
        
           |  |  | 283 |          } # foreach
 | 
        
           |  |  | 284 |       } # if..else
 | 
        
           |  |  | 285 |    } # foreach
 | 
        
           |  |  | 286 |    return;
 | 
        
           |  |  | 287 | }
 | 
        
           | 34 | rodolico | 288 |   | 
        
           |  |  | 289 | # This will go through and first, see if anything is a directory, in
 | 
        
           |  |  | 290 | # which case, we'll create new entries for all files in there.
 | 
        
           |  |  | 291 | # then, it will do keyword substitution of <bindir> and <confdir>
 | 
        
           |  |  | 292 | # to populate the target.
 | 
        
           |  |  | 293 | # When this is done, each file should have a source and target that is
 | 
        
           |  |  | 294 | # a fully qualified path and filename
 | 
        
           | 33 | rodolico | 295 | sub populateSourceDir {
 | 
        
           |  |  | 296 |    my ( $install, $sourceDir ) = @_;
 | 
        
           | 35 | rodolico | 297 |    my %placeHolders = 
 | 
        
           |  |  | 298 |       ( 
 | 
        
           |  |  | 299 |         '<bindir>' => $$install{'bindir'},
 | 
        
           | 50 | rodolico | 300 |         '<confdir>' => $$install{'confdir'},
 | 
        
           |  |  | 301 |         '<default owner>' => $$install{'default owner'},
 | 
        
           |  |  | 302 |         '<default group>' => $$install{'default group'},
 | 
        
           |  |  | 303 |         '<default permission>' => $$install{'default permission'}
 | 
        
           | 35 | rodolico | 304 |       );
 | 
        
           | 33 | rodolico | 305 |   | 
        
           |  |  | 306 |    my $allFiles = $$install{'files'};
 | 
        
           |  |  | 307 |   | 
        
           | 35 | rodolico | 308 |    # find all directory entries and load files in that directory into $$install{'files'}
 | 
        
           | 33 | rodolico | 309 |    foreach my $dir ( keys %$allFiles ) {
 | 
        
           |  |  | 310 |       if ( defined( $$allFiles{$dir}{'type'} ) && $$allFiles{$dir}{'type'} eq 'directory' ) {
 | 
        
           | 35 | rodolico | 311 |          print "Found directory $dir\n" if $verbose > 2;
 | 
        
           | 33 | rodolico | 312 |          if ( opendir( my $dh, "$sourceDir/$dir" ) ) {
 | 
        
           |  |  | 313 |             my @files = map{ $dir . '/' . $_ } grep { ! /^\./ && -f "$sourceDir/$dir/$_" } readdir( $dh );
 | 
        
           | 35 | rodolico | 314 |             print "\tFound files " . join( ' ', @files ) . "\n" if $verbose > 2;
 | 
        
           | 33 | rodolico | 315 |             foreach my $file ( @files ) {
 | 
        
           |  |  | 316 |                $$allFiles{ $file }{'type'} = 'file';
 | 
        
           | 37 | rodolico | 317 |                if ( $dir eq 'modules' ) {
 | 
        
           |  |  | 318 |                   $$allFiles{ $file }{'permission'} = ( $file =~ m/$$install{'modules'}/ ) ? '0700' : '0600';
 | 
        
           |  |  | 319 |                } else {
 | 
        
           |  |  | 320 |                   $$allFiles{ $file }{'permission'} = $$allFiles{ $dir }{'permission'};
 | 
        
           |  |  | 321 |                }
 | 
        
           | 33 | rodolico | 322 |                $$allFiles{ $file }{'owner'} = $$allFiles{ $dir }{'owner'};
 | 
        
           |  |  | 323 |                $$allFiles{ $file }{'target'} = $$allFiles{ $dir }{'target'};
 | 
        
           |  |  | 324 |             } # foreach
 | 
        
           |  |  | 325 |             closedir $dh;
 | 
        
           |  |  | 326 |          } # if opendir
 | 
        
           |  |  | 327 |       } # if it is a directory
 | 
        
           |  |  | 328 |    } # foreach
 | 
        
           | 35 | rodolico | 329 |    # find all files, and set the source directory, and add the filename to
 | 
        
           |  |  | 330 |    # the target
 | 
        
           | 33 | rodolico | 331 |    foreach my $file ( keys %$allFiles ) {
 | 
        
           |  |  | 332 |       $$allFiles{$file}{'source'} = "$sourceDir/$file";
 | 
        
           |  |  | 333 |       $$allFiles{$file}{'target'} .= "/$file";
 | 
        
           |  |  | 334 |    } # foreach
 | 
        
           | 35 | rodolico | 335 |   | 
        
           |  |  | 336 |    # finally, do place holder substitution. This recursively replaces all keys
 | 
        
           |  |  | 337 |    # in  %placeHolders with the values.
 | 
        
           |  |  | 338 |    &doPlaceholderSubstitution( $install, \%placeHolders );
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 |    print Dumper( $install ) if $verbose > 2;
 | 
        
           |  |  | 341 |   | 
        
           | 33 | rodolico | 342 |    return 1;
 | 
        
           |  |  | 343 | } # populateSourceDir
 | 
        
           |  |  | 344 |   | 
        
           | 34 | rodolico | 345 | # there is a file named VERSIONS. We get the values out of the install
 | 
        
           |  |  | 346 | # directory and (if it exists) the target so we can decide what needs
 | 
        
           |  |  | 347 | # to be updated.
 | 
        
           | 33 | rodolico | 348 | sub getVersions {
 | 
        
           |  |  | 349 |    my $install = shift;
 | 
        
           |  |  | 350 |    my $currentVersionFile = $$install{'files'}{'VERSION'}{'target'};
 | 
        
           |  |  | 351 |    my $newVersionFile = $$install{'files'}{'VERSION'}{'source'};
 | 
        
           |  |  | 352 |    if ( open FILE,"<$currentVersionFile" ) {
 | 
        
           |  |  | 353 |       while ( my $line = <FILE> ) {
 | 
        
           |  |  | 354 |          chomp $line;
 | 
        
           |  |  | 355 |          my ( $filename, $version, $checksum ) = split( "\t", $line );
 | 
        
           |  |  | 356 |          $$install{'files'}{$filename}{'installed version'} = $version ? $version : '';
 | 
        
           |  |  | 357 |       }
 | 
        
           |  |  | 358 |       close FILE;
 | 
        
           |  |  | 359 |    }
 | 
        
           |  |  | 360 |    if ( open FILE,"<$newVersionFile" ) {
 | 
        
           |  |  | 361 |       while ( my $line = <FILE> ) {
 | 
        
           |  |  | 362 |          chomp $line;
 | 
        
           |  |  | 363 |          my ( $filename, $version, $checksum ) = split( "\t", $line );
 | 
        
           |  |  | 364 |          $$install{'files'}{$filename}{'new version'} = $version ? $version : '';
 | 
        
           |  |  | 365 |       }
 | 
        
           |  |  | 366 |       close FILE;
 | 
        
           |  |  | 367 |    }
 | 
        
           | 40 | rodolico | 368 |    foreach my $file ( keys %{$$install{'files'}} ) {
 | 
        
           |  |  | 369 |       $$install{'files'}{$file}{'installed version'} = -2 unless defined $$install{'files'}{$file}{'installed version'};
 | 
        
           |  |  | 370 |       $$install{'files'}{$file}{'new version'} = -1 unless defined $$install{'files'}{$file}{'new version'};
 | 
        
           |  |  | 371 |    }
 | 
        
           | 33 | rodolico | 372 |    return 1;
 | 
        
           |  |  | 373 | } # getVersions
 | 
        
           |  |  | 374 |   | 
        
           | 34 | rodolico | 375 | # this actually does the installation, except for the configuration
 | 
        
           | 33 | rodolico | 376 | sub doInstall {
 | 
        
           |  |  | 377 |    my $install = shift;
 | 
        
           |  |  | 378 |    my $fileList = $$install{'files'};
 | 
        
           |  |  | 379 |   | 
        
           | 50 | rodolico | 380 |    &checkDirectoryExists( $$install{'bindir'} . '/', $$install{'default permission'}, "$$install{'default owner'}:$$install{'default group'}" );
 | 
        
           | 33 | rodolico | 381 |    foreach my $file ( keys %$fileList ) {
 | 
        
           |  |  | 382 |       next unless ( $$fileList{$file}{'type'} && $$fileList{$file}{'type'} eq 'file' );
 | 
        
           | 40 | rodolico | 383 |       next if $$install{'action'} eq 'upgrade' && ! defined( $$fileList{$file}{'installed version'} )
 | 
        
           |  |  | 384 |               ||
 | 
        
           |  |  | 385 |               ( $$fileList{$file}{'new version'} eq $$fileList{$file}{'installed version'} );
 | 
        
           | 50 | rodolico | 386 |       &checkDirectoryExists( $$fileList{$file}{'target'}, $$install{'default permission'}, "$$install{'default owner'}:$$install{'default group'}" );
 | 
        
           | 33 | rodolico | 387 |       &runCommand( 
 | 
        
           |  |  | 388 |             "cp $$fileList{$file}{'source'} $$fileList{$file}{'target'}",
 | 
        
           |  |  | 389 |             "chmod $$fileList{$file}{'permission'} $$fileList{$file}{'target'}",
 | 
        
           |  |  | 390 |             "chown  $$fileList{$file}{'owner'} $$fileList{$file}{'target'}"
 | 
        
           |  |  | 391 |             );
 | 
        
           |  |  | 392 |    } # foreach file
 | 
        
           |  |  | 393 |    return 1;
 | 
        
           |  |  | 394 | }
 | 
        
           |  |  | 395 |   | 
        
           | 35 | rodolico | 396 | sub postInstall {
 | 
        
           |  |  | 397 |    my $install = shift;
 | 
        
           | 37 | rodolico | 398 |   | 
        
           | 35 | rodolico | 399 |    # set up crontab, if necessary
 | 
        
           | 37 | rodolico | 400 |    &runCommand( $$install{'crontab'} ) if defined ( $$install{'crontab'} );
 | 
        
           |  |  | 401 |   | 
        
           |  |  | 402 |    # seed configuration, if needed
 | 
        
           |  |  | 403 |    if ( $$install{'build config'} ) {
 | 
        
           | 43 | rodolico | 404 |       my $config;
 | 
        
           |  |  | 405 |       my @fileList;
 | 
        
           | 37 | rodolico | 406 |       my $seedFile = $$install{'configuration'}{'configuration seed file'};
 | 
        
           |  |  | 407 |       my $confFile = $$install{'configuration'}{'configuration file'};
 | 
        
           |  |  | 408 |   | 
        
           |  |  | 409 |       if ( -f $seedFile  && &yesno( 'Add installation seed file? ' ) ) {
 | 
        
           | 43 | rodolico | 410 |          push @fileList, $seedFile;
 | 
        
           | 37 | rodolico | 411 |       } # if preload seed file
 | 
        
           |  |  | 412 |   | 
        
           | 35 | rodolico | 413 |       if (  -f $confFile  ) {
 | 
        
           | 43 | rodolico | 414 |          push @fileList, $confFile;
 | 
        
           | 35 | rodolico | 415 |       }
 | 
        
           | 43 | rodolico | 416 |       $config = makeConfig( @fileList );
 | 
        
           |  |  | 417 |       my $content = &showConf( $config );
 | 
        
           | 35 | rodolico | 418 |       print $content;
 | 
        
           | 37 | rodolico | 419 |       print &writeConfig( $$install{'configuration'}{'configuration file'} , $content ) . "\n"
 | 
        
           |  |  | 420 |          if ( &yesno( "Write the above configuration to $$install{'configuration'}{'configuration file'}?" ) );
 | 
        
           |  |  | 421 |    } # if we are building/merging configuration
 | 
        
           | 35 | rodolico | 422 |   | 
        
           |  |  | 423 | }
 | 
        
           | 34 | rodolico | 424 |   | 
        
           | 35 | rodolico | 425 | sub help {
 | 
        
           |  |  | 426 |    my $oses = join( ' ', keys %operatingSystems );
 | 
        
           |  |  | 427 |    print <<END
 | 
        
           |  |  | 428 | $0 --verbose=x --os="osname" --dryrun --help --version
 | 
        
           | 34 | rodolico | 429 |   | 
        
           | 35 | rodolico | 430 | --os      - osname is one of [$oses]
 | 
        
           |  |  | 431 | --dryrun  - do not actually do anything, just tell you what I'd do
 | 
        
           |  |  | 432 | --verbose - x is 0 (normal) to 3 (horrible)
 | 
        
           |  |  | 433 | END
 | 
        
           |  |  | 434 | }
 | 
        
           | 34 | rodolico | 435 |   | 
        
           | 35 | rodolico | 436 |   | 
        
           |  |  | 437 |   | 
        
           |  |  | 438 | ##########################################
 | 
        
           |  |  | 439 | # Main Loop
 | 
        
           |  |  | 440 | ##########################################
 | 
        
           |  |  | 441 |   | 
        
           |  |  | 442 | # handle any command line parameters that may have been passed in
 | 
        
           |  |  | 443 |   | 
        
           |  |  | 444 | GetOptions (
 | 
        
           |  |  | 445 |             "verbose|v=i" => \$verbose, # verbosity level, 0-9
 | 
        
           |  |  | 446 |             "os|o=s"      => \$os,      # pass in the operating system
 | 
        
           |  |  | 447 |             "dryrun|n"    => \$dryRun,  # do NOT actually do anything
 | 
        
           |  |  | 448 |             'help|h'      => \$help,
 | 
        
           |  |  | 449 |             'version|V'   => \$version
 | 
        
           |  |  | 450 |             ) or die "Error parsing command line\n";
 | 
        
           |  |  | 451 |   | 
        
           |  |  | 452 | if ( $help ) { &help() ; exit; }
 | 
        
           |  |  | 453 | if ( $version ) { print "$0 version $VERSION\n"; exit; }
 | 
        
           |  |  | 454 | &setDryRun( $dryRun ); # tell the library whether this is a dry run or not
 | 
        
           |  |  | 455 |   | 
        
           | 33 | rodolico | 456 | # figure out if we know our operating system
 | 
        
           | 35 | rodolico | 457 | $os = &getOS( \%install, \%operatingSystems, $os );
 | 
        
           | 33 | rodolico | 458 |   | 
        
           | 42 | rodolico | 459 | &validateLibraries( \%libraries, $os );
 | 
        
           |  |  | 460 |   | 
        
           | 35 | rodolico | 461 | $installType = &getInstallActions( \%install );
 | 
        
           | 34 | rodolico | 462 |   | 
        
           | 33 | rodolico | 463 | # based on the defaults, flesh out the install hash
 | 
        
           |  |  | 464 | $status = &populateSourceDir( \%install, $sourceDir );
 | 
        
           |  |  | 465 |   | 
        
           | 35 | rodolico | 466 |   | 
        
           | 33 | rodolico | 467 | $status = &getVersions( \%install );
 | 
        
           |  |  | 468 |   | 
        
           | 35 | rodolico | 469 | &showWork( \%install );
 | 
        
           |  |  | 470 | die unless &yesno( "Ready to run? Select No to abort." );
 | 
        
           |  |  | 471 |   | 
        
           | 33 | rodolico | 472 | $status = &doInstall( \%install );
 | 
        
           |  |  | 473 |   | 
        
           | 35 | rodolico | 474 | $status = &postInstall( \%install );
 | 
        
           | 34 | rodolico | 475 |   | 
        
           | 37 | rodolico | 476 | if ( ( -x $install{'configuration'}{'configurator'} ) && $install{'build config'} ) {
 | 
        
           |  |  | 477 |    exec( $install{'configuration'}{'configurator'} );
 | 
        
           |  |  | 478 | } else {
 | 
        
           |  |  | 479 |    print "Done, you should check the files in $install{'bindir'} and $install{'confdir'} before running\n";
 | 
        
           |  |  | 480 |    print "If you need help configuring, the helper app at\n$install{'configuration'}{'configurator'}\ncan be used.\n";
 | 
        
           |  |  | 481 | }
 | 
        
           |  |  | 482 |   | 
        
           | 35 | rodolico | 483 | 1;   
 | 
        
           | 40 | rodolico | 484 |   | 
        
           |  |  | 485 |   | 
        
           |  |  | 486 | # add uninstall, clean to install.pl
 | 
        
           |  |  | 487 | # clean will look for any file in bindir which is NOT in the list of available files and remove them
 | 
        
           |  |  | 488 | # if files already exist in install, preserve their permissions
 |