pxelinux_makeconf.in 4.96 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2014, 2019 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
use English;
use strict;
use Getopt::Std;

#
# Import an image from an external source. 
#
sub usage()
{
33
    print STDERR "Usage: pxelinux_makeconf [-A] [-a action] node\n";
34
    print STDERR "Options:\n";
35
    print STDERR " -A        - Recreate boot files for all nodes\n";
36
    print STDERR " -a action - Menu action to select, one of:\n";
37 38
    print STDERR "    diskboot, mfsboot, pxewait, pxefail, nfsboot, default\n";
    print STDERR "    'default' (set to current) is the default.\n";
39 40
    exit(-1);
}
41 42
my $optlist = "Aa:d";
my $action = "default";
43
my $debug = 0;
44
my $doall = 0;
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

#
# Configure variables
#
my $TB		= "@prefix@";

#
# Untaint the path
#
$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
# Turn off line buffering on output
#
$| = 1;

#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
    die("*** $0:\n".
	"    Must be setuid! Maybe its a development version?\n");
}

#
# Load the Testbed support stuff.
#
use lib "@prefix@/lib";
use libdb;
use Node;
use Interface;

# Locals;
my $cfile;
my $configdir = "/tftpboot/pxelinux.cfg";
my $template = "$configdir/boot.template";

# Protos
84
sub donode($$);
85 86 87 88 89 90 91 92 93 94 95 96 97

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
    $debug = 1;
}
if (defined($options{"a"})) {
98
    if ($options{"a"} =~ /^(diskboot|mfsboot|nfsboot|pxewait|pxefail|default|recovery)$/) {
99 100 101 102 103
	$action = $1;
    } else {
	usage();
    }
}
104 105 106
if (defined($options{"A"})) {
    print STDERR "Not implemented yet...\n";
    exit(2);
107

108 109
    $doall = 1;
    $action = "default";
110 111
}

112
if (!$doall && @ARGV < 1) {
113 114 115
    usage();
}

116 117 118 119
my @nodes;
if ($doall) {
    # find all the pxelinux nodes...
    ;
120
} else {
121
    @nodes = @ARGV;
122 123
}

124 125 126 127 128 129 130 131
my $rv = 0;
foreach my $nodeid (@nodes) {
    if ($nodeid =~ /^([-\w]+)$/) {
	$nodeid = $1;
    } else {
	print STDERR "$nodeid: bogus nodeid!\n";
	usage();
    }
132

133 134 135 136 137 138
    # No permissions checks right now, we assume the caller is legit.
    my $node = Node->Lookup($nodeid);
    if (!$node) {
	print STDERR "$nodeid: bogus node!\n";
	usage();
    }
139

140
    $rv += donode($node, $action);
141 142
}

143
exit ($rv);
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
sub donode($$)
{
    my ($node, $action) = @_;
    my $nodeid = $node->node_id();

    print "$nodeid: called to set action $action\n"
	if ($debug);

    if ($node->boot_method() ne "pxelinux") {
	print STDERR "$nodeid: not a pxelinux booted node, ignored\n";
	return 0;
    }

    if ($action eq "default") {
	TBPxelinuxConfig($node, \$action);
	if ($action eq "default" || $action eq "pxefail") {
	    # something went wrong
	    print STDERR "$nodeid: could not determine the correct action!?\n";
	    return 1;
164 165 166
	}
    }

167 168 169 170 171 172
    my $cnet = Interface->LookupControl($node);
    if (!$cnet || !$cnet->mac() || $cnet->mac() !~ /^(..)(..)(..)(..)(..)(..)$/) {
	print STDERR "$nodeid: bogus cnet MAC.\n";
	return 1;
    } else {
	$cfile = "$configdir/01-$1-$2-$3-$4-$5-$6";
173
    }
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

    # already exists, see if it is set correctly
    if (-e "$cfile" && open(FD, "<$cfile")) {
	while (<FD>) {
	    if (/^ONTIMEOUT\s+(\S+)/) {
		if ($1 eq $action) {
		    close(FD);
		    print "$nodeid: already set to '$action'\n"
			if ($debug);
		    return 0;
		}
		last;
	    }
	}
	close(FD);
189 190
    }

191 192 193 194
    #
    # Need to create a new version.
    # XXX racy. Maybe we should do "install -C"?
    #
195
    my $nodetype = $node->type();
196
    my $tmpfile = "$configdir/$nodeid.$PID";
197 198 199 200 201 202
    if (-e "${template}.$nodeid") {
	$template = "${template}.$nodeid";
    }
    elsif (-e "${template}.$nodetype") {
	$template = "${template}.$nodetype";
    }
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
    if (open(IFD, "<$template") && open(OFD, ">$tmpfile")) {
	while (<IFD>) {
	    s/%default%/$action/;
	    s/%nodeid%/$nodeid/;
	    print OFD;
	}
	close(IFD);
	close(OFD);
	chmod(0644, $tmpfile);

	if (!rename($tmpfile, $cfile)) {
	    unlink($tmpfile);
	    print STDERR "$nodeid: could not install new pxelinux config!\n";
	    return 1;
	}
	print "$nodeid: updated $cfile with action $action\n"
	    if ($debug);
	return 0;
    }

    print STDERR "$nodeid: could not read template or write config!\n";
    return 1;
}
226