Project

General

Profile

Download (73.5 KB) Statistics
| Branch: | Revision:
1
#!/usr/bin/perl
2

    
3
# All rights reserved and Copyright (c) 2020 Origo Systems ApS.
4
# This file is provided with no warranty, and is subject to the terms and conditions defined in the license file LICENSE.md.
5
# The license file is part of this source code package and its content is also available at:
6
# https://www.origo.io/info/stabiledocs/licensing/stabile-open-source-license
7

    
8

    
9
use warnings;
10
use strict;
11

    
12
use Cwd;
13
use File::Temp qw(tempfile);
14
use File::Basename;
15
use File::Copy;
16
use File::Path;
17
use File::Spec;
18
use Getopt::Long qw(:config bundling no_ignore_case no_auto_abbrev);
19
use Debconf::Client::ConfModule qw(:all);
20
use Data::Dumper;
21
use Config::Simple;
22
use Digest::SHA qw(sha512_base64);
23

    
24
# Debconf does not like us printing to STDOUT so we print to STDERR if we need to say something
25
my $out = *STDERR;
26

    
27
#$ENV{'DEBIAN_FRONTEND'} = 'noninteractive';
28
#$ENV{'LC_ALL'} = 'C';
29

    
30
my $base_dir = Cwd::abs_path(dirname($0));
31
chdir($base_dir);
32

    
33
my $perlmodules_file = '/usr/share/stabile/cpan_modules';
34
my $tftp_root = '/mnt/stabile/tftp';
35

    
36
# Settings for connecting to the database.
37
my $g_db_name   = 'steamregister';
38
my $g_db_host   = 'localhost';
39
my $g_db_user   = 'root';
40
my $g_db_pass = '';
41
#my $g_db_pass = `grep -m1 password /etc/mysql/debian.cnf`;
42
#if ($g_db_pass  =~ /password.*=(.+)/) {
43
#	$g_db_pass = $1;
44
#} else {
45
#	$g_db_pass = '';
46
#}
47

    
48
# Global varible for our hostname
49
my $g_hostname = get('stabile/hostname');
50

    
51
# Initial user/password to put in DB
52
my $i_user = get('stabile/initial_user');
53
my $i_password = get('stabile/initial_password');
54
if ($i_password) {
55
    $i_password = sha512_base64($i_password);
56
    set('stabile/initial_password'); # clear password from debconf db
57
}
58
# Check if we should configure SSL via letsencrypt
59
my $letsencrypt = get("stabile/letsencrypt");
60
$letsencrypt = '' if ($letsencrypt eq 'false');
61

    
62
my ($internalnic, $externalnic) = getNics();
63
print $out "Using these network interfaces: $externalnic, $internalnic\n";
64

    
65
my $oldversion = 0;
66
my $oldrev = 0;
67
my $curversion = `cat /etc/stabile/version`;
68
my $currev = 0;
69
$currev = $1 if ($curversion =~ /\d+\.\d+-(\d+)/);
70
# Stop debconf processing
71
#stop();
72

    
73

    
74
# Container for our list of tasks to complete.
75
my @tasks;
76

    
77
#my @piston_versions = ('lucid', 'stabile-lucid');
78
my @piston_versions = ('bionic');
79

    
80
################
81
##### MAIN #####
82
################
83
MAIN:
84
$0 = "postinst";
85
# Die if we're not root.
86
if($< != 0) {
87
	die("You must be root for this to work\n");
88
}
89

    
90
# Modules from CPAN we need
91
# Read and strip newlines.
92
open(my $pm_fh, '<', $perlmodules_file) or die $!;
93
my @cpan_modules = <$pm_fh>;
94
close($pm_fh);
95

    
96
chomp(@cpan_modules);
97

    
98
my $summary = <<END
99
Summary of how this script can be called:
100
       * <postinst> `configure' <most-recently-configured-version>
101
       * <old-postinst> `abort-upgrade' <new version>
102
       * <conflictor's-postinst> `abort-remove' `in-favour' <package>
103
         <new-version>
104
       * <postinst> `abort-remove'
105
       * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
106
         <failed-install-package> <version> `removing'
107
         <conflicting-package> <version>
108
       * <postinst> `createdb'
109
       * <postinst> `setuptftp'
110
       * <postinst> `configure_apache'
111
       * <postinst> `create_users'
112
       * <postinst> `upgrade'
113
for details, see http://www.debian.org/doc/debian-policy/ or
114
the debian-policy package.
115
END
116
;
117
die "This script requires arguments.\n$summary"  unless (@ARGV);
118

    
119
#0
120
push(@tasks, {
121
		function    => \&install_cpan,
122
		description => "Install packages from CPAN",
123
		prompt      => "Installing CPAN packages",
124
		args        => [@cpan_modules]
125
	});
126
#1
127
push(@tasks, {
128
		function    => \&install_mod_auth_tkt,
129
		description => "Install mod_auth_tkt",
130
		prompt      => "Installing mod_auth_tkt" 
131
	});
132
#2
133
push(@tasks, {
134
		function    => \&setup_tftp,
135
		description => "Install Piston roots",
136
		prompt      => "Installing Piston roots"
137
	});
138
#3
139
push(@tasks, {
140
		function    => \&install_webmin_api,
141
		description => "Install Webmin API",
142
		prompt      => "Installing Webmin API",
143
		args        => ["/usr/share/stabile/Webmin-API-1.0.tar.gz"]
144
	});
145
#4
146
push(@tasks, {
147
		function    => \&setup_ntpd_mon_etc,
148
		description => "Set up NTP and mon",
149
		prompt      => "Setting up NTPD and mon"
150
	});
151
#5
152
push(@tasks, {
153
		function    => \&setup_nfs,
154
		description => "Set up NFS shares",
155
		prompt      => "Setting up NFS"
156
	});
157
#6
158
push(@tasks, {
159
		function    => \&setup_dhcp,
160
		description => "Set up DHCP",
161
		prompt      => "Setting up DHCP"
162
	});
163
#7
164
push(@tasks, {
165
		function    => \&configure_apache,
166
		description => "Configure apache",
167
		prompt      => "Configuring Apache"
168
	});
169
#8
170
push(@tasks, {
171
		function    => \&create_database,
172
		description => "Create MySQL database",
173
		prompt      => "Creating MySQL Database",
174
		args        => [$g_db_name, $g_db_host, $g_db_user, $g_db_pass]
175
	});
176
#9
177
push(@tasks, {
178
		function    => \&create_users,
179
		description => "Create users",
180
		prompt      => "Creating system users"
181
	});
182
#10
183
push(@tasks, {
184
		function    => \&upgrade,
185
		description => "Upgrade installed Stabile",
186
		prompt      => "Upgrading Stabile"
187
	});
188

    
189
if($ARGV[0] eq 'configure') {
190
    $oldversion = $ARGV[1] if ($ARGV[1]);
191
    $oldrev = 0;
192
    $oldrev = $1 if ($oldversion =~ /\d+\.\d+-(\d+)/);
193
    if ($oldrev && $currev > $oldrev) {
194
        print_headline("Commencing Stabile upgrade installation ($oldrev -> $currev)", 1);
195
        if (-e "/usr/share/stabile/custom_pre_upgrade") {
196
            print $out " *** Executing custom pre upgrade script ***\n";
197
            sysexec("/usr/share/stabile/custom_pre_upgrade");
198
        }
199
        install_all(($tasks[0], $tasks[7], $tasks[5], $tasks[8], $tasks[9], $tasks[10]));
200
        print_headline("Upgrade succeeded!");
201
        if (-e "/usr/share/stabile/custom_post_upgrade") {
202
			print $out " *** Executing custom post upgrade script ***\n";
203
            sysexec("/usr/share/stabile/custom_post_upgrade");
204
        }
205
    } elsif ($oldrev && $currev == $oldrev) {
206
        print_headline("Commencing Stabile reconfiguration ($oldrev -> $currev)", 1);
207
        install_all(($tasks[2], $tasks[4], $tasks[5], $tasks[6], $tasks[7], $tasks[8], $tasks[9], $tasks[10]));
208
        print_headline("Configuration succeeded!");
209
    } else {
210
        print_headline("Commencing full Stabile configuration", 1);
211
        install_all(@tasks);
212
        print_headline("Configuration succeeded!");
213
    }
214
    unless (-e '/tmp/stabile.upgrading') { # Stabile is being upgraded by pressurecontrol, don't restart it
215
		print $out " *** Restarting pressurecontrol ***\n";
216
        sysexec("systemctl restart pressurecontrol");
217
		print $out " *** Restarting movepiston ***\n";
218
		sysexec("systemctl restart movepiston");
219
    }
220

    
221
} elsif ($ARGV[0] eq 'createdb') {
222
	print_headline("Creating steamregister db", 1);
223
	execute_task($tasks[8]);
224

    
225
} elsif ($ARGV[0] eq 'configure_apache') {
226
	print_headline("Configuring Apache for Stabile", 1);
227
	execute_task($tasks[4]);
228

    
229
} elsif ($ARGV[0] eq 'create_users') {
230
	print_headline("Creating users in valve and piston", 1);
231
	execute_task($tasks[9]);
232

    
233
} elsif ($ARGV[0] eq 'upgrade') {
234
	print_headline("Performing upgrade tasks", 1);
235
	execute_task($tasks[0], $tasks[10]);
236
    print_headline("Upgrade succeeded!");
237
    unless (-e '/tmp/stabile.upgrading') { # Stabile is being upgraded by pressurecontrol, don't restart it
238
        print " *** Restarting pressurecontrol ***\n";
239
        sysexec("systemctl restart pressurecontrol");
240
		print " *** Restarting movepiston ***\n";
241
		sysexec("systemctl restart movepiston");
242
    }
243
} else {
244
	die "No understandable instructions passed $ARGV[0]";
245
}
246

    
247
my $baseurl = `cat /etc/stabile/baseurl`; chomp $baseurl;
248
print $out "You can now manage your Stabile engine at https://$g_hostname/stabile with the username and password you just configured.\n";
249
print $out "A shortcut has also been placed in your application menu, if you are on a desktop system\n";
250

    
251
exit 0;
252

    
253
#####################
254
##### FUNCTIONS #####
255
#####################
256

    
257
# On the valve, we have an internal and an external IP.
258
# The hostname points to our external IP.
259
# We create a DNS zone, and point our hostname
260
# to the internal IP.
261
# This is so nodes requesting our IP gets the internal.
262
# But it is currently not needed (or set up).
263
sub install_bind {
264

    
265
	# Create zone for our host.
266
	my $include = "zone \"$g_hostname\" {
267
	type master;
268
	file \"/var/lib/$g_hostname.hosts;
269
	};";
270

    
271
	open(my $FH, '>>', '/etc/bind/named.conf.local');
272
	print $FH $include;
273
	close($FH);
274

    
275
	#my $zone = "\$ttl 38400 $g_hostname.	IN	SOA	;
276
	return 0;
277
}
278

    
279
sub create_users {
280

    
281
	###########################
282
	# Set up ssh and Webmin   #
283
	###########################
284
    my $inetaddr = `ifconfig $externalnic 2>/dev/null | grep "inet "`;
285
    my $hostnet;
286
    my $hostmask;
287
    if ($inetaddr =~ /inet (\d+\.\d+\.\d+)\..+ netmask (\d+\.\d+\.\d+\.\d+)/) {
288
        $hostnet = $1;
289
        $hostmask = $2;
290
    }
291

    
292
    unless (!(-e "/etc/hosts.allow")) {
293
		print $out "Disallowing SSH and Webmin access\n";
294
        `echo "sshd: ALL" >> /etc/hosts.deny` unless (`grep sshd /etc/hosts.deny`);
295
		`echo "sshd: 10.0.0.0/24" >> /etc/hosts.allow` unless (`grep 'sshd.*10.0.0' /etc/hosts.allow`);
296
		`echo "sshd: 127.0.0.1" >> /etc/hosts.allow` unless (`grep 'sshd.*127.0.0' /etc/hosts.allow`);
297
		`echo "sshd: 192.168.0.0/16" >> /etc/hosts.allow` unless (`grep 'sshd.*192.168.0.0' /etc/hosts.allow`);
298
    }
299
	`echo "allow=127.0.0.1 192.168.0.0/16" >> /etc/webmin/miniserv.conf` unless (`grep 'allow=.*127.0.0' /etc/webmin/miniserv.conf`);
300

    
301
    if ($oldrev && $currev == $oldrev) { # reconfiguring
302
		print $out "Allowing SSH and Webmin access from $hostnet\n";
303
        `echo "sshd: $hostnet." >> /etc/hosts.allow` unless (!(-e "/etc/hosts.allow") || `grep "sshd: $hostnet" /etc/hosts.allow`);
304
        if (`grep "allow=" /etc/webmin/miniserv.conf`) {
305
            `perl -pi -e 's/allow=(.*)/allow=\$1 $hostnet.0\\/$hostmask 127.0.0.1/' /etc/webmin/miniserv.conf`;
306
        } else {
307
            `echo "allow=127.0.0.1" >> /etc/webmin/miniserv.conf`;
308
        }
309
    }
310
    `systemctl restart webmin`;
311

    
312

    
313
	#################
314
	# Add user irigo#
315
	#################
316

    
317
    my $irigo_uid = getpwnam('irigo');
318
    my $irigo_gid = getgrnam('irigo');
319

    
320
	# Ubuntu will not use IDs above 60k
321
	$irigo_uid = 60001 unless ($irigo_uid);
322
	$irigo_gid = 60001 unless ($irigo_gid);
323
	my $irigo_groups = 'sudo,adm,libvirt,kvm';
324

    
325
	my $ret;
326

    
327
	my $g_cmd = "groupadd -g $irigo_gid irigo";
328
	my $u_cmd = "useradd -m -u $irigo_uid -g $irigo_gid -s /bin/bash irigo";
329

    
330
	# create the user on the running system
331
	if (defined getgrgid($irigo_gid) && getgrgid($irigo_gid) eq 'irigo') {
332
		print $out " *** Group irigo already exists ***\n";
333
	} else {
334
		print $out " *** Creating irigo group ***\n";
335
		$ret = sysexec($g_cmd);
336
		# Give the option to return after errors.
337
		if($ret != 0 && !ask("Ignore error...")) {
338
			return $?;
339
		}
340
	}
341
	# create the group on the running system
342
	if (defined getpwuid($irigo_uid) && getpwuid($irigo_uid) eq 'irigo') {
343
		print $out " *** User irigo already exists ***\n";
344
	} else {
345
		print $out " *** Creating irigo user ***\n";
346
		$ret = sysexec($u_cmd);
347
		if($ret != 0 && !ask("Ignore error...")) {
348
			return $?;
349
		}
350
	}
351
	if (! `grep ^irigo: /etc/shadow`) {
352
		print $out q(
353
The next step is to create the password for the 'irigo' user. This
354
password is used both for this machine as well as the individual
355
pistons.
356

    
357
Not providing a password prevents you from logging in as 'irigo'
358
directly. Instead, you can log in to this machine as another user
359
switch to it using 'sudo -i -u irigo'. You may then connect to the
360
individual pistons using SSH without providing a password.
361

    
362
);
363

    
364
		#my $user_pass = ask_password("Enter desired password for the 'irigo' user");
365
		#print "\n";
366
		#set_password('irigo', $user_pass);
367
		set_password('irigo', '');
368
	}
369

    
370
	my $user_pass = (split(':', `sed -n '/^irigo:/p' /etc/shadow`))[1];
371

    
372
	if (-e "/home/irigo/.ssh/known_hosts") {
373
#		unlink("/home/irigo/.ssh/known_hosts"); # Why would we do this?
374
	}
375

    
376
	if (! -e "/home/irigo/.ssh/id_rsa") {
377
		print $out " *** Create SSH key pair ***\n";
378

    
379
		my $ssh_key_cmd = "sudo -u irigo ssh-keygen -t rsa -f /home/irigo/.ssh/id_rsa -P '' -q";
380
		$ret = sysexec($ssh_key_cmd);
381
		if($ret != 0 && !ask("Key no found. Ignore error...")) {
382
			return $?;
383
		}
384
	}
385

    
386
	$ret = chmod(0750, '/home/irigo/.ssh');
387
	if($ret != 1 && !ask("$!SSH dir not found. Ignore error...")) {
388
		return $?;
389
	}
390

    
391
	$ret = copy('/home/irigo/.ssh/id_rsa.pub',
392
				'/home/irigo/.ssh/authorized_keys');
393
	if($ret != 1 && !ask("$! Ignore error...")) {
394
		return $?;
395
	}
396

    
397
	$ret = chown($irigo_uid, $irigo_gid, '/home/irigo/.ssh/authorized_keys');
398
#	$ret = `chown irigo:irigo /home/irigo/.ssh/authorized_keys`;
399
	if($ret != 1 && !ask("$! Ignore error...")) {
400
		return $?;
401
	}
402

    
403
	$ret = chmod(0600, '/home/irigo/.ssh/authorized_keys');
404
	if($ret != 1 && !ask("$! Ignore error...")) {
405
		return $?;
406
	}
407

    
408
	$ret = `echo "Host 10.0.0.*\n    StrictHostKeyChecking no" > /home/irigo/.ssh/config`;
409
    $ret = `chown irigo:irigo /home/irigo/.ssh/config`;
410
    $ret = `chmod 600 /home/irigo/.ssh/config`;
411

    
412
	$ret = system('usermod', '--append', '--groups', $irigo_groups, 'irigo');
413
	if($ret && !ask("Ignore error...")) {
414
		return $?;
415
	}
416

    
417
	$ret = copy('/usr/share/stabile/sudoers', '/etc/sudoers.d/stabile');
418
	if($ret != 1 && !ask("$!\nIgnore error...")) {return $?;}
419
	$ret = chmod(0440, '/etc/sudoers.d/stabile');
420
	if($ret != 1 && !ask("$!\nIgnore error...")) {return $?;}
421

    
422
	$ret = system('usermod', '--append', '--groups', 'www-data', 'mon');
423
	if($ret && !ask("Ignore error...")) {
424
		return $?;
425
	}
426

    
427
	$ret = system("rsync", "--archive", "--delete", '/home/irigo/.ssh/', '/var/www/.ssh/');
428
	if($ret && !ask("Ignore error...")) {
429
		return $?;
430
	}
431

    
432
    my $www_data_uid = getpwnam('www-data') || die $!;
433
    my $www_data_gid = getgrnam('www-data') || die $!;
434
    my $mon_uid = getpwnam('mon') || die $!;
435
    my $mon_gid = getgrnam('mon') || die $!;
436

    
437
    $ret = chown($www_data_uid, $www_data_gid, '/var/www/.ssh');
438
    if($ret != 1 && !ask("$!Ignore error...")) {
439
        return $?;
440
    }
441

    
442
    $ret = copy('/var/www/.ssh/id_rsa', '/var/www/.ssh/id_rsa_www');
443
    if($ret != 1 && !ask("$! Ignore error...")) {
444
        return $?;
445
    }
446

    
447
    $ret = chown($www_data_uid, $www_data_gid, '/var/www/.ssh/id_rsa_www');
448
    if($ret != 1 && !ask("$! Ignore error...")) {
449
        return $?;
450
    }
451

    
452
    $ret = copy('/var/www/.ssh/id_rsa', '/var/www/.ssh/id_rsa_mon');
453
    if($ret != 1 && !ask("$! Ignore error...")) {
454
        return $?;
455
    }
456

    
457
    $ret = chown($mon_uid, $mon_gid, '/var/www/.ssh/id_rsa_mon');
458
    if($ret != 1 && !ask("$!\nIgnore error...")) {
459
        return $?;
460
    }
461

    
462
    $ret = chmod(0700, '/var/www/.ssh/id_rsa_mon', '/var/www/.ssh/id_rsa_www');
463
    if($ret != 2 && !ask("$!\nIgnore error...")) {
464
        return $?;
465
    }
466

    
467
	foreach my $piston_version (@piston_versions) {
468
		my $piston_root = "$tftp_root/$piston_version/casper/filesystem.dir";
469
		next unless (-e $piston_root && -e "$piston_root/bin/bash");
470

    
471
		$ret = cp_conf('/usr/share/stabile/valve-cgrules.conf', "$piston_root/etc/cgrules.conf");
472
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
473
		$ret = cp_conf('/usr/share/stabile/valve-cgconfig.conf', "$piston_root/etc/stabile/cgconfig.conf");
474
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
475
		$ret = cp_conf('/usr/share/stabile/valve-cgrulesgend.service', "$piston_root/etc/systemd/system/cgrulesgend.service");
476
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
477
		$ret = sysexec("chroot $piston_root systemctl enable cgrulesgend");
478
		if(!$ret && !ask("Error: $!. Ignoring exec error...")) {return 1;}
479

    
480
		# create the group in the chroot
481
		if (!sysexec("chroot $piston_root groupmod irigo 2> /dev/null")) {
482
			print $out " *** Group irigo already exists in the $piston_version chroot ***\n";
483
		} else {
484
			my $cmd = "chroot $piston_root $g_cmd";
485
			my $ret = sysexec($cmd);
486
			if($ret != 0 && !ask("Ignore error...")) {
487
				return $ret;
488
			}
489
		}
490

    
491
		# create the user in the chroot
492
		if (!sysexec("chroot $piston_root id irigo > /dev/null 2> /dev/null")) {
493
			print $out " *** User irigo already exists in the $piston_version chroot ***\n";
494
		} else {
495
			my $cmd = "chroot $piston_root $u_cmd";
496
			print $out " *** Creating irigo in the $piston_version chroot: $cmd ***\n";
497
			my $ret = sysexec($cmd);
498
			if($ret != 0 && !ask("Ignore error...")) {
499
				return $ret;
500
			}
501
		}
502

    
503
		$ret = system('chroot', $piston_root,
504
					  'usermod', '--append', '--groups', $irigo_groups,
505
					  'irigo');
506
		if($ret && !ask("Ignore error...")) {
507
			return $?;
508
		}
509

    
510
		# then copy the password from the valve to the piston
511
		# TODO: use chpasswd -e when we drop support for lucid
512
		my $shadow = `grep ^irigo: /etc/shadow`;
513
		my $ret = system('sed', '-i', '/^irigo:/d', $piston_root . '/etc/shadow');
514
		if($ret && !ask("$!\nIgnore error...")) {
515
			return $?;
516
		}
517
		print $out "Setting password for irigo in $piston_root\n";
518
		open(my $fh, '>>', $piston_root . '/etc/shadow') or return 0;
519
		print $fh "$shadow";
520
		close($fh) or return 0;
521

    
522
		$ret = system("rsync", "--archive", "--delete",
523
					  '/home/irigo/.ssh/',
524
					  $piston_root . '/home/irigo/.ssh/');
525
		if($ret && !ask("Ignore error...")) {
526
			return $?;
527
		}
528

    
529
		$ret = copy('/usr/share/stabile/sudoers', $piston_root . '/etc/sudoers.d/stabile');
530
		if($ret != 1 && !ask("$!\nIgnore error...")) {return $?;}
531
		$ret = chmod(0440, $piston_root . '/etc/sudoers.d/stabile');
532
		if($ret != 1 && !ask("$!\nIgnore error...")) {return $?;}
533

    
534
	}
535

    
536
    # Copy cgroup rules configuration
537
	`touch /etc/cgconfig.conf`; # In case it does not exist - needed by cgrulesgend.service. pressurecontrol uses file in /etc/stabile
538
	unless (-e '/etc/stabile/cgconfig.conf') {
539
		$ret = cp_conf('/usr/share/stabile/valve-cgconfig.conf', '/etc/stabile/cgconfig.conf');
540
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
541
	}
542
	if (-e '/etc/cgrules.conf') {
543
        print $out " *** cgroups are already configured on this engine\n";
544
        unless (`grep stabile /etc/cgrules.conf`) {
545
            print $out "copying rules into /etc/cgrules.conf\n";
546
            `cat /usr/share/stabile/valve-cgrules.conf >> /etc/cgrules.conf`;
547
        }
548
	} else {
549
		print $out "Setting up cgroups on engine\n";
550
		$ret = cp_conf('/usr/share/stabile/valve-cgrules.conf', '/etc/cgrules.conf');
551
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
552
		$ret = cp_conf('/usr/share/stabile/valve-cgrulesgend.service', '/etc/systemd/system/cgrulesgend.service');
553
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
554
		$ret = sysexec("systemctl daemon-reload");
555
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
556
		$ret = sysexec("systemctl enable cgrulesgend");
557
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
558
    }
559
    # If we are in a virtual environment main disk seems to have primary number 252
560
	my $majmin = '8:0';
561
	my $rootdev = '/dev/sda';
562
	$rootdev = $1 if (`df /` =~ /(\/dev\/\w+)/);
563
	# It seems that cgroups cannot handle individual partitions for blkio
564
	if ($rootdev =~ /(\/dev\/\w+)p\d+/) { # nvme naming - e.g. nvme0n1p1
565
		$rootdev = $1;
566
	} elsif ($rootdev =~ /(\/dev\/\w+)\d+/) {
567
		$rootdev = $1; # sata naming e.g. sda1
568
	}
569
	$majmin = $1 if (`lsblk -l $rootdev` =~ /\S+ +(\d+:\d+)/);
570
	if ($majmin) {
571
		print $out "Identified maj:min as $majmin on device $rootdev\n";
572
		`perl -pi -e 's/8:0 /$majmin /' /etc/stabile/cgconfig.conf`;
573
 	} else {
574
		print $out "Unable to analyse root device $rootdev for Cgroups disk throttling - please edit /etc/stabile/cgconfig.conf manually\n"
575
	}
576

    
577
    my $allowusers = <<END
578
AllowUsers irigo\@10.0.*
579
AllowUsers irigo\@localhost
580
AllowUsers irigo\@127.0.0.1
581
AllowUsers stabile
582
END
583
;
584
    unless (`grep "AllowUsers irigo\@localhost" /etc/ssh/sshd_config`) {
585
		print $out "Adding irigo to sshd_config\n";
586
        $ret = `echo "$allowusers" >> /etc/ssh/sshd_config`;
587
        `systemctl restart ssh`;
588
    }
589

    
590
    # Disallow user access to log file
591
    `touch /var/log/stabile/steam.log`;
592
    `chown www-data:www-data /var/log/stabile/steam.log`;
593

    
594
	# Don't show these users in login screen on workstations with GUI
595
	`echo "[User]\nSystemAccount=true" > /var/lib/AccountsService/users/libvirt-qemu`;
596
	`echo "[User]\nSystemAccount=true" > /var/lib/AccountsService/users/irigo`;
597

    
598
	# Add icon to application menu
599
	if (-e "/usr/share/applications") {
600
		$ret = cp_conf('/usr/share/stabile/stabile.desktop', '/usr/share/applications/stabile.desktop');
601
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
602
		else {
603
			`perl -pi -e 's/localhost/$g_hostname/' /usr/share/applications/stabile.desktop`;
604
			`chmod 644 /usr/share/applications/stabile.desktop`;
605
		}
606
	}
607

    
608
	print $out "Updating version to $curversion\n";
609
    `perl -pi -e 's/VERSION=.*/VERSION=$curversion/' /etc/stabile/config.cfg`;
610
	unless (`grep 'mynetworks = .*10.0.0.0' /etc/postfix/main.cf`) {
611
	    `perl -pi -e 's/mynetworks = .+/mynetworks = 10.0.0.0\\/24 127.0.0.0\\/8 [::ffff:127.0.0.0]\\/104 [::1]\\/128/' /etc/postfix/main.cf`;
612
	}
613
	return 0;
614
}
615

    
616
sub install_mod_auth_tkt {
617
	if (! -f '/etc/apache2/conf-available/auth_tkt_cgi.conf') {
618
		print $out "Copying /usr/share/stabile/Apache2/auth_tkt_cgi.conf -> /etc/apache2/conf-available/auth_tkt_cgi.conf\n";
619
		my $ret = copy('/usr/share/stabile/Apache2/auth_tkt_cgi.conf',
620
					   '/etc/apache2/conf-available/auth_tkt_cgi.conf');
621

    
622
		if($ret == 0 && !ask("Error copying auth_tkt_cgi.conf into apache. " .
623
							 "Continue?")) {
624
			return 1;
625
		}
626
        # Replace the password for authtkt
627
        my $password = `pwgen -s 32 1`;
628
        chomp($password);
629
        system("perl -pi -e 's/<%PASSWORD%>/$password/' /etc/apache2/conf-available/auth_tkt_cgi.conf");
630
		system("a2enconf auth_tkt_cgi");
631
	} else {
632
		print $out "/etc/apache2/conf-available/auth_tkt_cgi.conf already exists.\n";
633
	}
634
	return 0;
635
}
636

    
637
sub setup_nfs {
638
	my @exportlines = (
639
		"/mnt/stabile/tftp",
640
		"/mnt/stabile/images",
641
	);
642
	my @exportlines2 = (
643
		"\t10.0.0.0/24(async,ro,no_subtree_check,no_root_squash)",
644
		"\t10.0.0.0/24(sync,rw,no_subtree_check,no_root_squash)",
645
	);
646

    
647
	my $mounts = `cat /proc/mounts`;
648
	my $i = 0;
649
	my $newexports;
650
	foreach my $line (@exportlines) {
651
	    my $line2 = $exportlines2[$i];
652
	    $i++;
653
#	    next if ($mounts =~ /$line/); # Don't export directories we are importing from elsewhere
654
		if (sysexec("grep '^$line' /etc/exports")) { # Line not found - put it in
655
			open(my $fh, '>>', '/etc/exports') or die $!;
656
    		print $fh "$line$line2\n";
657
			close $fh or die $!;
658
			$newexports++;
659
		};
660
	}
661
    # Reload nfs server if new exports are found
662
	sysexec('exportfs -ra') if ($newexports);
663

    
664
	return 0;
665
}
666

    
667
sub setup_dhcp {
668
	my $cfg_path = '/usr/share/stabile/dhcpd.conf';
669
	my $cfg_dest = '/etc/dhcp/stabile_net.conf';
670
	`mkdir /run/dhcp-server` unless (-e "/run/dhcp-server"); # For some reason mimssing
671

    
672
	my $cfg_line = "include \"$cfg_dest\";";
673

    
674
	my $ret = cp_conf($cfg_path, $cfg_dest);
675
	if(!$ret && !ask("Error: $!. Ignore error...")) {
676
		return 1;
677
	}
678

    
679
	$ret = chmod 0644, $cfg_dest;
680
	if(!$ret && !ask("Error: $!. Ignore error...")) {
681
		return 1;
682
	}
683

    
684
	print $out "Bringing up $internalnic\n";
685
	
686
	my $enp2s0 = <<END
687
iface $internalnic inet static
688
	address 10.0.0.1
689
	netmask 255.255.255.0
690
	broadcast 10.0.0.255
691
	network 10.0.0.0
692
END
693
;
694

    
695
	my $enp2s0_netplan = <<END
696
network:
697
    ethernets:
698
        $internalnic:
699
            addresses: ['10.0.0.1/24']
700
END
701
;
702
	my $enp2s0_systemd = <<END
703
[Match]
704
Name=$internalnic
705

    
706
[Link]
707
RequiredForOnline=no
708

    
709
[Network]
710
ConfigureWithoutCarrier=true
711
Address=10.0.0.1/24
712
END
713
;
714
	`rm /etc/systemd/network/10-*.network`; # first remove any previous configuration
715
	if ($internalnic ne $externalnic) {
716
		if (-d "/etc/netplan") {
717
	#		$ret = `echo "$enp2s0_netplan" >> "/etc/netplan/$internalnic.yaml"` unless (-e "/etc/netplan/$internalnic.yaml");
718
	#		$ret .= `netplan apply`;
719
	# We now use systemd to support cases without active link
720
			$ret = `echo "$enp2s0_systemd" >> "/etc/systemd/network/10-$internalnic.network"` unless (-e "/etc/systemd/network/10-$internalnic.network");
721
			$ret .= `systemctl enable systemd-networkd.service`;
722
			$ret .= `systemctl restart systemd-networkd.service`;
723
		} else {
724
			$ret = `echo "$enp2s0" >> /etc/network/interfaces` unless (`grep enp2s0 /etc/network/interfaces`);
725
			$ret .= `ifconfig enp2s0 up`;
726
		}
727
	} else { # Let pressurecontrol bring up virtual nic with 10.0.0.1 if only one nic
728
		$ret = copy("/usr/share/stabile/stabile.dhclient-hook", "/etc/dhcp/dhclient-enter-hooks.d/stabile");
729
		`chmod 755 /etc/dhcp/dhclient-enter-hooks.d/stabile`;
730
		if(!$ret && !ask("Error: $!. Ignore error...")) {
731
			return 1;
732
		}
733
	}
734
	if(!$ret && !ask("Error: $!. Ignore error...")) {
735
		return 1;
736
	}
737

    
738
    unless (`grep "10.0.0.0" /etc/dhcp/dhcpd.conf`) { # Don't add include line if already configured
739
        if (sysexec("grep '$cfg_line' /etc/dhcp/dhcpd.conf")) {
740
            open(my $dhcp_cfg, '>>', '/etc/dhcp/dhcpd.conf') or die $!;
741
            print $dhcp_cfg "\n#Include stabile options:\n$cfg_line\n"
742
                or die $!;
743
            close $dhcp_cfg or die $!;
744
        }
745
    }
746
	print $out "Setting $internalnic as DHCP interface\n";
747
	`perl -pi -e 's/interface .+;/interface $internalnic;/' $cfg_dest`;
748

    
749
	if ($internalnic ne $externalnic) {
750
		$ret = sysexec('systemctl restart isc-dhcp-server');
751
		if($ret != 0 && !ask("Ignore error...")) {
752
			return $?;
753
		}
754
	} else { # Disable dhcp server if only one nic
755
		$ret = sysexec('systemctl stop isc-dhcp-server');
756
		$ret .= sysexec('systemctl disable isc-dhcp-server');
757
		if($ret != 0 && !ask("Ignore error...")) {
758
			return $?;
759
		}
760
	}
761
	return 0;
762
}
763

    
764
sub setup_ntpd_mon_etc {
765
#	my $cfg_path = '/usr/share/stabile/ntp-valve.conf';
766
#	my $cfg_dest = '/etc/ntp.conf';
767
#	my $ret = cp_conf($cfg_path, $cfg_dest);
768
#	if(!$ret && !ask("Error: $!. Ignore error...")) {
769
#		return 1;
770
#	}
771
#	$ret = sysexec('systemctl restart ntp');
772
#	if($ret != 0 && !ask("Ignore error...")) {
773
#		return $?;
774
#	}
775

    
776
	unless (-e "/etc/stabile/config.cfg") {
777
		my $ret = cp_conf('/usr/share/stabile/config.template.cfg', '/etc/stabile/config.cfg');
778
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
779
	}
780

    
781
	# unless (`grep ENGINE_DATA_NIC=. /etc/stabile/config.cfg`) {
782
	# 	print $out "Setting ENGINE_DATA_NIC to $externalnic\n";
783
    #     `perl -pi -e 's/ENGINE_DATA_NIC=.*/ENGINE_DATA_NIC=$externalnic/' /etc/stabile/config.cfg`;
784
    # }
785
	# unless (`grep EXTERNAL_NIC=. /etc/stabile/config.cfg`) {
786
	# 	print $out "Setting EXTERNAL_NIC to $externalnic\n";
787
    #     `perl -pi -e 's/EXTERNAL_NIC=.*/EXTERNAL_NIC=$externalnic/' /etc/stabile/config.cfg`;
788
    # }
789

    
790
	my $ret = cp_conf('/usr/share/stabile/nodeconfig.template.cfg', '/etc/stabile/nodeconfig.cfg');
791
	if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
792

    
793
	# unless (`grep DATA_NIC=. /etc/stabile/nodeconfig.cfg`) {
794
	# 	print $out "Setting local node DATA_NIC to $externalnic\n";
795
    #     `perl -pi -e 's/DATA_NIC=.*/DATA_NIC=$externalnic/' /etc/stabile/nodeconfig.cfg`;
796
    # }
797
	# unless (`grep ADMIN_NIC=. /etc/stabile/nodeconfig.cfg`) {
798
	# 	print $out "Setting local node ADMIN_NIC to $internalnic\n";
799
    #     `perl -pi -e 's/ADMIN_NIC=.*/ADMIN_NIC=$internalnic/' /etc/stabile/nodeconfig.cfg`;
800
    # }
801

    
802
	`chmod 644 /etc/stabile/nodeconfig.cfg`;
803
	`chmod 644 /etc/stabile/config.cfg`; # Tomcat needs to be able to read this
804

    
805
	if (system ("systemctl is-active --quiet docker")) {
806
		print $out "Disabling Docker - Docker networking (10.0.0.1) is not compatible with Stabile\n";
807
		`systemctl stop docker`;
808
		`systemctl disable docker`;
809
		`ip link delete docker0`;
810
	}
811
	`systemctl enable pressurecontrol`;
812
	`systemctl enable movepiston`;
813
	`systemctl enable stabile`;
814

    
815
	# Disable apparmor for libvirt
816
	`ln -s /etc/apparmor.d/usr.sbin.libvirtd /etc/apparmor.d/disable/usr.sbin.libvirtd` if (-e "/etc/apparmor.d/usr.sbin.libvirtd" && !(-e "/etc/apparmor.d/disable/usr.sbin.libvirtd"));
817

    
818
# Custom mon still needed...?
819
	$ret = sysexec('cp /usr/share/stabile/mon /usr/sbin');
820
	if($ret != 0 && !ask("Ignore error...")) {
821
		return $?;
822
	}
823
    # Allow mon to actually save disabled states
824
    `chown mon:mon /usr/lib/mon/state.d`;
825

    
826
	$ret = sysexec('cp /usr/share/stabile/auth.cf /etc/mon');
827
	if($ret != 0 && !ask("Ignore error...")) {
828
		return $?;
829
	}
830
	# Patch mon https monitor to not barf about SSL certificates
831
	`perl -pi -e 's/(my .+ = new LWP::UserAgent;)/\$1\n\\\$ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);/;' /usr/lib/mon/mon.d/http_tppnp.monitor` unless (`grep ssl_opts /usr/lib/mon/mon.d/http_tppnp.monitor`);
832

    
833
	print $out "Patching rdiff-backup to support sparse files\n";
834
	if (`grep sparse /usr/share/pyshared/rdiff_backup/rpath.py`) {
835
		print $out "Patch already applied to valve\n";
836
	} else {
837
		print $out `cd /usr/share/pyshared/rdiff_backup; patch < /usr/share/stabile/0-sparse.patch`;
838
	}
839
	if (-e "/mnt/stabile/tftp/bionic/casper/filesystem.dir/usr/share/pyshared/rdiff_backup/rpath.py") {
840
		if (`grep sparse /mnt/stabile/tftp/bionic/casper/filesystem.dir/usr/share/pyshared/rdiff_backup/rpath.py`) {
841
			print $out "Patch already applied to piston\n";
842
		} else {
843
			print $out `cd /mnt/stabile/tftp/bionic/casper/filesystem.dir/usr/share/pyshared/rdiff_backup; patch < /usr/share/stabile/0-sparse.patch`;
844
		}
845
	}
846
	`systemctl restart mon`;
847

    
848
	print $out "Updating fuse.conf to work with guestmount\n";
849
	print $out `perl -pi -e "s/#user_allow_other/user_allow_other/g;" /etc/fuse.conf`;
850
	print $out `chmod 644 /etc/fuse.conf`;
851
#	print $out `adduser www-data fuse`;
852
#	print $out `adduser irigo fuse`;
853
    # Guestmount needs to be able to read the running kernel
854
	print $out `chmod 644 /boot/vmlinuz*`;
855

    
856
    # We need quotas for VM mountable NFS
857
	print $out "Adding quota support to fstab\n";
858
    `perl -pi -e 's/(errors=remount-ro)/errors=remount-ro,noatime,usrjquota=aquota.user,jqfmt=vfsv0/' /etc/fstab` unless (`grep aquota /etc/fstab`);
859

    
860
	# Generate random secret for Graphite
861
	my $secretkey = sprintf "%12X", rand(0xffffffffffff);
862
	print $out `perl -pi -e "s/#SECRET_KEY = .*/#SECRET_KEY = '$secretkey'/;" /etc/graphite/local_settings.py`;
863

    
864
	# Link suidperl in case we have forgotten to weed it out somewhere
865
	`ln -s /usr/bin/perl /usr/bin/suidperl` unless (-e "/usr/bin/suidperl");
866

    
867
	# We reference qemu-img as kvm-img many places, so create a symlink for backwards compatibility
868
	`ln -s /usr/bin/qemu-img /usr/bin/kvm-img` unless (-e "/usr/bin/kvm-img");
869

    
870
	# Make Guacamole available to tomcat
871
	`ln -s /usr/share/stabile/guacamole-0.9.14.war /var/lib/tomcat8/webapps/guacamole.war` unless (-e "/var/lib/tomcat8/webapps/guacamole.war");
872
	`mv /etc/tomcat8/Catalina/localhost/guacamole.xml /etc/tomcat8/Catalina/localhost/guacamole.xml.bak` unless (-e "/etc/tomcat8/Catalina/localhost/guacamole.xml.bak");
873
	`mkdir -p /etc/guacamole/extensions`;
874
	`cp /usr/share/stabile/guacamole-auth-stabile-0.9.14.jar /etc/guacamole/extensions/guacamole-auth-stabile-0.9.14.jar`;
875
	`systemctl restart tomcat8`;
876
	# Tomcat has not created guacamle folder yet, so we do this in pressurecontrol
877
	# `cp /usr/share/stabile/guacamole-index.html /var/lib/tomcat8/webapps/guacamole/index.html`;
878

    
879
	# Provide time service to pistons
880
	unless (`grep '10.0.0.1' /etc/openntpd/ntpd.conf`) {
881
		`echo "listen on 10.0.0.1" >> /etc/openntpd/ntpd.conf`;
882
		`systemctl restart openntpd`;
883
	}
884
	return 0;
885
}
886

    
887
# Setup the tftp server installation
888
sub setup_tftp {
889
	print $out "Setting up tftpd-hpa\n";
890

    
891
	#############
892
	# Variables #
893
	#############
894
	my $cfg_src = '/usr/share/stabile/TFTP';
895
	my $cfg_dest = '/etc/default';
896

    
897
	#my $gpxe_folder = 'Apache2/gpxe';
898
	#my $gpxe_dest = '/var/www/';
899

    
900
	##################
901
	# Install Config # 
902
	##################
903
	my $ret = copy("$cfg_src/tftpd-hpa", "$cfg_dest/tftpd-hpa") or die $!;
904
	if(!$ret && !ask("Error: $!. Ignore error...")) {
905
		return 1;
906
	}
907

    
908
	# Enable local KVM's VNC server - outside access disabled by pressurecontrol
909
	my $cmd = 'sed -i ' . '\'s/^# *\\(vnc_listen = .*\\)$/\\1/\' ' .  "/etc/libvirt/qemu.conf";
910
	$ret = sysexec($cmd);
911
	if($ret != 0 && !ask("Ignore error...")) {
912
		return $ret;
913
	}
914

    
915
	#system("cp -r $gpxe_folder $gpxe_dest");
916

    
917
	foreach my $piston_version (@piston_versions) {
918
		my $piston_root = "$tftp_root/$piston_version/casper/filesystem.dir";
919

    
920
		unless (-e "$cfg_src/$piston_version.tar.gz") {
921
			print $out "Skipping $piston_version - $cfg_src/$piston_version.tar.gz not found\n";
922
			print $out "To install $piston_version Stabile node, please apt install stabile-$piston_version-node\n";
923
			next;
924
		} elsif (-d "$tftp_root/$piston_version/casper/filesystem.dir") {
925
			print $out "Skipping $piston_version - already unpacked\n";
926
			next;
927
		} else {
928
			print $out "Insttalling $piston_version piston \n";
929
		}
930

    
931
        unless (-d "$piston_root/etc/init.d") { # Don't overwrite existing piston root
932
    		File::Path::make_path($piston_root);
933
            $cmd = "tar --strip=1 -zxf $cfg_src/$piston_version.tar.gz -C $piston_root";
934
			print $out $cmd, "\n";
935
            $ret = sysexec($cmd);
936
            if($ret != 0 && !ask("Ignore error...")) {
937
                return $ret;
938
            }
939
        } else {
940
			print $out "Skipping piston root $tftp_root/$piston_version - already exists.\n";
941
		}
942

    
943
    # We do not include these in the .deb file, since they will then be removed when removing stabile,
944
    # e.g. when reinstalling, which means running nodes will no longer have their mount point
945
        `mkdir -p $piston_root/mnt/stabile/images` unless (-d "$piston_root/mnt/stabile/images");
946
        `mkdir -p $piston_root/mnt/stabile/node` unless (-d "$piston_root/mnt/stabile/node");
947
		# Enable KVM's VNC server
948
		$cmd = 'sed -i ' . '\'s/^# *\\(vnc_listen = .*\\)$/\\1/\' ' .  "$piston_root/etc/libvirt/qemu.conf";
949
		$ret = sysexec($cmd);
950
		if($ret != 0 && !ask("Ignore error...")) {
951
			return $ret;
952
		}
953

    
954
		# force pistons to boot from the interface given as 'bootif'
955
		# on the kernel command line, rather than the first available
956
		# interface -- which frequently happens to be wrong...
957
		my $kernel_version = readlink($piston_root . '/vmlinuz');
958
		$kernel_version =~ s/^boot\/vmlinuz-//;
959

    
960

    
961
		# Only relevant for Lucid piston
962
		# my @files =
963
		# 	glob($piston_root .
964
		# 		 '/usr/share/initramfs-tools/scripts/*/select_eth_device');
965
        #
966
		# if ($#files != -1)
967
		# {
968
		# 	$ret = sysexec('sed', '-i',
969
		# 				   's,l_interfaces="\?\($([^")]*)\)"\?,' .
970
		# 				   'l_interfaces="${bootif:-\1}",',
971
		# 				   @files);
972
        #
973
		# 	if($ret != 0 && !ask("Ignore error...")) {
974
		# 		return $ret;
975
		# 	}
976
		# }
977

    
978
		# Generate initramfs
979

    
980
		unless (-e "$piston_root/boot/initrd.img-$kernel_version") {
981
			$ret = sysexec('mount', '-t', 'proc', 'proc', "$piston_root/proc");
982
			if($ret != 0 && !ask("Ignore error...")) {
983
				return $ret;
984
			}
985

    
986
			if (!expand_template('/usr/share/stabile/initramfs.conf',
987
								 $piston_root . '/etc/initramfs-tools') &&
988
				ask("Error installing APT source. Continue?")) {
989
				return 1;
990
			}
991

    
992
			$ret = sysexec('chroot', $piston_root, 'update-initramfs', '-u',
993
						   '-k', $kernel_version);
994
			sysexec('umount', "$piston_root/proc");
995
			if($ret != 0 && !ask("Ignore error...")) {
996
				return $ret;
997
			}
998
		}
999

    
1000

    
1001
		# Copy over kernels for PXE booting
1002

    
1003
		$ret = hardlink($piston_root . '/boot/vmlinuz-' . $kernel_version,
1004
						$tftp_root . '/vmlinuz-' . $kernel_version);
1005
		if(!$ret && !ask("$!\nIgnore error...")) {
1006
			return $ret;
1007
		}
1008

    
1009
		$ret = hardlink($piston_root . '/boot/initrd.img-' . $kernel_version,
1010
						$tftp_root . '/initrd.img-' . $kernel_version);
1011
		if(!$ret && !ask("$!\nIgnore error...")) {
1012
			return $ret;
1013
		}
1014

    
1015
		# make the first piston version the default
1016
		if ($piston_version eq $piston_versions[0]) {
1017
			$ret = hardlink($piston_root . '/boot/vmlinuz-' . $kernel_version,
1018
							$tftp_root . '/vmlinuz');
1019
			if(!$ret && !ask("$!\nIgnore error...")) {
1020
				return $ret;
1021
			}
1022

    
1023
			$ret = hardlink($piston_root . '/boot/initrd.img-' . $kernel_version,
1024
							$tftp_root . '/initrd.img');
1025
			if(!$ret && !ask("$!\nIgnore error...")) {
1026
				return $ret;
1027
			}
1028
		} else {
1029
			# copy over SSH keys from the first version
1030
			my $srcdir =
1031
				"$tftp_root/$piston_versions[0]/casper/filesystem.dir/etc/ssh";
1032

    
1033
			$ret = unlink(glob($piston_root . '/etc/ssh/ssh_host_*_key*'));
1034
			if($ret == 0 && !ask("Unlink failed - $! " . 'Ignore error...')) {
1035
				return $ret;
1036
			}
1037

    
1038
			foreach my $srcfile (glob($srcdir . '/ssh_host_*_key*')) {
1039
				$ret = copy($srcfile, $piston_root . '/etc/ssh/' .
1040
							basename($srcfile));
1041
				if ($ret != 1 &&
1042
					!ask("Copy failed - $! " . "Ignore error...")) {
1043
						return -1;
1044
				}
1045
			}
1046

    
1047
			chmod(0600, glob($piston_root . '/etc/ssh/ssh_host_*_key'));
1048
			chmod(0644, glob($piston_root . '/etc/ssh/ssh_host_*_key.pub'));
1049
		}
1050

    
1051
		# Configure piston to use the same timezone as us
1052

    
1053
		$ret = copy('/etc/timezone', $piston_root . '/etc/timezone');
1054
		if(!$ret && !ask("Error copy /etc/timezone -> $piston_root/etc/timezone: $!. Ignore error...")) {
1055
			return 1;
1056
		}
1057

    
1058
#		$ret = sysexec("chroot $piston_root dpkg-reconfigure -f noninteractive tzdata");
1059
#		if($ret != 0 && !ask("Ignore error...")) {
1060
#			return $ret;
1061
#		}
1062

    
1063
		if (-e $piston_root . '/etc/hostname') {
1064
			$ret = unlink($piston_root . '/etc/hostname');
1065
			if($ret != 1 && !ask("Ignore error...")) {
1066
				return $ret;
1067
			}
1068
		}
1069

    
1070
		# Copy piston config
1071
        print $out `mkdir $piston_root/etc/stabile/` unless (-e "$piston_root/etc/stabile");
1072
		unless (-e "$piston_root/etc/stabile/nodeconfig.cfg") {
1073
			my $ret = copy("/usr/share/stabile/nodeconfig.template.cfg", "$piston_root/etc/stabile/nodeconfig.cfg");
1074
			if($ret == 0 && !ask("Error copying nodeconfig.cfg into stabile. Continue?")) {
1075
			   return 1;
1076
			}
1077
			print $out "Setting piston DATA_NIC to $externalnic\n";
1078
			`perl -pi -e 's/DATA_NIC=.*/DATA_NIC=$externalnic/' $piston_root/etc/stabile/nodeconfig.cfg`;
1079
			print $out "Setting piston ADMIN_NIC to $internalnic\n";
1080
			`perl -pi -e 's/ADMIN_NIC=.*/ADMIN_NIC=$internalnic/' $piston_root/etc/stabile/nodeconfig.cfg`;
1081
		}
1082

    
1083
		# Set up SOL for IPMI and AMT
1084
		my $ltext = <<END
1085
start on stopped rc RUNLEVEL=[2345]
1086
stop on runlevel [!2345]
1087

    
1088
respawn
1089
exec /sbin/getty -8 115200,38400,9600 ttyS1
1090
END
1091
		;
1092
		print $out `echo "$ltext" > $piston_root/etc/init/ttyS1.conf` unless (-e "$piston_root/etc/init/ttyS1.conf");
1093
		$ltext = <<END
1094
start on stopped rc RUNLEVEL=[2345]
1095
stop on runlevel [!2345]
1096

    
1097
respawn
1098
exec /sbin/getty -8 115200,38400,9600 ttyS4
1099
END
1100
		;
1101
		print $out `echo "$ltext" > $piston_root/etc/init/ttyS4.conf` unless (-e "$piston_root/etc/init/ttyS4.conf");
1102

    
1103
	}
1104

    
1105
	$ret = chmod(0644, glob $tftp_root . '/vmlinuz*');
1106
	if(!$ret && !ask("$!\nIgnore error...")) {
1107
		return $ret;
1108
	}
1109

    
1110
	unless (-e "$tftp_root/pxelinux.cfg") {
1111
		$ret = sysexec("mkdir -p $tftp_root/pxelinux.cfg");
1112
		$ret .= sysexec("chmod 777 $tftp_root/pxelinux.cfg");
1113
		if($ret != 0 && !ask("Ignore error...")) {
1114
			return $ret;
1115
		}
1116
		$ret = copy('/usr/share/stabile/TFTP/pxe_default', "$tftp_root/pxelinux.cfg/default");
1117
		if(!$ret && !ask("Error: $!. Ignore error...")) {
1118
			return 1;
1119
		}
1120
		$ret = copy('/usr/lib/PXELINUX/pxelinux.0', "$tftp_root/pxelinux.0");
1121
		if(!$ret && !ask("Error: $!. Ignore error...")) {
1122
			return 1;
1123
		}
1124
		$ret = copy('/usr/lib/syslinux/modules/bios/menu.c32', "$tftp_root/menu.c32");
1125
		if(!$ret && !ask("Error: $!. Ignore error...")) {
1126
			return 1;
1127
		}
1128
		$ret = copy('/usr/lib/syslinux/modules/bios/ldlinux.c32', "$tftp_root/ldlinux.c32");
1129
		if(!$ret && !ask("Error: $!. Ignore error...")) {
1130
			return 1;
1131
		}
1132
		$ret = copy('/usr/lib/syslinux/modules/bios/libutil.c32', "$tftp_root/libutil.c32");
1133
		if(!$ret && !ask("Error: $!. Ignore error...")) {
1134
			return 1;
1135
		}
1136
	}
1137

    
1138
	#print "Transfer of node root-filesystem completed.\n";
1139
	#print "When you press a key you will be editing the PXE config.\n";
1140
	#print "Please edit the NFS ip address: ..";
1141
	#prompt("");
1142

    
1143
	#edit_file("$tftp_root/pxelinux.cfg/default");
1144

    
1145
	$ret = sysexec('/bin/systemctl restart tftpd-hpa');
1146
	if($ret != 0 && !ask("Ignore error...")) {
1147
		return 1;
1148
	}
1149

    
1150
	return 0;
1151
}
1152

    
1153
# Sub for installing apache config files
1154
# and generating a self-signed certificate.
1155
sub configure_apache {
1156
	my $certificate_out_dir = "/etc/apache2/ssl";
1157
	my $cfg = '/usr/share/stabile/Apache2/apache_config';
1158
	my $cfg_ssl = '/usr/share/stabile/Apache2/apache_config.ssl';
1159
	my $hostname = $g_hostname;
1160

    
1161
	if(!$hostname) {
1162
		$hostname = `hostname -A | cut -d ' ' -f 1`;
1163
		chomp($hostname);
1164
		if (!$hostname) {
1165
			$hostname = `hostname -f`;
1166
			chomp($hostname);
1167
		}
1168
	}
1169

    
1170
	# Set the global hostname. Used for bind installation.
1171
	# But not needed right now.
1172
	$g_hostname = $hostname;
1173
	print $out "Using $hostname as virtual_host name.\n";
1174
	print $out `echo $hostname > /etc/hostname`;
1175
	print $out `hostname $hostname`;
1176
	my $cmd = qq|perl -pi -e 's/^127\.0\.1\.1.*\$/127.0.1.1 $hostname/' /etc/hosts|;
1177
	print $out `$cmd`;
1178

    
1179
	my $sslcert = '/etc/apache2/ssl/stabile.crt';
1180
	my $sslkey = '/etc/apache2/ssl/stabile.key';
1181
	my $sslchain = '';
1182
	my $sslcerts;
1183
	my $plainsite;
1184

    
1185
	my @sites = split("\n", `grep -l 'VirtualHost.*:80' /etc/apache2/sites-available/*`);
1186
	foreach my $site (@sites) {
1187
		my $asite = $site;
1188
		$asite =~ s/-available/-enabled/;
1189
		if (-e $asite) {
1190
			$plainsite = $site;
1191
			last;
1192
		}
1193
	}
1194
	my $sslsite;
1195
	@sites = split("\n", `grep -l 'VirtualHost.*:443' /etc/apache2/sites-available/*`);
1196
	foreach my $site (@sites) {
1197
		my $asite = $site;
1198
		$asite =~ s/-available/-enabled/;
1199
		if (-e $asite) {
1200
			$sslsite = $site;
1201
			last;
1202
		}
1203
	}
1204

    
1205
	#########################################
1206
	# Installing Apache configuration files #
1207
	#########################################
1208
	my $cfg_out = "/etc/apache2/sites-available/stabile.conf";
1209
	$cfg_out = "/etc/apache2/conf-available/stabile.conf" if ($plainsite);
1210
	my $cfg_out_ssl = "/etc/apache2/sites-available/stabile-ssl.conf";
1211
	$cfg_out_ssl = "/etc/apache2/conf-available/stabile-ssl.conf" if ($sslsite);
1212
	$sslchain = '/etc/apache2/ssl/stabile.chain' if ($letsencrypt);
1213

    
1214
	if ( -f "$certificate_out_dir/stabile.key" && -f "$certificate_out_dir/stabile.crt" ) {
1215
		print $out "Skipping SSL configuration and certificate generation\n";
1216
		$sslcert = `sed -n -e 's/[ ]*SSLCertificateFile[ ]*//pi' $cfg_out_ssl`;
1217
		chomp $sslcert;
1218
		$sslkey = `sed -n -e 's/[ ]*SSLCertificateKeyFile[ ]*//pi' $cfg_out_ssl`;
1219
		chomp $sslkey;
1220
		$sslchain = `sed -n -e 's/[ ]*SSLCACertificateFile[ ]*//pi' $cfg_out_ssl`;
1221
		chomp $sslchain;
1222
		$sslcerts = 1;
1223
	}
1224

    
1225
	open(my $cfg_fh, '<', $cfg) or die $!;
1226
	open(my $cfg_out_fh, '>', $cfg_out) or die $!;
1227
	while(my $line = <$cfg_fh>) {
1228
		$line =~ s/\[%HOSTNAME%\]/$hostname/g;
1229
		unless ($plainsite && $line =~ /(VirtualHost|DocumentRoot)/i) {
1230
			print $cfg_out_fh $line;
1231
		}
1232
	}
1233
	close($cfg_fh) or die $!;
1234
	close($cfg_out_fh) or die $!;
1235
	if ($plainsite && !(`grep stabile $plainsite`)) {
1236
		`perl -pi -e "s/<\\/VirtualHost>/Include \\/etc\\/apache2\\/conf-available\\/stabile.conf\\n<\\/VirtualHost>/gi" "$plainsite"`;
1237
	}
1238

    
1239
	open(my $ssl_fh, '<', $cfg_ssl) or die $!;
1240
	open(my $ssl_out_fh, '>', $cfg_out_ssl) or die $!;
1241
	while(my $line = <$ssl_fh>) {
1242
		$line =~ s/\[%HOSTNAME%\]/$hostname/g;
1243
		if (!$sslsite) { # configuring new SSL-site
1244
			$line =~ s/\[%SSLCERTIFICATEFILE%\]/$sslcert/g;
1245
			$line =~ s/\[%SSLCERTIFICATEKEYFILE%\]/$sslkey/g;
1246
			if ($sslchain) {
1247
				$line =~ s/\[%SSLCACERTIFICATEFILE%\]/$sslchain/g;
1248
			} else {
1249
				$line =~ s/( +SSLCACertificateFile \[%SSLCACERTIFICATEFILE%\])/# $1/g;
1250
			}
1251
		} else {
1252
			$line =~ s/( +SSLCertificateFile \[%SSLCERTIFICATEFILE%\])/# $1/g;
1253
			$line =~ s/( +SSLCertificateKeyFile \[%SSLCERTIFICATEKEYFILE%\])/# $1/g;
1254
			$line =~ s/( +SSLCACertificateFile \[%SSLCACERTIFICATEFILE%\])/# $1/g;
1255
		}
1256
		unless ($sslsite && $line =~ /(VirtualHost|DocumentRoot)/i) {
1257
			print $ssl_out_fh $line;
1258
		}
1259
	}
1260
	close($ssl_fh) or die $!;
1261
	close($ssl_out_fh) or die $!;
1262

    
1263
	if ($sslsite && !(`grep stabile $sslsite`)) {
1264
		`perl -pi -e "s/<\\/VirtualHost>/Include \\/etc\\/apache2\\/conf-available\\/stabile-ssl.conf\\n<\\/VirtualHost>/gi" "$sslsite"`;
1265
		`ln -s /var/www/stabile /var/www/html/stabile` if (-e "/var/www/html" && !(-e "/var/www/html/stabile"));
1266
	}
1267
# disable private tmp
1268
	`perl -pi -e "s/PrivateTmp=true/PrivateTmp=false/" /lib/systemd/system/apache2.service`;
1269
	system("systemctl daemon-reload");
1270

    
1271
	if ($sslsite) { # We already have a SSL-enabled site, assume SSL is configured
1272
		print $out "Found SSL-enabled site $sslsite - assuming certs are configured\n";
1273
	} elsif (!$sslcerts) { # Generate new certs
1274
		# Obtain certificat via Let's Encrypt
1275
		if ($letsencrypt) {
1276
			my $wd = "/etc/stabile/getssl";
1277
			if (! -e("$wd/$hostname")) {
1278
				`find $wd ! -name '$hostname' -type d -exec rm -rf {} +`; # Remove leftover dirs in case of change of hostname
1279
				`mkdir -p $wd/$hostname` unless -e ("$wd/$hostname");
1280
				`cp /usr/share/stabile/getssl.cfg $wd/$hostname/` unless -e ("$wd/$hostname/getssl.cfg");
1281
			}
1282
			`echo "127.0.0.1 $hostname" >> /etc/hosts` unless (`grep "$hostname" /etc/hosts`);
1283
			print $out "Getting SSL certificate from Let's Encrypt\n";
1284
			print $out `/usr/local/bin/getssl -w $wd $hostname`;
1285
		}
1286
		# Generate our own if letsencrypt failed or was deselected
1287
		if (!(-e "$certificate_out_dir/stabile.crt")) {
1288
			`perl -pi -e "s/( +SSLCACertificateFile .+)/# $1/i" $cfg_out_ssl`; # remove reference to ssl chain cert
1289
			print $out "Generating self-signed certificate with OpenSSL, you can add your own later\n";
1290
			# Replace the hostname in SSL_Config file.
1291
			open(my $ssl_cfg_tmp, '<', '/usr/share/stabile/openssl.config.template') or die $!;
1292
			open(my $ssl_cfg, '>', '/usr/share/stabile/openssl.config') or die $!;
1293
			while(my $line = <$ssl_cfg_tmp>) {
1294
				$line =~ s/\[%HOSTNAME%\]/$hostname/g;
1295
				print $ssl_cfg $line;
1296
			}
1297
			close($ssl_cfg_tmp);
1298
			close($ssl_cfg);
1299
			system("openssl req -newkey rsa:2048 -nodes -x509 -days 780 -out /usr/share/stabile/certificate.crt -utf8 -config /usr/share/stabile/openssl.config -keyout /usr/share/stabile/privkey.key");
1300
			if($? != 0) {
1301
				print $out "Error generating certificate\n";
1302
				return 1;
1303
			}
1304
			# Making sure $certificate_out_dir exists.
1305
			if(! -d $certificate_out_dir) {
1306
				mkdir($certificate_out_dir) or die "Failed to create directory: $!";
1307
			}
1308
			# install certificates.
1309
			move('/usr/share/stabile/certificate.crt', $certificate_out_dir . "/stabile.crt") or die $!;
1310
			move('/usr/share/stabile/privkey.key', $certificate_out_dir . "/stabile.key") or die $!;
1311
		}
1312
	}
1313

    
1314
#	print $out `echo "AddHandler cgi-script cgi pl" > /etc/apache2/sites-available/default`;
1315

    
1316
	###########################
1317
	# Enabling site in apache #
1318
	###########################
1319
	print $out "Configuration installed, enabling site\n";
1320

    
1321
#	system("/etc/init.d/apache2 stop");
1322
#	system("a2dissite default");
1323
	system("a2ensite stabile") unless ($plainsite);
1324
	system("a2ensite stabile-ssl") unless ($sslsite);
1325
	system("a2enmod ssl");
1326
	system("a2enmod cgid");
1327
	system("a2enmod rewrite");
1328
	system("a2enmod auth_tkt");
1329
	system("a2enmod perl");
1330
	system("a2enmod proxy_http");
1331
	system("a2enmod headers");
1332

    
1333
	# Reloading apache2
1334
	system("systemctl restart apache2");
1335

    
1336
	########################################
1337
	# Generating htpasswd-file for pistons #
1338
	########################################
1339

    
1340
	print $out "Creating /etc/apache2/htpassswd-piston\n";
1341
	system("htpasswd -bc /etc/apache2/htpasswd-piston irigo sunshine");
1342

    
1343
	#######################################################
1344
	# Creating /mnt/stabile/images/irigo and settings rights #
1345
	#######################################################
1346

    
1347
	my $uid = getpwnam('www-data');
1348
	my $gid = getgrnam('www-data');
1349

    
1350
	my $ret = chown($uid, $gid, '/mnt/stabile/images');
1351
	$ret = chown($uid, $gid, '/mnt/stabile/images/irigo');
1352

    
1353
 	if ($ret != 1 && !ask("Ignore error...")) {
1354
		return -1;
1355
	}
1356

    
1357
    unless (`grep ENGINENAME=. /etc/stabile/config.cfg`) {
1358
		print $out "Setting ENGINENAME to $g_hostname\n";
1359
        `perl -pi -e 's/ENGINENAME=.*/ENGINENAME=$g_hostname/' /etc/stabile/config.cfg`;
1360
    }
1361

    
1362
	return 0;
1363
}
1364

    
1365
# Install modules from CPAN
1366
sub install_cpan {
1367
#    return unless ($ARGV[0] eq 'upgrade');
1368

    
1369
    `perl /usr/share/stabile/install_cpan_modules.pl`;
1370
    return 0;
1371

    
1372
	my @cpan_packages = @_;
1373
	foreach my $pack(@cpan_packages) {
1374
		my $cmd = "PERL_MM_USE_DEFAULT=1 perl -MCPAN -e 'install $pack'";
1375
		print $out $cmd . "\n";
1376

    
1377
		# Run command and continue if successfull, else return error.
1378
		my $ret = sysexec($cmd);
1379

    
1380
		if($ret != 0 && !ask("Ignore error...")) {
1381
			print $out "Got an error: $ret\n";
1382
		}
1383
	}
1384

    
1385
	return 0;
1386
}
1387

    
1388
## Creates the database db_name, and installs all .sql files from the hardcoded_foldername into it
1389
sub create_database {
1390
	require DBI;
1391
	require DBIx::Simple;
1392
	require SQL::Abstract;
1393

    
1394
	# Fix for problem connecting with jdbc to mysql. See: https://www.programmersought.com/article/64995511893/
1395
	`perl -pi -e "s/\\[mysqld\\]/[mysqld]\nskip_ssl/" /etc/mysql/mysql.conf.d/mysqld.cnf` unless (`grep "skip_ssl" /etc/mysql/mysql.conf.d/mysqld.cnf`);
1396

    
1397
	my $db_name = shift or die;
1398
	my $db_host = shift or die;
1399
	my $db_user = shift or die;
1400
	my $db_pass = shift || $g_db_pass;
1401
	my $dbinit; # Set if database has been initialized
1402

    
1403
	# TODO 
1404
	# Accept a folder with stored .sql files dynamically,
1405
	# and insert them into the new database. For now:
1406
	my $hardcoded_foldername = '/usr/share/stabile/SQL';
1407

    
1408
	if(! defined($db_pass)) {
1409
	#	$db_pass = ask_password("Enter desired MySQL root password");
1410
		$db_pass = '';
1411
	}
1412

    
1413
	my $drh = DBI->install_driver('mysql');
1414

    
1415
	# Test if there is a password for mysql.
1416
	# If there isn't, set the password.
1417
	# If not, just assume they know what they're doing.
1418
	print $out "Trying to connect to mysql on $db_host with $db_user, $db_pass\n";
1419
	my $dbh = $drh->connect("mysql", $db_user, $db_pass, { host => $db_host});
1420
	# Test if the MySQL password is identical to the password we have.
1421
	# If it is, we don't set the new password.
1422
	if (!$dbh) {
1423
		print $out "Could not connect to database!!\n";
1424
		return;
1425
#		$db_pass = ask_password( "Reenter MySQL password for user '$db_user'", 1);
1426
	} else {
1427
		print $out "Successfully connected to DB\n";
1428
	}
1429

    
1430
	#######################
1431
	# Create the database #
1432
	#######################
1433

    
1434
    # Just try to create the database, and if it fails, it already exists
1435
    $drh->func('createdb', $db_name, $db_host, $db_user, $db_pass, 'admin');
1436

    
1437
    $dbh = $drh->connect("$db_name", $db_user, $db_pass,
1438
                         { host => $db_host, RaiseError => 1,
1439
                           mysql_multi_statements => 1, });
1440

    
1441
    my $db = new DBIx::Simple($dbh) or die DBIx::Simple->error;
1442
    $db->abstract = new SQL::Abstract();
1443

    
1444
    # Test if database exists and contains user irigo in table users
1445
    my @i_users = $db->query("SELECT username FROM users WHERE username=\"irigo\"")->flat;
1446
    # If db not found, read in SQL files
1447
    if (!@i_users) {
1448
		print $out "User irigo not found in DB $db_name, initializing DB\n";
1449

    
1450
        #######################################################
1451
        # Read all our saved .sql files into the new database #
1452
        #######################################################
1453

    
1454
        my @files = glob($hardcoded_foldername . '/*.sql');
1455
        foreach my $sql(@files) {
1456
			print $out "$sql\n";
1457
            my $source = `cat $sql`;
1458
            my $ret = $dbh->do($source);
1459

    
1460
            if (!defined $ret || $ret ne "0E0") {
1461
				print $out DBI->errstr;
1462
                return 1;
1463
            }
1464
        }
1465
		$dbinit = 1;
1466
    } else {
1467
		print $out "User irigo found in DB $db_name, assuming all is good\n";
1468
	}
1469

    
1470
	###########################
1471
	# Granting user privilege #
1472
	###########################
1473
	{
1474
		my $cmd = "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON $db_name.* TO irigo\@localhost IDENTIFIED BY 'sunshine'";
1475

    
1476
		if($dbh->do($cmd) ne "0E0") {
1477
			print $out "!!! Error granting access to user irigo. This needs to be fixed.\n";
1478
		}
1479
	}
1480

    
1481
	##############################
1482
	# Create initial user in SQL #
1483
	##############################
1484

    
1485
#	if (($db->select('users', 'count(*)')->list)[0] eq 0) {
1486
		# Don't encrypt it. If stabile sees the unencrypted password,
1487
		# it will automatically create the unix user.
1488
	if ($dbinit) {
1489
		$i_user = $i_user || 'origo';
1490
		$i_password = $i_password || 'sunshine';
1491
	}
1492
    if ($i_user && $i_password) {
1493
        my @i_users = $db->query("SELECT username FROM users WHERE username=\"$i_user\"")->flat;
1494
        if (!@i_users) {
1495
			print $out "Adding initial user $i_user\n";
1496
            $db->insert('users', { 'username' => $i_user.'',
1497
                                   'password' => $i_password.'',
1498
                                   'fullname' => $i_user.'',
1499
								   'created'  => time(),
1500
                                   'allowinternalapi' => '1',
1501
                                   'privileges' => 'a' })
1502
                || die $db->error;
1503
        } else {
1504
			print $out "Setting password for initial user $i_user\n";
1505
            $db->update('users', {'password' => $i_password.'', 'privileges' => 'a' }, { 'username' => $i_user.''})
1506
                || die $db->error;
1507
        }
1508
	}
1509
    if ($i_user ne 'irigo') {
1510
        my @i_users = $db->query("SELECT username FROM users WHERE username=\"irigo\"")->flat;
1511
        if (!@i_users) {
1512
			print $out "Also adding irigo user (without password, in order to allow stash use)\n";
1513
            $db->insert('users', { 'username' => 'irigo',
1514
                                   'password' => '',
1515
                                   'privileges' => 'a' })
1516
                || die $db->error;
1517

    
1518
        }
1519
    }
1520
    # Create guest user
1521
    my @g_users = $db->query("SELECT username FROM users WHERE username=\"guest\"")->flat;
1522
    if (!@g_users) {
1523
        print $out "Adding guest user\n";
1524
        $db->insert('users', { 'username' => 'guest',
1525
                               'password' => '',
1526
                               'allowinternalapi' => '0',
1527
                               'privileges' => 'd' })
1528
            || die $db->error;
1529
    } else {
1530
        print $out "Removing priviliges from guest user\n";
1531
        $db->update('users', {'password' => '', 'privileges' => 'd' }, { 'username' => 'guest'})
1532
            || die $db->error;
1533
    }
1534
#	}
1535

    
1536
	#############################################################
1537
	# Creating nodeidentities to allow integration of new nodes #
1538
	#############################################################
1539

    
1540
	foreach my $piston_version (@piston_versions) {
1541
		my $piston_root = "$tftp_root/$piston_version/casper/filesystem.dir";
1542
		next unless (-e $piston_root && -e "$piston_root/bin/bash");
1543

    
1544
		my $kernel_version = readlink($piston_root . '/vmlinuz');
1545
		$kernel_version =~ s/^boot\/vmlinuz-//;
1546
		next unless ($kernel_version);
1547

    
1548
		$db->insert('nodeidentities', {
1549
			identity => "kvm-$piston_version-x64",
1550
			hypervisor => 'kvm',
1551
			dist => $piston_version,
1552
			formats => 'img,qcow,qcow2',
1553
			name => "kvm-$piston_version-x64",
1554
			sleepafter => 0,
1555
			kernel => $kernel_version,
1556
			path => '/mnt/stabile/tftp/' . $piston_version,
1557
			arch => undef,
1558
					});
1559

    
1560
		$db->insert('nodeidentities', {
1561
			identity => "vbox-$piston_version-x64",
1562
			hypervisor => 'vbox',
1563
			dist => $piston_version,
1564
			formats => 'vmdk,vdi,vhd',
1565
			name => "vbox-$piston_version-x64",
1566
			sleepafter => 0,
1567
			kernel => $kernel_version,
1568
			path => '/mnt/stabile/tftp/' . $piston_version,
1569
			arch => undef,
1570
					});
1571
	}
1572

    
1573
	my @i_ids = $db->query("SELECT name FROM nodeidentities WHERE identity=\"default\"")->flat;
1574
	unless (@i_ids) {
1575
		my $piston_version = $piston_versions[0];
1576

    
1577
		$db->update('nodeidentities',
1578
		            { identity => 'default' },
1579
		            { name => "kvm-$piston_version-x64" })
1580
			|| print $out $db->error,"\n";
1581
	}
1582

    
1583
	###################################
1584
	# Alter tables to add new columns #
1585
	###################################
1586
	my %new_columns = (
1587
		'users' => {
1588
			'allowfrom' => 'varchar(512)',
1589
			'lastloginfrom' => 'varchar(15)',
1590
			'lasttkt' => 'varchar(512)',
1591
			'allowinternalapi' => 'varchar(4)',
1592
		},
1593
	);
1594

    
1595
	while (my ($table, $new_columns) = each %new_columns) {
1596
		while (my ($cname, $ctype) = each %$new_columns) {
1597
			if (! $db->select($table, $cname)) {
1598
				$db->query("ALTER TABLE $table ADD $cname $ctype")
1599
					|| print $out $db->error,"\n";
1600
			}
1601
		}
1602
	}
1603

    
1604
	## TODO: Check exit status of MYSQL commands
1605

    
1606
	# Create symlink to MySQL driver for Tomcat
1607
	`ln -s ../../java/mysql-connector-java.jar /usr/share/tomcat8/lib/` unless (-e "/usr/share/tomcat8/lib/mysql-connector-java.jar");
1608

    
1609
	return 0;
1610
}
1611

    
1612
# Install the Webmin API.
1613
sub install_webmin_api {
1614
	my $tarball = shift;
1615
	my $ret;
1616

    
1617
	# HACK: install Webmin API
1618
	$ret = sysexec("tar zxf $tarball");
1619

    
1620
	if($ret != 0 && !ask("Ignore error...")) {
1621
		return $ret;
1622
	}
1623

    
1624
	$ret = sysexec("cd Webmin-API-1.0 && perl Makefile.PL && make install");
1625

    
1626
	if($ret != 0 && !ask("Ignore error...")) {
1627
		return $ret;
1628
	}
1629

    
1630
	$ret = rmtree("Webmin-API-1.0");
1631

    
1632
	if($ret == 0 && !ask("Ignore error...")) {
1633
		return $ret;
1634
	}
1635

    
1636
	$ret = sysexec("chmod a+rX /etc/webmin/miniserv.conf");
1637

    
1638
	if($ret != 0 && !ask("Ignore error...")) {
1639
		return $ret;
1640
	}
1641

    
1642
	{
1643
		my $oldcwd = cwd();
1644
		require Webmin::API;
1645

    
1646
		my $cfg_path = '/etc/webmin/config';
1647
		my $cfg = {};
1648

    
1649
		Webmin::API::read_file($cfg_path, $cfg);
1650

    
1651
		$cfg->{'referer'} = 1;
1652
		$cfg->{'referers'} = '';
1653
		$cfg->{'referers_none'} = '0';
1654

    
1655
		Webmin::API::write_file($cfg_path, $cfg);
1656

    
1657
		$cfg_path = '/etc/webmin/mon/config';
1658
		$cfg = {};
1659

    
1660
		Webmin::API::read_file($cfg_path, $cfg);
1661

    
1662
		$cfg->{'mon_cgi'} = '/usr/lib/cgi-bin/mon.cgi';
1663
		$cfg->{'pid_file'} = '/var/run/mon/mon.pid';
1664

    
1665
		Webmin::API::write_file($cfg_path, $cfg);
1666

    
1667
		chdir($oldcwd);
1668
	}
1669

    
1670
	return 0;
1671
}
1672

    
1673
# Upgrade existing stabile.
1674
sub upgrade {
1675
	# Bump up mon limits
1676
#	 `perl -pi -e "s/(maxprocs\\s+)= \\d+/maxprocs = 100/" /etc/mon/mon.cf`;
1677
#	 `perl -pi -e "s/(histlength\\s+)= \\d+/histlength = 10000/" /etc/mon/mon.cf`;
1678

    
1679
	 # Allow mon to actually save disabled states
1680
#	 `chown mon:mon /usr/lib/mon/state.d`;
1681

    
1682
	# Change uuid source for libvirt, because some vendors use same uuid for different nodes
1683
	`perl -pi -e "s/#host_uuid_source =.*/host_uuid_source = 'machine-id'/" /etc/libvirt/libvirtd.conf`;
1684
	foreach my $piston_version (@piston_versions) {
1685
		my $piston_root = "$tftp_root/$piston_version/casper/filesystem.dir";
1686
		if (!(-e "$piston_root/bin/bash")) {
1687
			print $out "** Invalid piston found in $piston_root **\n";
1688
			next;
1689
		}
1690
		else {
1691
			print $out "** Now upgrading software and configuration in existing node root $piston_root **\n";
1692
		}
1693

    
1694
		# Copy in new movepiston
1695
		my $ret = copy('/usr/local/sbin/movepiston', "$piston_root/usr/local/sbin/movepiston");
1696
		if(!$ret && !ask("Error: $!. Ignore error...")) {return 1;}
1697
		else {`chmod 755 $piston_root/usr/local/sbin/movepiston`};
1698

    
1699
		# Handle piston config
1700
		if (-e "$piston_root/etc/stabile/nodeconfig.cfg") {
1701
			print $out "** Now upgrading nodeconfig.cfg in existing $piston_versions[0] piston **\n";
1702
			my $cfgtpl = new Config::Simple("/usr/share/stabile/nodeconfig.template.cfg");
1703
			my %cfgtplhash = $cfgtpl->vars();
1704
			my $cfg = new Config::Simple("$piston_root/etc/stabile/nodeconfig.cfg");
1705
			my %cfghash = $cfg->vars();
1706

    
1707
			# Copy in new config keys and values
1708
			foreach my $pref (keys %cfgtplhash) {
1709
				if (!(defined $cfg->param($pref))) {
1710
					$cfg->param($pref, $cfgtplhash{$pref});
1711
				}
1712
			}
1713
			# Weed out obsolete config keys
1714
			foreach my $pref (keys %cfghash) {
1715
				if (!(defined $cfgtpl->param($pref))) {
1716
					$cfg->delete($pref);
1717
				}
1718
			}
1719
			$cfg->save();
1720

    
1721
			`perl -pi -e "s/#host_uuid_source =.*/host_uuid_source = 'machine-id'/" $piston_root/etc/libvirt/libvirtd.conf`;
1722
		}
1723
	}
1724

    
1725
	`echo "ALTER TABLE networks ADD systems varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1726
	`echo "ALTER TABLE networks ADD systemnames varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1727
	`echo "ALTER TABLE systems ADD networkuuids varchar(2048);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1728
	`echo "ALTER TABLE systems ADD networknames varchar(2048);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1729
	`echo "ALTER TABLE domains ADD networkuuids varchar(2048);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1730
	`echo "ALTER TABLE domains ADD networknames varchar(2048);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1731
	`echo "ALTER TABLE systems ADD autostart varchar(16);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1732
	`echo "ALTER TABLE users ADD dnsdomains varchar(512);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1733
	`echo "ALTER TABLE users ADD appstoreurl varchar(512);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1734
	`echo "ALTER TABLE users ADD totpsecret varchar(32);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1735

    
1736
	`echo "ALTER TABLE networks MODIFY name varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1737

    
1738
	#	print $out "** Now upgrading software in existing valve **\n";
1739
#	print $out `perl -MCPAN -e 'install Locale::TextDomain'`;
1740
#	print $out "** Now upgrading database **\n";
1741
#    `echo "ALTER TABLE nodes ADD vmuuids varchar(4096);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1742
#    `echo "ALTER TABLE nodes ADD vmnames varchar(4096);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1743
#    `echo "ALTER TABLE nodes ADD vmusers varchar(4096);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1744
#    `echo "ALTER TABLE nodes ADD nfsroot varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1745
#    `echo "ALTER TABLE nodes ADD kernel varchar(64);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1746
#    `echo "ALTER TABLE nodes ADD maintenance int(4);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1747
#	`echo "ALTER TABLE nodes ADD amtip varchar(15);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1748
#	`echo "ALTER TABLE nodes ADD stor varchar(8);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1749
#    `echo "ALTER TABLE domains ADD image3 varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1750
#    `echo "ALTER TABLE domains ADD image3name varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1751
#    `echo "ALTER TABLE domains ADD image3type varchar(5);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1752
#    `echo "ALTER TABLE domains ADD image4 varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1753
#    `echo "ALTER TABLE domains ADD image4name varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1754
#    `echo "ALTER TABLE domains ADD image4type varchar(5);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1755
#    `echo "ALTER TABLE domains ADD locktonode varchar(5);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1756
#    `echo "ALTER TABLE domains ADD maccpucores int(4);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1757
#    `echo "ALTER TABLE domains ADD statustime varchar(48);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1758
#
1759
#    `echo "ALTER TABLE domains ADD networkuuid3 varchar(48);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1760
#    `echo "ALTER TABLE domains ADD networkid3 varchar(48);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1761
#    `echo "ALTER TABLE domains ADD networkname3 varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1762
#    `echo "ALTER TABLE domains ADD nicmac3 varchar(17);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1763
#
1764
#    `echo "ALTER TABLE systems ADD image varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1765
#    `echo "ALTER TABLE systems ADD networkuuid1 varchar(48);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1766
#    `echo "ALTER TABLE systems ADD internalip varchar(15);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1767
#    `echo "ALTER TABLE users ADD billto varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1768
#
1769
#    `echo "ALTER TABLE images ADD upgradelink varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1770
#    `echo "ALTER TABLE images ADD version varchar(48);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1771
#    `echo "ALTER TABLE images ADD terminallink varchar(256);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1772
#
1773
#    `echo "ALTER TABLE users CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;" | mysql steamregister 2>/dev/null 1>/dev/null`;
1774

    
1775
    # Delete archived billing rows - getting out of hand
1776
#	print $out "** Cleaning up billing tables\n";
1777
#    `echo "DELETE FROM billing_images where userstoragepooltime like '%__-____-__-%.%';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1778
#    `echo "DELETE FROM billing_domains where usernodetime like '%__-____-__-%.%';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1779
#    `echo "DELETE FROM billing_networks where useridtime like '%__-____-__-%.%';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1780
    # Changing to from float to double to increase precision
1781
#    `echo "ALTER TABLE billing_images MODIFY virtualsizeavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1782
#    `echo "ALTER TABLE billing_images MODIFY realsizeavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1783
#    `echo "ALTER TABLE billing_images MODIFY backupsizeavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1784
#    `echo "ALTER TABLE billing_images MODIFY startvirtualsizeavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1785
#    `echo "ALTER TABLE billing_images MODIFY startrealsizeavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1786
#    `echo "ALTER TABLE billing_images MODIFY startbackupsizeavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1787
#    `echo "ALTER TABLE billing_domains MODIFY vcpuavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1788
#    `echo "ALTER TABLE billing_domains MODIFY memoryavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1789
#    `echo "ALTER TABLE billing_domains MODIFY startvcpuavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1790
#    `echo "ALTER TABLE billing_domains MODIFY startmemoryavg double(24,6);" | mysql steamregister 2>/dev/null 1>/dev/null`;
1791
#    # Cleaning up billing tables
1792
##    `echo "UPDATE billing_images SET virtualsizeavg=virtualsize WHERE userstoragepooltime LIKE '%2015-09';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1793
##    `echo "UPDATE billing_images SET virtualsizeavg=virtualsize WHERE userstoragepooltime LIKE '%2015-10';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1794
##    `echo "UPDATE billing_images SET virtualsizeavg=virtualsize WHERE userstoragepooltime LIKE '%2015-11';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1795
##    `echo "UPDATE billing_images SET realsizeavg=realsize WHERE userstoragepooltime LIKE '%2015-09';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1796
##    `echo "UPDATE billing_images SET realsizeavg=realsize WHERE userstoragepooltime LIKE '%2015-10';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1797
##    `echo "UPDATE billing_images SET realsizeavg=realsize WHERE userstoragepooltime LIKE '%2015-11';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1798
##    `echo "UPDATE billing_images SET backupsizeavg=backupsize WHERE userstoragepooltime LIKE '%2015-09';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1799
##    `echo "UPDATE billing_images SET backupsizeavg=backupsize WHERE userstoragepooltime LIKE '%2015-10';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1800
##    `echo "UPDATE billing_images SET backupsizeavg=backupsize WHERE userstoragepooltime LIKE '%2015-11';" | mysql steamregister 2>/dev/null 1>/dev/null`;
1801

    
1802
	print $out "** Now upgrading config.cfg in existing admin node **\n";
1803
    # Handle valve config
1804
    if (-e "/etc/stabile/config.cfg") {
1805
        my $cfgtpl = new Config::Simple("/usr/share/stabile/config.template.cfg");
1806
        my %cfgtplhash = $cfgtpl->vars();
1807
        my $cfg = new Config::Simple("/etc/stabile/config.cfg");
1808
        my %cfghash = $cfg->vars();
1809

    
1810
        # Copy in new config keys and values
1811
        foreach my $pref (keys %cfgtplhash) {
1812
            if (!(defined $cfg->param($pref))) {
1813
                $cfg->param($pref, $cfgtplhash{$pref});
1814
            }
1815
        }
1816
        # Weed out obsolete config keys
1817
        foreach my $pref (keys %cfghash) {
1818
            if (!(defined $cfgtpl->param($pref))) {
1819
                $cfg->delete($pref);
1820
            }
1821
        }
1822
        $cfg->save();
1823
    }
1824

    
1825
	# We use netperf to measure network performance. Outside access is disallowed in pressurecontrol
1826
	print `apt-get -q -y install netperf`; # included in dependencies instead
1827

    
1828
    # We need quotas for VM mountable NFS
1829
#    `perl -pi -e 's/(errors=remount-ro)/errors=remount-ro,usrjquota=aquota.user,jqfmt=vfsv0/' /etc/fstab` unless (`grep aquota /etc/fstab`);
1830
#    # print `apt-get -q -y --force-yes install quota quotatool`; # included in dependencies instead
1831
#    `mount -o remount /`;
1832
#    `quotacheck -avum`;
1833

    
1834
    # We need xinetd to run netperf netserver with hosts.allow support
1835
    # print `apt-get -q -y --force-yes install xinetd`; # included in dependencies instead
1836
    # Install copy of netserver from Precise netperf package
1837
#	print $out `cp -a /usr/share/stabile/netserver /usr/local/bin`;
1838
#	print $out `cp /usr/share/stabile/netperf /etc/xinetd.d/netperf`;
1839
    # Run netserver under xinetd
1840
#    `perl -pi -e "s/(smsqp\\s+11201\\/udp)/$1\nnetperf         12865\\/tcp/" /etc/services` unless (`grep netperf /etc/services`);
1841
#    `echo "netserver: 10.0.0.0/8" >> /etc/hosts.allow` unless (`grep netserver /etc/hosts.allow`);
1842

    
1843
    # We need libmime-lite-perl to send HTML mon alerts
1844
    # print `apt-get -q -y --force-yes install libmime-lite-perl`; # included in dependencies instead
1845

    
1846
    # Copy in new version of amtterm
1847
#    print `cp /usr/share/stabile/amtterm /usr/bin/amtterm`;
1848
    # Upgrade pxe boot files to support AMT terminal
1849
#    `perl -pi -e "s/(bootif=eth0)/console=ttyS4,38400n81 bootif=eth0/" /mnt/stabile/tftp/pxelinux.cfg/*` unless (`grep ttyS4 /mnt/stabile/tftp/pxelinux.cfg/default`);
1850

    
1851
    # Change permissions of dnsmasq configuration files
1852
#    print `chown -R www-data:www-data /etc/stabile/networks`;
1853
    return 0;
1854
}
1855

    
1856
########################
1857
### HELPER FUNCTIONS ###
1858
########################
1859

    
1860
sub edit_file {
1861
	my $path = shift || die "no file to edit!";
1862
	my $prompt = shift;
1863

    
1864
	my $editor = $ENV{"VISUAL"} || $ENV{"EDITOR"} || "nano";
1865

    
1866
	if($prompt) {
1867
		print $out("$prompt\n");
1868
	}
1869

    
1870
	return system("$editor $path");
1871
}
1872

    
1873
# Copies a file to a directory, lets you edit the copy before installing it.
1874
# Meant for configuration files.
1875
sub cp_conf {
1876
	my $file = shift or die "No input";
1877
	my $dest = shift or die "No dest";
1878
	my $edit = shift;
1879
	die "Input file not found: $file" unless (-e $file);
1880
	open(my $fh, '<', $file) or die $!;
1881
	my ($tfh, $tfile) = tempfile() or die $!;
1882
	while(my $line = <$fh>) {
1883
		print $tfh $line;
1884
	}
1885
	close($tfh);
1886
	print $out "Installing '$file'\n";
1887
	print $out "The file will be installed here '$dest'\n";
1888
	if($edit) {
1889
		if(ask("Do you want to edit this file before installation? (You probably should..)")) {
1890
			system("vim $tfile");
1891
		}
1892
	}
1893
	#system("less -N $tfile");
1894
	#unlink($tfile);
1895
	move($tfile, $dest);
1896
}
1897

    
1898
sub db_get {
1899
	my $var = shift;
1900
	my $res = `/usr/share/stabile/db_get.sh $var 2>&1`;
1901
	chomp $res;
1902
	return $res;
1903
}
1904

    
1905
sub ask {
1906
	my $ret;
1907
	my $warning = shift;
1908
	my $default = shift;
1909

    
1910
	if(!$default) {
1911
		$default = 'y';
1912
	} elsif (lc($default) ne 'y' && lc($default) ne 'n') {
1913
		die "Invalid default option passed";
1914
	} else {
1915
		$default = lc($default);
1916
	}
1917

    
1918
	if($default eq 'y') {
1919
		print $out "$warning [Y/n]: \n";
1920
	} else {
1921
		print $out "$warning [y/N]: \n";
1922
	}
1923
	return $default;
1924
}
1925

    
1926
# Pretty print a header.
1927
sub print_headline {
1928
	my $text = shift;
1929
	my $level = shift || 2;
1930

    
1931
	my $char;
1932
	if($level == 1) {
1933
		$char = '*';
1934
	} elsif($level == 2) {
1935
		$char = '-';
1936
	}
1937

    
1938
	# my ($twidth, $theight) = GetTerminalSize();
1939
	my $twidth = 64;
1940

    
1941
	my $indent = " " x ((($twidth - 1)/2) - length($text)/2);
1942
	print $out "$char" x ($twidth) . "\n";
1943
	print $out "$char" . $indent . $text;
1944
	print $out " " x ($twidth - 2 - length($indent) - length($text));
1945
	print $out "$char\n";
1946
	print $out "$char" x ($twidth) . "\n";
1947
}
1948

    
1949
sub execute_task {
1950
	my $task = shift;
1951
	my $function = $task->{function};
1952
	my $name = $task->{prompt};
1953

    
1954
	print_headline($name);
1955

    
1956
	# Call the function with the rest of the array as parameters.
1957
	my $return = &$function(@{$task->{args}});
1958

    
1959
	if(!defined $return || $return eq '' || $return != 0) {
1960
		print $out "Something went wrong executing $name\n";
1961
	}
1962
}
1963

    
1964
# Runs through every task and does the install.
1965
sub install_all {
1966
	my @tasks = @_;
1967
	my $tasknum = 0;
1968
	foreach my $task (@tasks) {
1969
#	    if ($tasknum == 11 || $tasknum == 5) {$tasknum++; next;}; # Don't upgrade, skip guacamole
1970
		execute_task($task);
1971
		$tasknum++;
1972
	}
1973
}
1974

    
1975
sub die_handler {
1976
	print $out "$!\n";
1977
    exit 0;
1978
}
1979

    
1980
sub int_handler {
1981
	print $out "\n";
1982
	exit 1;
1983
}
1984

    
1985
sub sysexec {
1986
	my $ret = system(@_);
1987

    
1988
	my $int = $ret & 127;
1989
	$ret = $ret >> 8;
1990

    
1991
	if($int == 2) {
1992
		print $out "Exiting on CTRL-C\n";
1993
		exit 0;
1994
	}
1995

    
1996
	if(wantarray) {
1997
		return ($ret, $int)
1998
	} else {
1999
		return $ret;
2000
	}
2001
}
2002

    
2003
# Look up the ip for a given interface.
2004
sub if_ip {
2005
	my $ifname = shift or die "No interface name";
2006

    
2007
	my $req = `ip addr show $ifname 2> /dev/null | grep "inet "`;
2008
	if($? != 0) {
2009
		return undef;
2010
	}
2011
	$req =~ s/^\s+\S//;
2012
	my $ip = (split(/\//, (split(/\ /, $req))[1]))[0];
2013
	if(($ip !~ m/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)) {
2014
		return undef;
2015
	}
2016

    
2017
	return $ip;
2018
}
2019

    
2020
sub set_password {
2021
	my $user = shift;
2022
	my $passwd = shift;
2023
	my $chroot = shift;
2024

    
2025
	my $precmd;
2026
	my $msgsuffix;
2027

    
2028
	if ($chroot) {
2029
		$msgsuffix = "in $chroot";
2030
		$precmd = "chroot $chroot";
2031
	} else {
2032
		$msgsuffix = "on the running system";
2033
		$precmd = '';
2034
	}
2035

    
2036
	if ($passwd) {
2037
		print $out "Setting password for $user $msgsuffix\n";
2038

    
2039
		open(my $fh, '|-', "$precmd chpasswd") or return 0;
2040
		print $fh "$user:$passwd";
2041
		close($fh) or return 0;
2042
	} else {
2043
		print $out "Removing password for $user $msgsuffix\n";
2044

    
2045
		return !system("$precmd passwd --quiet --delete $user");
2046
	}
2047
}
2048

    
2049
# expand a template file
2050
sub expand_template {
2051
	my $srcfile = shift;
2052
	my $dstfile = shift;
2053
	my $expansions = shift;
2054
	# my ($srcfile, $dstfile, %expansions) = $_;
2055

    
2056
	if (-d $dstfile) {
2057
		$dstfile .= "/" . basename($srcfile);
2058
	}
2059

    
2060
	open(my $srcfh, "<", $srcfile) or return 0;
2061
	open(my $dstfh, ">", $dstfile) or return 0;
2062

    
2063
	while (my $line = <$srcfh>) {
2064
		while (my $key = (each %$expansions)) {
2065
			$line =~ s/\%$key\%/$expansions->{$key}/g;
2066
        }
2067

    
2068
		print $dstfh $line;
2069
	}
2070

    
2071
	return 1;
2072
}
2073

    
2074
# Enumerate and return network interfaces
2075
sub getNics {
2076
	my $externalnic = '';
2077
	my $droute = `ip route show default`;
2078
	$externalnic = $1 if ($droute =~ /default via .+ dev (.+) proto/);
2079
	my $niclist = `ifconfig | grep flags= | sed -n -e 's/: .*//p'`;
2080
	if (-e "/mnt/stabile/tftp/$piston_versions[0]") { # If a piston root exists, assume we will be providing boot services over secondary NIC even if it has no link
2081
		$niclist = `ifconfig -a | grep flags= | sed -n -e 's/: .*//p'`;
2082
	}
2083
	# my $niclist = `netstat -in`;
2084
	my @nics = ();
2085
	push @nics, $externalnic if ($externalnic);
2086
	foreach my $line (split("\n", $niclist)) {
2087
		my $nic = $1 if ($line =~ /(\S+)/);
2088
		if ($nic ne 'lo' && $nic ne $externalnic && !($nic=~/^virbr/) && !($nic=~/^docker/) && !($nic=~/^br/) && !($nic=~/^vnet/) && !($nic=~/^Name/) && !($nic=~/^Kernel/) && !($nic=~/^Iface/) && !($nic=~/(\.|\:)/)) {
2089
			push @nics, $1;
2090
		}
2091
	}
2092
	$externalnic = $nics[0] unless ($externalnic);
2093
	my $internalnic = $externalnic;
2094
	$internalnic = $nics[1] if (scalar @nics > 1);
2095
	return ($internalnic, $externalnic);
2096
}
2097

    
2098
# hardlink a file, overwriting the destination
2099
sub hardlink {
2100
	my $src = shift;
2101
	my $dst = shift;
2102

    
2103
	if (-e $dst && !unlink($dst)) {
2104
		return 0;
2105
	}
2106

    
2107
	return link($src, $dst);
2108
}
2109

    
2110
# Local Variables:
2111
# mode: perl;
2112
# indent-tabs-mode: t;
2113
# tab-width: 4;
2114
# End:
(3-3/5)