aboutsummaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-09-29 16:49:00 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-09-29 16:49:00 -0700
commitbe00bea49c01c59cb6891934b13614393043c877 (patch)
tree7d8395bd95f14e7c18b6302bffd28c731a44e89b /utils
parent15c6dc79f8d488c585057987eefddd72b114534e (diff)
downloadsyslinux.git-be00bea49c01c59cb6891934b13614393043c877.tar.gz
syslinux.git-be00bea49c01c59cb6891934b13614393043c877.tar.xz
syslinux.git-be00bea49c01c59cb6891934b13614393043c877.zip
pxelinux: support hardcoded options "before" or "after"; tool
Support hardcoded DHCP options both "before" or "after" the PXE-provided options. Add a tool to manipulate these hardcoded options. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'utils')
-rwxr-xr-xutils/pxelinux-options482
1 files changed, 482 insertions, 0 deletions
diff --git a/utils/pxelinux-options b/utils/pxelinux-options
new file mode 100755
index 00000000..0d45f26c
--- /dev/null
+++ b/utils/pxelinux-options
@@ -0,0 +1,482 @@
+#!/usr/bin/perl
+#
+# Set PXELINUX hard-coded options
+#
+
+use Socket; # For gethostbyname
+use Fcntl;
+use bytes;
+
+%option_names = (
+ 6 => 'domain-name-servers',
+ 15 => 'domain-name',
+ 54 => 'next-server',
+ 209 => 'config-file',
+ 210 => 'path-prefix',
+ 211 => 'reboottime'
+ );
+
+@fmt_oneip = (\&parse_oneip, \&show_ip);
+@fmt_multiip = (\&parse_multiip, \&show_ip);
+@fmt_string = (\&parse_string, \&show_string);
+@fmt_uint32 = (\&parse_uint32, \&show_uint32);
+
+%option_format = (
+ 6 => \@fmt_multiip,
+ 15 => \@fmt_string,
+ 54 => \@fmt_oneip,
+ 67 => \@fmt_string,
+ 209 => \@fmt_string,
+ 210 => \@fmt_string,
+ 211 => \@fmt_uint32
+ );
+
+sub parse_oneip($)
+{
+ my($s) = @_;
+ my($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($s);
+
+ return ($addrtype == AF_INET) ? $addrs[0] : undef;
+}
+
+sub parse_multiip($)
+{
+ my($l) = @_;
+ my $s;
+ my @a = ();
+ my $addr;
+ my $d = '';
+
+ foreach $s (split(/,/, $l)) {
+ my($name,$aliases,$addrtype,$length,@addrs)
+ = gethostbyname($s);
+ if ($addrtype == AF_INET) {
+ foreach $addr (@addrs) {
+ $d .= $addr;
+ }
+ }
+ }
+
+ return $d ne '' ? $d : undef;
+}
+
+sub show_ip($)
+{
+ my($l) = @_;
+
+ if (length($l) & 3) {
+ return undef;
+ } else {
+ my @h = ();
+ my $i;
+
+ for ($i = 0; $i < length($l); $i += 4) {
+ push(@h, inet_ntoa(substr($l, $i, 4)));
+ }
+
+ return join(',', @h);
+ }
+}
+
+sub parse_string($)
+{
+ return $_[0];
+}
+
+sub show_string($)
+{
+ my($s) = @_;
+ my $o, $i, $c;
+
+ $o = "\'";
+ for ($i = 0; $i < length($s); $i++) {
+ $c = substr($s, $i, 1);
+ if ($c eq "\'" || $c eq '!') {
+ $o .= "\'\\$c\'";
+ } else {
+ $o .= $c;
+ }
+ }
+ $o .= "\'";
+
+ return $o;
+}
+
+sub parse_uint32($)
+{
+ my($s) = @_;
+
+ if ($s =~ /^[0-9]+$/) {
+ return pack("N", $s);
+ } else {
+ return undef;
+ }
+}
+
+sub show_uint32($)
+{
+ my($l) = @_;
+
+ if (length($l) == 4) {
+ return unpack("N", $l);
+ } else {
+ return undef;
+ }
+}
+
+sub parse_generic($)
+{
+ my($s) = @_;
+
+ if ($s =~ /^[0-9a-f]{1,2}(:[0-9a-f]{1,2})*$/) {
+ my $h;
+ my @b = ();
+
+ foreach $h (split(/\:/, $s)) {
+ push(@b, hex $h);
+ }
+
+ return pack("C", @b);
+ } else {
+ return undef;
+ }
+}
+
+sub show_generic($)
+{
+ my($l) = @_;
+ my $i;
+ my @h;
+
+ for ($i = 0; $i < length($l); $i++) {
+ push(@h, sprintf("%02x", unpack("C", substr($l, $i, $1))));
+ }
+
+ return join(':', @h);
+}
+
+sub parse_option($$)
+{
+ my($opt, $arg) = @_;
+ my $v;
+
+ if (defined($option_format{$opt})) {
+ $v = $option_format{$opt}[0]($arg);
+ return $v if (defined($v));
+ }
+
+ return parse_generic($arg);
+}
+
+sub show_option($$)
+{
+ my($opt, $arg) = @_;
+ my $v;
+
+ if (defined($option_format{$opt})) {
+ $v = $option_format{$opt}[1]($arg);
+ return $v if (defined($v));
+ }
+
+ return show_generic($arg);
+}
+
+sub option_number($)
+{
+ my($n) = @_;
+
+ if (defined($option_rnames{$n})) {
+ return $option_rnames{$n};
+ } elsif ($n =~ /^[0-9]+$/ && $n >= 1 && $n <= 254) {
+ return $n+0;
+ } else {
+ return undef;
+ }
+}
+
+sub read_optsets($)
+{
+ my($file) = @_;
+ my $data, $bdata, $adata;
+ my $patch_start = (stat($file))[7];
+
+ return undef unless (seek($file, 8, SEEK_SET));
+ return undef unless (read($file, $data, 7*4) == 7*4);
+
+ my($magic, $len, $flags, $boff, $blen, $aoff, $alen)
+ = unpack("VVVVVVV", $data);
+ return undef if ($magic != 0x2983c8ac);
+ return undef if ($len < 7*4);
+
+ if ($blen == 0) {
+ $bdata = '';
+ } else {
+ return undef unless (seek($file, $boff, SEEK_SET));
+ return undef unless (read($file, $bdata, $blen) == $blen);
+ $patch_start = $boff if ($boff < $patch_start);
+ }
+
+ if ($alen == 0) {
+ $adata = '';
+ } else {
+ return undef unless (seek($file, $aoff, SEEK_SET));
+ return undef unless (read($file, $adata, $alen) == $alen);
+ $patch_start = $aoff if ($aoff < $patch_start);
+ }
+
+ return ($patch_start, $bdata, $adata);
+}
+
+sub write_optsets($$@)
+{
+ my($file, $patch_start, $bdata, $adata) = @_;
+ my $boff = 0;
+ my $aoff = 0;
+
+ if (length($bdata) > 0) {
+ $bdata .= "\xff";
+ $boff = $patch_start;
+ return undef unless (seek($file, $boff, SEEK_SET));
+ return undef unless (print $file $bdata);
+ $patch_start += length($bdata);
+ }
+
+ if (length($adata) > 0) {
+ $adata .= "\xff";
+ $aoff = $patch_start;
+ return undef unless (seek($file, $aoff, SEEK_SET));
+ return undef unless (print $file $adata);
+ $patch_start += length($adata);
+ }
+
+ my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
+
+ return undef unless (seek($file, 8+3*4, SEEK_SET));
+ return undef unless (print $file $hdr);
+
+ truncate($file, $patch_start);
+ return 1;
+}
+
+sub delete_option($$)
+{
+ my ($num, $block) = @_;
+ my $o, $l, $c, $x;
+
+ $x = 0;
+ while ($x < length($block)) {
+ ($o, $l) = unpack("CC", substr($block, $x, 2));
+ if ($o == $num) {
+ # Delete this option
+ substr($block, $x, $l+2) = '';
+ } elsif ($o == 0) {
+ # Delete a null option
+ substr($block, $x, 1) = '';
+ } elsif ($o == 255) {
+ # End marker - truncate block
+ $block = substr($block, 0, $x);
+ last;
+ } else {
+ # Skip to the next option
+ $x += $l+2;
+ }
+ }
+
+ return $block;
+}
+
+sub add_option($$$)
+{
+ my ($num, $data, $block) = @_;
+
+ $block = delete_option($num, $block);
+
+ if (length($data) == 0) {
+ return $block;
+ } elsif (length($data) > 255) {
+ die "$0: option $num has too much data (max 255 bytes)\n";
+ } else {
+ return $block . pack("CC", $num, length($data)) . $data;
+ }
+}
+
+sub list_options($$)
+{
+ my($pfx, $data) = @_;
+ my $x, $o, $l;
+
+ while ($x < length($data)) {
+ ($o, $l) = unpack("CC", substr($data, $x, 2));
+
+ if ($o == 0) {
+ $x++;
+ } elsif ($o == 255) {
+ last;
+ } else {
+ my $odata = substr($data, $x+2, $l);
+ last if (length($odata) != $l); # Incomplete option
+
+ printf "%s%-20s %s\n", $pfx,
+ $option_names{$o} || sprintf("%d", $o),
+ show_option($o, $odata);
+
+ $x += $l+2;
+ }
+ }
+}
+
+sub usage()
+{
+ print STDERR "FIX USAGE MESSAGE\n";
+}
+
+%option_rnames = ();
+foreach $opt (keys(%option_names)) {
+ $option_rnames{$option_names{$opt}} = $opt;
+}
+
+%before = ();
+%after = ();
+@clear = ();
+$usage = 0;
+$err = 0;
+$list = 0;
+$no_write = 0;
+undef $file;
+
+while (defined($opt = shift(@ARGV))) {
+ if ($opt !~ /^-/) {
+ if (defined($file)) {
+ $err = $usage = 1;
+ last;
+ }
+ $file = $opt;
+ } elsif ($opt eq '-b' || $opt eq '--before') {
+ $oname = shift(@ARGV);
+ $odata = shift(@ARGV);
+
+ if (!defined($odata)) {
+ $err = $usage = 1;
+ last;
+ }
+
+ $onum = option_number($oname);
+ if (!defined($onum)) {
+ print STDERR "$0: unknown option name: $oname\n";
+ $err = 1;
+ next;
+ }
+
+ $odata = parse_option($onum, $odata);
+ if (!defined($odata)) {
+ print STDERR "$0: unable to parse data for option $oname\n";
+ $err = 1;
+ next;
+ }
+
+ delete $after{$onum};
+ $before{$onum} = $odata;
+ push(@clear, $onum);
+ } elsif ($opt eq '-a' || $opt eq '--after') {
+ $oname = shift(@ARGV);
+ $odata = shift(@ARGV);
+
+ if (!defined($odata)) {
+ $err = $usage = 1;
+ last;
+ }
+
+ $onum = option_number($oname);
+ if (!defined($onum)) {
+ print STDERR "$0: unknown option name: $oname\n";
+ $err = 1;
+ next;
+ }
+
+ $odata = parse_option($onum, $odata);
+ if (!defined($odata)) {
+ print STDERR "$0: unable to parse data for option $oname\n";
+ $err = 1;
+ next;
+ }
+
+ delete $before{$onum};
+ $after{$onum} = $odata;
+ push(@clear, $onum);
+ } elsif ($opt eq '-d' || $opt eq '--delete') {
+ $oname = shift(@ARGV);
+
+ if (!defined($oname)) {
+ $err = $usage = 1;
+ last;
+ }
+
+ $onum = option_number($oname);
+ if (!defined($onum)) {
+ print STDERR "$0: unknown option name: $oname\n";
+ $err = 1;
+ next;
+ }
+
+ push(@clear, $onum);
+ delete $before{$onum};
+ delete $after{$onum};
+ } elsif ($opt eq '-n' || $opt eq '--no-write' || $opt eq '--dry-run') {
+ $no_write = 1;
+ } elsif ($opt eq '-l' || $opt eq '--list') {
+ $list = 1;
+ } elsif ($opt eq '-h' || $opt eq '--help') {
+ $usage = 1;
+ } else {
+ print STDERR "Invalid option: $opt\n";
+ $err = $usage = 1;
+ }
+}
+
+if (!defined($file) && !$usage) {
+ $err = $usage = 1;
+}
+if ($usage) {
+ usage();
+}
+if ($err || $usage) {
+ exit($err);
+}
+
+if (!scalar(@clear)) {
+ $no_write = 1; # No modifications requested
+}
+
+$mode = $no_write ? '<' : '+<';
+
+open(FILE, $mode, $file)
+ or die "$0: cannot open: $file: $!\n";
+($patch_start, @data) = read_optsets(\*FILE);
+if (!defined($patch_start)) {
+ die "$0: $file: patch block not found or file corrupt\n";
+}
+
+foreach $o (@clear) {
+ $data[0] = delete_option($o, $data[0]);
+ $data[1] = delete_option($o, $data[1]);
+}
+foreach $o (keys(%before)) {
+ $data[0] = add_option($o, $before{$o}, $data[0]);
+}
+foreach $o (keys(%after)) {
+ $data[1] = add_option($o, $after{$o}, $data[1]);
+}
+
+if ($list) {
+ list_options('-b ', $data[0]);
+ list_options('-a ', $data[1]);
+}
+
+if (!$no_write) {
+ if (!write_optsets(\*FILE, $patch_start, @data)) {
+ die "$0: $file: failed to write options: $!\n";
+ }
+}
+
+close(FILE);
+exit 0;