3 # Set PXELINUX hard-coded options
6 use Socket
; # For gethostbyname
11 6 => 'domain-name-servers',
19 @fmt_oneip = ("ip-address", \
&parse_oneip
, \
&show_ip
);
20 @fmt_multiip = ("ip-address-list", \
&parse_multiip
, \
&show_ip
);
21 @fmt_string = ("string", \
&parse_string
, \
&show_string
);
22 @fmt_uint32 = ("uint32", \
&parse_uint32
, \
&show_uint32
);
37 my($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($s);
39 return ($addrtype == AF_INET
) ?
$addrs[0] : undef;
50 foreach $s (split(/,/, $l)) {
51 my($name,$aliases,$addrtype,$length,@addrs)
53 if ($addrtype == AF_INET
) {
54 foreach $addr (@addrs) {
60 return $d ne '' ?
$d : undef;
73 for ($i = 0; $i < length($l); $i += 4) {
74 push(@h, inet_ntoa
(substr($l, $i, 4)));
92 for ($i = 0; $i < length($s); $i++) {
93 $c = substr($s, $i, 1);
94 if ($c eq "\'" || $c eq '!') {
109 if ($s =~ /^[0-9]+$/) {
110 return pack("N", $s);
120 if (length($l) == 4) {
121 return unpack("N", $l);
131 if ($s =~ /^[0-9a-f]{1,2}(:[0-9a-f]{1,2})*$/) {
135 foreach $h (split(/\:/, $s)) {
139 return pack("C", @b);
151 for ($i = 0; $i < length($l); $i++) {
152 push(@h, sprintf("%02x", unpack("C", substr($l, $i, $1))));
155 return join(':', @h);
163 if (defined($option_format{$opt})) {
164 $v = $option_format{$opt}[1]($arg);
165 return $v if (defined($v));
168 return parse_generic
($arg);
176 if (defined($option_format{$opt})) {
177 $v = $option_format{$opt}[2]($arg);
178 return $v if (defined($v));
181 return show_generic
($arg);
188 if (defined($option_rnames{$n})) {
189 return $option_rnames{$n};
190 } elsif ($n =~ /^[0-9]+$/ && $n >= 1 && $n <= 254) {
200 my $data, $bdata, $adata;
201 my $patch_start = (stat($file))[7];
202 my $hdroffset = 0; # 0 means non-deep-embedded
207 return undef unless (seek($file, 0, SEEK_SET
));
208 return undef unless (read($file, $data, 48) == 48);
210 my($mzmagic, $junk, $magic, $len, $flags, $boff, $blen, $aoff, $alen)
211 = unpack("va[6]VVVVVVV", $data);
213 if ($mzmagic == 0x5a4d) {
214 # It is an EFI file... search for the magic number
216 my $magic = pack("VVVV", 0x2a171ead, 0x0600e65e,
217 0x4025a4e4, 0x42388fc8);
220 return undef unless (read($file, $data, 16) == 16);
221 last if ($data eq $magic);
226 return undef unless (read($file, $data, 16) == 16);
227 ($blen, $alen, $bufsize, $junk) = unpack("VVVV", $data);
229 printf STDERR
"EFI: offset = 0x%x, blen = %d, alen = %d, bufsize = %d\n", $hdroffset, $blen, $alen, $bufsize;
231 $patch_start = $boff = $hdroffset + 32;
232 $aoff = $boff + $blen;
235 $hdr{'bufsize'} = $bufsize;
236 $hdr{'hdroffset'} = $hdroffset;
238 # It is a BIOS PXE file
240 return undef if ($magic != 0x2983c8ac);
241 return undef if ($len < 7*4);
249 return undef unless (seek($file, $boff, SEEK_SET
));
250 return undef unless (read($file, $bdata, $blen) == $blen);
251 $patch_start = $boff if ($boff < patch_start
);
257 return undef unless (seek($file, $aoff, SEEK_SET
));
258 return undef unless (read($file, $adata, $alen) == $alen);
259 $patch_start = $aoff if ($aoff < $patch_start);
262 $hdr{'patch_start'} = $patch_start;
264 return (\
%hdr, $bdata, $adata);
267 sub write_optsets
($$$@
)
269 my($file, $hdr, $bdata, $adata) = @_;
273 my $patch_start = $hdr->{'patch_start'};
276 $bdata .= "\xff" unless ($bdata eq '');
277 $adata .= "\xff" unless ($adata eq '');
279 $len = length($bdata) + length($adata);
281 if (defined($hdr->{'bufsize'})) {
282 return undef unless ($len <= $hdr->{'bufsize'});
285 return undef unless (seek($file, $patch_start, SEEK_SET
));
287 if (length($bdata)) {
288 $boff = $patch_start;
289 return undef unless (print $file $bdata);
290 $patch_start += length($bdata);
293 if (length($adata)) {
294 $aoff = $patch_start;
295 return undef unless (print $file $adata);
296 $patch_start += length($adata);
299 if ($hdr->{'deep'}) {
300 return undef unless (print $file "\0" x
($hdr->{'bufsize'} - $len));
301 return undef unless (seek($file, $hdr->{'hdroffset'} + 16, SEEK_SET
));
302 my $hdr = pack("VV", length($bdata), length($adata));
303 return undef unless (print $file $hdr);
305 my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
307 return undef unless (seek($file, 8+3*4, SEEK_SET
));
308 return undef unless (print $file $hdr);
310 truncate($file, $patch_start);
316 sub delete_option
($$)
318 my ($num, $block) = @_;
322 while ($x < length($block)) {
323 ($o, $l) = unpack("CC", substr($block, $x, 2));
326 substr($block, $x, $l+2) = '';
328 # Delete a null option
329 substr($block, $x, 1) = '';
330 } elsif ($o == 255) {
331 # End marker - truncate block
332 $block = substr($block, 0, $x);
335 # Skip to the next option
345 my ($num, $data, $block) = @_;
347 $block = delete_option
($num, $block);
349 if (length($data) == 0) {
351 } elsif (length($data) > 255) {
352 die "$0: option $num has too much data (max 255 bytes)\n";
354 return $block . pack("CC", $num, length($data)) . $data;
360 my($pfx, $data) = @_;
363 while ($x < length($data)) {
364 ($o, $l) = unpack("CC", substr($data, $x, 2));
368 } elsif ($o == 255) {
371 my $odata = substr($data, $x+2, $l);
372 last if (length($odata) != $l); # Incomplete option
374 printf "%s%-20s %s\n", $pfx,
375 $option_names{$o} || sprintf("%d", $o),
376 show_option
($o, $odata);
387 print STDERR
"Usage: $0 options pxelinux.0\n";
388 print STDERR
"Options:\n";
389 print STDERR
"--before option value -b Add an option before DHCP data\n";
390 print STDERR
"--after option value -a Add an option after DHCP data\n";
391 print STDERR
"--delete option -d Delete an option\n";
392 print STDERR
"--list -l List set options\n";
393 print STDERR
"--dry-run -n Don't modify the target file\n";
394 print STDERR
"--help -h Display this help text\n";
396 print STDERR
"The following DHCP options are currently recognized:\n";
397 printf STDERR
"%-23s %-3s %s\n", 'Name', 'Num', 'Value Format';
399 foreach $i (sort { $a <=> $b } keys(%option_names)) {
400 printf STDERR
"%-23s %3d %s\n",
401 $option_names{$i}, $i, $option_format{$i}[0];
406 foreach $opt (keys(%option_names)) {
407 $option_rnames{$option_names{$opt}} = $opt;
419 while (defined($opt = shift(@ARGV))) {
421 if (defined($file)) {
426 } elsif ($opt eq '-b' || $opt eq '--before') {
427 $oname = shift(@ARGV);
428 $odata = shift(@ARGV);
430 if (!defined($odata)) {
435 $onum = option_number
($oname);
436 if (!defined($onum)) {
437 print STDERR
"$0: unknown option name: $oname\n";
442 $odata = parse_option
($onum, $odata);
443 if (!defined($odata)) {
444 print STDERR
"$0: unable to parse data for option $oname\n";
449 delete $after{$onum};
450 $before{$onum} = $odata;
452 } elsif ($opt eq '-a' || $opt eq '--after') {
453 $oname = shift(@ARGV);
454 $odata = shift(@ARGV);
456 if (!defined($odata)) {
461 $onum = option_number
($oname);
462 if (!defined($onum)) {
463 print STDERR
"$0: unknown option name: $oname\n";
468 $odata = parse_option
($onum, $odata);
469 if (!defined($odata)) {
470 print STDERR
"$0: unable to parse data for option $oname\n";
475 delete $before{$onum};
476 $after{$onum} = $odata;
478 } elsif ($opt eq '-d' || $opt eq '--delete') {
479 $oname = shift(@ARGV);
481 if (!defined($oname)) {
486 $onum = option_number
($oname);
487 if (!defined($onum)) {
488 print STDERR
"$0: unknown option name: $oname\n";
494 delete $before{$onum};
495 delete $after{$onum};
496 } elsif ($opt eq '-n' || $opt eq '--no-write' || $opt eq '--dry-run') {
498 } elsif ($opt eq '-l' || $opt eq '--list') {
500 } elsif ($opt eq '-h' || $opt eq '--help') {
503 print STDERR
"Invalid option: $opt\n";
508 if (!defined($file) && !$usage) {
514 if ($err || $usage) {
518 if (!scalar(@clear)) {
519 $no_write = 1; # No modifications requested
522 $mode = $no_write ?
'<' : '+<';
524 open(FILE
, $mode, $file)
525 or die "$0: cannot open: $file: $!\n";
526 ($hdrinfo, @data) = read_optsets
(\
*FILE
);
527 if (!defined($hdrinfo)) {
528 die "$0: $file: patch block not found or file corrupt\n";
531 foreach $o (@clear) {
532 $data[0] = delete_option
($o, $data[0]);
533 $data[1] = delete_option
($o, $data[1]);
535 foreach $o (keys(%before)) {
536 $data[0] = add_option
($o, $before{$o}, $data[0]);
538 foreach $o (keys(%after)) {
539 $data[1] = add_option
($o, $after{$o}, $data[1]);
543 list_options
('-b ', $data[0]);
544 list_options
('-a ', $data[1]);
548 if (!write_optsets
(\
*FILE
, $hdrinfo, @data)) {
549 die "$0: $file: failed to write options: $!\n";