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];
203 return undef unless (seek($file, 8, SEEK_SET));
204 return undef unless (read($file, $data, 7*4) == 7*4);
206 my($magic, $len, $flags, $boff, $blen, $aoff, $alen)
207 = unpack("VVVVVVV", $data);
208 return undef if ($magic != 0x2983c8ac);
209 return undef if ($len < 7*4);
214 return undef unless (seek($file, $boff, SEEK_SET));
215 return undef unless (read($file, $bdata, $blen) == $blen);
216 $patch_start = $boff if ($boff < $patch_start);
222 return undef unless (seek($file, $aoff, SEEK_SET));
223 return undef unless (read($file, $adata, $alen) == $alen);
224 $patch_start = $aoff if ($aoff < $patch_start);
227 return ($patch_start, $bdata, $adata);
230 sub write_optsets($$@)
232 my($file, $patch_start, $bdata, $adata) = @_;
236 if (length($bdata) > 0) {
238 $boff = $patch_start;
239 return undef unless (seek($file, $boff, SEEK_SET));
240 return undef unless (print $file $bdata);
241 $patch_start += length($bdata);
244 if (length($adata) > 0) {
246 $aoff = $patch_start;
247 return undef unless (seek($file, $aoff, SEEK_SET));
248 return undef unless (print $file $adata);
249 $patch_start += length($adata);
252 my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
254 return undef unless (seek($file, 8+3*4, SEEK_SET));
255 return undef unless (print $file $hdr);
257 truncate($file, $patch_start);
261 sub delete_option($$)
263 my ($num, $block) = @_;
267 while ($x < length($block)) {
268 ($o, $l) = unpack("CC", substr($block, $x, 2));
271 substr($block, $x, $l+2) = '';
273 # Delete a null option
274 substr($block, $x, 1) = '';
275 } elsif ($o == 255) {
276 # End marker - truncate block
277 $block = substr($block, 0, $x);
280 # Skip to the next option
290 my ($num, $data, $block) = @_;
292 $block = delete_option($num, $block);
294 if (length($data) == 0) {
296 } elsif (length($data) > 255) {
297 die "$0: option $num has too much data (max 255 bytes)\n";
299 return $block . pack("CC", $num, length($data)) . $data;
305 my($pfx, $data) = @_;
308 while ($x < length($data)) {
309 ($o, $l) = unpack("CC", substr($data, $x, 2));
313 } elsif ($o == 255) {
316 my $odata = substr($data, $x+2, $l);
317 last if (length($odata) != $l); # Incomplete option
319 printf "%s%-20s %s\n", $pfx,
320 $option_names{$o} || sprintf("%d", $o),
321 show_option($o, $odata);
332 print STDERR "Usage: $0 options pxelinux.0\n";
333 print STDERR "Options:\n";
334 print STDERR "--before option value -b Add an option before DHCP data\n";
335 print STDERR "--after option value -a Add an option after DHCP data\n";
336 print STDERR "--delete option -d Delete an option\n";
337 print STDERR "--list -l List set options\n";
338 print STDERR "--dry-run -n Don't modify the target file\n";
339 print STDERR "--help -h Display this help text\n";
341 print STDERR "The following DHCP options are currently recognized:\n";
342 printf STDERR "%-23s %-3s %s\n", 'Name', 'Num', 'Value Format';
344 foreach $i (sort { $a <=> $b } keys(%option_names)) {
345 printf STDERR "%-23s %3d %s\n",
346 $option_names{$i}, $i, $option_format{$i}[0];
351 foreach $opt (keys(%option_names)) {
352 $option_rnames{$option_names{$opt}} = $opt;
364 while (defined($opt = shift(@ARGV))) {
366 if (defined($file)) {
371 } elsif ($opt eq '-b' || $opt eq '--before') {
372 $oname = shift(@ARGV);
373 $odata = shift(@ARGV);
375 if (!defined($odata)) {
380 $onum = option_number($oname);
381 if (!defined($onum)) {
382 print STDERR "$0: unknown option name: $oname\n";
387 $odata = parse_option($onum, $odata);
388 if (!defined($odata)) {
389 print STDERR "$0: unable to parse data for option $oname\n";
394 delete $after{$onum};
395 $before{$onum} = $odata;
397 } elsif ($opt eq '-a' || $opt eq '--after') {
398 $oname = shift(@ARGV);
399 $odata = shift(@ARGV);
401 if (!defined($odata)) {
406 $onum = option_number($oname);
407 if (!defined($onum)) {
408 print STDERR "$0: unknown option name: $oname\n";
413 $odata = parse_option($onum, $odata);
414 if (!defined($odata)) {
415 print STDERR "$0: unable to parse data for option $oname\n";
420 delete $before{$onum};
421 $after{$onum} = $odata;
423 } elsif ($opt eq '-d' || $opt eq '--delete') {
424 $oname = shift(@ARGV);
426 if (!defined($oname)) {
431 $onum = option_number($oname);
432 if (!defined($onum)) {
433 print STDERR "$0: unknown option name: $oname\n";
439 delete $before{$onum};
440 delete $after{$onum};
441 } elsif ($opt eq '-n' || $opt eq '--no-write' || $opt eq '--dry-run') {
443 } elsif ($opt eq '-l' || $opt eq '--list') {
445 } elsif ($opt eq '-h' || $opt eq '--help') {
448 print STDERR "Invalid option: $opt\n";
453 if (!defined($file) && !$usage) {
459 if ($err || $usage) {
463 if (!scalar(@clear)) {
464 $no_write = 1; # No modifications requested
467 $mode = $no_write ? '<' : '+<';
469 open(FILE, $mode, $file)
470 or die "$0: cannot open: $file: $!\n";
471 ($patch_start, @data) = read_optsets(\*FILE);
472 if (!defined($patch_start)) {
473 die "$0: $file: patch block not found or file corrupt\n";
476 foreach $o (@clear) {
477 $data[0] = delete_option($o, $data[0]);
478 $data[1] = delete_option($o, $data[1]);
480 foreach $o (keys(%before)) {
481 $data[0] = add_option($o, $before{$o}, $data[0]);
483 foreach $o (keys(%after)) {
484 $data[1] = add_option($o, $after{$o}, $data[1]);
488 list_options('-b ', $data[0]);
489 list_options('-a ', $data[1]);
493 if (!write_optsets(\*FILE, $patch_start, @data)) {
494 die "$0: $file: failed to write options: $!\n";