421 lines
8.2 KiB
Perl
421 lines
8.2 KiB
Perl
# -*- perl -*
|
|
|
|
package Xen::Tools::Common;
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
Xen::Tools::Common - Common functions used in xen-tools' Perl scripts
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Xen::Tools::Common;
|
|
|
|
=cut
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
use Exporter 'import';
|
|
use vars qw(@EXPORT_OK @EXPORT);
|
|
|
|
use English;
|
|
use File::Which;
|
|
|
|
@EXPORT = qw(readConfigurationFile xenRunning runCommand setupAdminUsers
|
|
findXenToolstack
|
|
logprint_with_config logonly_with_config fail_with_config);
|
|
|
|
=head1 FUNCTIONS
|
|
|
|
=head2 readConfigurationFile
|
|
|
|
=begin doc
|
|
|
|
Read the specified configuration file, and update our global configuration
|
|
hash with the values found in it.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub readConfigurationFile ($$)
|
|
{
|
|
my ($file, $CONFIG) = (@_);
|
|
|
|
# Don't read the file if it doesn't exist.
|
|
return if ( !-e $file );
|
|
|
|
|
|
my $line = "";
|
|
|
|
open( FILE, "<", $file ) or
|
|
fail_with_config("Cannot read file '$file' - $!", $CONFIG);
|
|
|
|
while ( defined( $line = <FILE> ) )
|
|
{
|
|
chomp $line;
|
|
if ( $line =~ s/\\$// )
|
|
{
|
|
$line .= <FILE>;
|
|
redo unless eof(FILE);
|
|
}
|
|
|
|
# Skip lines beginning with comments
|
|
next if ( $line =~ /^([ \t]*)\#/ );
|
|
|
|
# Skip blank lines
|
|
next if ( length($line) < 1 );
|
|
|
|
# Strip trailing comments.
|
|
if ( $line =~ /([^#]*)\#(.*)/ )
|
|
{
|
|
$line = $1;
|
|
}
|
|
|
|
# Find variable settings
|
|
if ( $line =~ /([^=]+)=([^\n]+)/ )
|
|
{
|
|
my $key = $1;
|
|
my $val = $2;
|
|
|
|
# Strip leading and trailing whitespace.
|
|
$key =~ s/^\s+//;
|
|
$key =~ s/\s+$//;
|
|
$val =~ s/^\s+//;
|
|
$val =~ s/\s+$//;
|
|
|
|
# command expansion?
|
|
if ( $val =~ /(.*)`([^`]+)`(.*)/ )
|
|
{
|
|
|
|
# store
|
|
my $pre = $1;
|
|
my $cmd = $2;
|
|
my $post = $3;
|
|
|
|
# get output
|
|
my $output = `$cmd`;
|
|
chomp($output);
|
|
|
|
# build up replacement.
|
|
$val = $pre . $output . $post;
|
|
}
|
|
|
|
# Store value.
|
|
$CONFIG->{ $key } = $val;
|
|
}
|
|
}
|
|
|
|
close(FILE);
|
|
}
|
|
|
|
=head2 xenRunning
|
|
|
|
=begin doc
|
|
|
|
Test to see if the given instance is running.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub xenRunning ($$)
|
|
{
|
|
my ($hostname, $CONFIG) = (@_);
|
|
|
|
my $running = 0;
|
|
|
|
unless ($CONFIG->{'xm'}) {
|
|
warn "Couldn't determine Xen toolstack, skipping check for running DomUs.\n"
|
|
unless $ENV{AS_INSTALLED_TESTING};
|
|
return 0;
|
|
}
|
|
|
|
open( CMD, $CONFIG->{'xm'}." list $hostname 2>/dev/null |" ) or
|
|
fail_with_config("Failed to run '".$CONFIG->{'xm'}." list $hostname'", $CONFIG);
|
|
while (<CMD>)
|
|
{
|
|
my $line = $_;
|
|
$running = 1 if ( $line =~ /\Q$hostname\E/ );
|
|
}
|
|
close(CMD);
|
|
|
|
return ($running);
|
|
}
|
|
|
|
=head2 findXenToolstack
|
|
|
|
=begin doc
|
|
|
|
Find the right Xen toolstack. On Debian and derivatives there's a
|
|
script which tells you about the current toolstack.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub findXenToolstack
|
|
{
|
|
my $helper = '/usr/lib/xen-common/bin/xen-toolstack';
|
|
|
|
my $xm = which('xm');
|
|
my $xl = which('xl');
|
|
|
|
if ($xm and $xl and -x $helper) {
|
|
my $toolstack = `$helper`;
|
|
chomp($toolstack);
|
|
return $toolstack if $toolstack;
|
|
}
|
|
|
|
if ($xm and system("$xm list >/dev/null 2>/dev/null") == 0) {
|
|
return $xm;
|
|
}
|
|
|
|
if ($xl and system("$xl list >/dev/null 2>/dev/null") == 0) {
|
|
return $xl;
|
|
}
|
|
|
|
return undef;
|
|
}
|
|
|
|
=head2 runCommand
|
|
|
|
=begin doc
|
|
|
|
A utility method to run a system command. We will capture the return
|
|
value and exit if the command files.
|
|
|
|
When running verbosely we will also display any command output once
|
|
it has finished.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub runCommand ($$;$)
|
|
{
|
|
local $| = 1;
|
|
my ($cmd, $CONFIG, $fail_ok) = (@_);
|
|
|
|
#
|
|
# Set a local if we don't have one.
|
|
#
|
|
$ENV{ 'LC_ALL' } = "C" unless ( $ENV{ 'LC_ALL' } );
|
|
|
|
#
|
|
# Header.
|
|
#
|
|
if ($CONFIG->{ 'verbose' }) {
|
|
logprint_with_config("Executing : $cmd\n", $CONFIG);
|
|
}
|
|
|
|
#
|
|
# Copy stderr to stdout, so we can see it, and make sure we log it.
|
|
#
|
|
$cmd .= " 2>&1";
|
|
|
|
#
|
|
# Run it.
|
|
#
|
|
my $rcopen = open(CMD, '-|', $cmd);
|
|
if (!defined($rcopen)) {
|
|
logprint_with_config("Starting command '$cmd' failed: $!\n", $CONFIG);
|
|
unless ($fail_ok) {
|
|
logprint_with_config("Aborting\n", $CONFIG);
|
|
print "See /var/log/xen-tools/".$CONFIG->{'hostname'}.".log for details\n";
|
|
$CONFIG->{'FAIL'} = 1;
|
|
exit 127;
|
|
}
|
|
}
|
|
|
|
while (my $line = <CMD>) {
|
|
if ($CONFIG->{ 'verbose' }) {
|
|
logprint_with_config($line, $CONFIG);
|
|
} else {
|
|
logonly_with_config($line, $CONFIG);
|
|
}
|
|
}
|
|
|
|
my $rcclose = close(CMD);
|
|
|
|
if ($CONFIG->{ 'verbose' }) {
|
|
logprint_with_config("Finished : $cmd\n", $CONFIG);
|
|
}
|
|
|
|
if (!$rcclose)
|
|
{
|
|
my $exit_code = $? >> 8;
|
|
logprint_with_config("Running command '$cmd' failed with exit code $exit_code.\n", $CONFIG);
|
|
logprint_with_config("Aborting\n", $CONFIG);
|
|
print "See /var/log/xen-tools/".$CONFIG->{'hostname'}.".log for details\n";
|
|
unless ($fail_ok) {
|
|
$CONFIG->{'FAIL'} = 1;
|
|
exit 127;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
=head2 setupAdminUsers (xen-shell helper)
|
|
|
|
=begin doc
|
|
|
|
This routine is designed to ensure that any users specified with
|
|
the --admins flag are setup as administrators of the new instance.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub setupAdminUsers ($)
|
|
{
|
|
my $CONFIG = (@_);
|
|
|
|
#
|
|
# If we're not root we can't modify users.
|
|
#
|
|
return if ( $EFFECTIVE_USER_ID != 0 );
|
|
|
|
#
|
|
# If we don't have a sudoers file then we'll also ignore this.
|
|
#
|
|
return if ( !-e "/etc/sudoers" );
|
|
|
|
#
|
|
# Find the path to the xen-login-shell
|
|
#
|
|
my $shell = undef;
|
|
$shell = "/usr/bin/xen-login-shell" if ( -x "/usr/bin/xen-login-shell" );
|
|
$shell = "/usr/local/bin/xen-login-shell"
|
|
if ( -x "/usr/bin/local/xen-login-shell" );
|
|
|
|
return if ( !defined($shell) );
|
|
|
|
|
|
#
|
|
# For each user make sure they exist, and setup the
|
|
# login shell for them.
|
|
#
|
|
foreach my $user ( split( /,/, $ENV{ 'admins' } ) )
|
|
{
|
|
|
|
# Strip leading and trailing whitespace.
|
|
$user =~ s/^\s+//;
|
|
$user =~ s/\s+$//;
|
|
|
|
# Ignore root
|
|
next if ( $user =~ /^root$/i );
|
|
|
|
# Does the user exist?
|
|
if ( getpwnam($user) )
|
|
{
|
|
|
|
# Change shell.
|
|
if ($CONFIG->{ 'verbose' }) {
|
|
logprint_with_config("Changing shell for $user: $shell\n", $CONFIG);
|
|
}
|
|
system( "chsh", "-s", $shell, $user );
|
|
}
|
|
else
|
|
{
|
|
|
|
# Add a new user.
|
|
if ($CONFIG->{ 'verbose' }) {
|
|
logprint_with_config("Adding new user: $user\n", $CONFIG);
|
|
}
|
|
system( "useradd", "-s", $shell, $user );
|
|
}
|
|
|
|
#
|
|
# Add the entry to /etc/sudoers.
|
|
#
|
|
open( SUDOERS, ">>", "/etc/sudoers" ) or
|
|
warn "Failed to add user to sudoers file : $user - $!";
|
|
print SUDOERS
|
|
"$user ALL = NOPASSWD: /usr/sbin/xm, /usr/sbin/xl, /usr/bin/xen-create-image\n";
|
|
close(SUDOERS);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
=head2 fail_with_config
|
|
|
|
=begin doc
|
|
|
|
Properly set $CONFIG{FAIL} on die
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub fail_with_config ($$)
|
|
{
|
|
my ($text, $CONFIG) = (@_);
|
|
|
|
logprint_with_config($text, $CONFIG);
|
|
$CONFIG->{'FAIL'} = 1;
|
|
exit 127;
|
|
}
|
|
|
|
|
|
|
|
=head2 logonly_with_config
|
|
|
|
=begin doc
|
|
|
|
Print the given string to the logfile.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub logonly_with_config ($$)
|
|
{
|
|
my ($text, $CONFIG) = (@_);
|
|
|
|
if ( $CONFIG->{ 'hostname' } )
|
|
{
|
|
open( LOGFILE, '>>', '/var/log/xen-tools/'.$CONFIG->{'hostname'}.'.log' ) or
|
|
return;
|
|
print LOGFILE $text;
|
|
close(LOGFILE);
|
|
}
|
|
}
|
|
|
|
|
|
=head2 logprint_with_config
|
|
|
|
=begin doc
|
|
|
|
Print the given string both to our screen, and to the logfile.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub logprint_with_config ($$)
|
|
{
|
|
my ($text, $CONFIG) = (@_);
|
|
|
|
print $text;
|
|
logonly_with_config($text, $CONFIG);
|
|
}
|
|
|
|
|
|
=head1 AUTHORS
|
|
|
|
Steve Kemp, https://steve.fi/
|
|
Axel Beckert, https://axel.beckert.ch/
|
|
Dmitry Nedospasov, http://www.nedos.net/
|
|
Stéphane Jourdois
|
|
|
|
Merged from several scripts by Axel Beckert.
|
|
|
|
=cut
|
|
|
|
return 1;
|