source: bootcd/isolinux/syslinux-6.03/utils/pxelinux-options

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100755
File size: 11.0 KB
Line 
1#!/usr/bin/perl
2#
3# Set PXELINUX hard-coded options
4#
5
6use Socket;                     # For gethostbyname
7use Fcntl;
8use bytes;
9
10%option_names = (
11      6 => 'domain-name-servers',
12     15 => 'domain-name',
13     54 => 'next-server',
14    209 => 'config-file',
15    210 => 'path-prefix',
16    211 => 'reboottime'
17    );
18
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);
23
24%option_format = (
25      6 => \@fmt_multiip,
26     15 => \@fmt_string,
27     54 => \@fmt_oneip,
28     67 => \@fmt_string,
29    209 => \@fmt_string,
30    210 => \@fmt_string,
31    211 => \@fmt_uint32
32    );
33
34sub parse_oneip($)
35{
36    my($s) = @_;
37    my($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($s);
38
39    return ($addrtype == AF_INET) ? $addrs[0] : undef;
40}
41
42sub parse_multiip($)
43{
44    my($l) = @_;
45    my $s;
46    my @a = ();
47    my $addr;
48    my $d = '';
49
50    foreach $s (split(/,/, $l)) {
51        my($name,$aliases,$addrtype,$length,@addrs)
52            = gethostbyname($s);
53        if ($addrtype == AF_INET) {
54            foreach $addr (@addrs) {
55                $d .= $addr;
56            }
57        }
58    }
59
60    return $d ne '' ? $d : undef;
61}
62
63sub show_ip($)
64{
65    my($l) = @_;
66
67    if (length($l) & 3) {
68        return undef;
69    } else {
70        my @h = ();
71        my $i;
72
73        for ($i = 0; $i < length($l); $i += 4) {
74            push(@h, inet_ntoa(substr($l, $i, 4)));
75        }
76
77        return join(',', @h);
78    }
79}
80
81sub parse_string($)
82{
83    return $_[0];
84}
85
86sub show_string($)
87{
88    my($s) = @_;
89    my $o, $i, $c;
90
91    $o = "\'";
92    for ($i = 0; $i < length($s); $i++) {
93        $c = substr($s, $i, 1);
94        if ($c eq "\'" || $c eq '!') {
95            $o .= "\'\\$c\'";
96        } else {
97            $o .= $c;
98        }
99    }
100    $o .= "\'";
101
102    return $o;
103}
104
105sub parse_uint32($)
106{
107    my($s) = @_;
108
109    if ($s =~ /^[0-9]+$/) {
110        return pack("N", $s);
111    } else {
112        return undef;
113    }
114}
115
116sub show_uint32($)
117{
118    my($l) = @_;
119
120    if (length($l) == 4) {
121        return unpack("N", $l);
122    } else {
123        return undef;
124    }
125}
126
127sub parse_generic($)
128{
129    my($s) = @_;
130
131    if ($s =~ /^[0-9a-f]{1,2}(:[0-9a-f]{1,2})*$/) {
132        my $h;
133        my @b = ();
134
135        foreach $h (split(/\:/, $s)) {
136            push(@b, hex $h);
137        }
138
139        return pack("C", @b);
140    } else {
141        return undef;
142    }
143}
144
145sub show_generic($)
146{
147    my($l) = @_;
148    my $i;
149    my @h;
150
151    for ($i = 0; $i < length($l); $i++) {
152        push(@h, sprintf("%02x", unpack("C", substr($l, $i, $1))));
153    }
154
155    return join(':', @h);
156}
157
158sub parse_option($$)
159{
160    my($opt, $arg) = @_;
161    my $v;
162
163    if (defined($option_format{$opt})) {
164        $v = $option_format{$opt}[1]($arg);
165        return $v if (defined($v));
166    }
167
168    return parse_generic($arg);
169}
170
171sub show_option($$)
172{
173    my($opt, $arg) = @_;
174    my $v;
175
176    if (defined($option_format{$opt})) {
177        $v = $option_format{$opt}[2]($arg);
178        return $v if (defined($v));
179    }
180
181    return show_generic($arg);
182}
183
184sub option_number($)
185{
186    my($n) = @_;
187
188    if (defined($option_rnames{$n})) {
189        return $option_rnames{$n};
190    } elsif ($n =~ /^[0-9]+$/ && $n >= 1 && $n <= 254) {
191        return $n+0;
192    } else {
193        return undef;
194    }
195}
196
197sub read_optsets($)
198{
199    my($file) = @_;
200    my $data, $bdata, $adata;
201    my $patch_start = (stat($file))[7];
202    my $hdroffset = 0;          # 0 means non-deep-embedded
203    my $bufsize = 0;
204    my $junk;
205    my %hdr;
206
207    return undef unless (seek($file, 0, SEEK_SET));
208    return undef unless (read($file, $data, 48) == 48);
209
210    my($mzmagic, $junk, $magic, $len, $flags, $boff, $blen, $aoff, $alen)
211        = unpack("va[6]VVVVVVV", $data);
212
213    if ($mzmagic == 0x5a4d) {
214        # It is an EFI file... search for the magic number
215        $hdroffset = 48;
216        my $magic = pack("VVVV", 0x2a171ead, 0x0600e65e,
217                         0x4025a4e4, 0x42388fc8);
218
219        while (1) {
220            return undef unless (read($file, $data, 16) == 16);
221            last if ($data eq $magic);
222
223            $hdroffset += 16;
224        }
225
226        return undef unless (read($file, $data, 16) == 16);
227        ($blen, $alen, $bufsize, $junk) = unpack("VVVV", $data);
228
229        $patch_start = $boff = $hdroffset + 32;
230        $aoff = $boff + $blen;
231
232        $hdr{'deep'} = 1;
233        $hdr{'bufsize'} = $bufsize;
234        $hdr{'hdroffset'} = $hdroffset;
235    } else {
236        # It is a BIOS PXE file
237
238        return undef if ($magic != 0x2983c8ac);
239        return undef if ($len < 7*4);
240
241        $hdr{'deep'} = 0;
242    }
243
244    if ($blen == 0) {
245        $bdata = '';
246    } else {
247        return undef unless (seek($file, $boff, SEEK_SET));
248        return undef unless (read($file, $bdata, $blen) == $blen);
249        $patch_start = $boff if ($boff < patch_start);
250    }
251
252    if ($alen == 0) {
253        $adata = '';
254    } else {
255        return undef unless (seek($file, $aoff, SEEK_SET));
256        return undef unless (read($file, $adata, $alen) == $alen);
257        $patch_start = $aoff if ($aoff < $patch_start);
258    }
259
260    $hdr{'patch_start'} = $patch_start;
261
262    return (\%hdr, $bdata, $adata);
263}
264
265sub write_optsets($$@)
266{
267    my($file, $hdr, $bdata, $adata) = @_;
268    my $boff = 0;
269    my $aoff = 0;
270    my $bufsize = 0;
271    my $patch_start = $hdr->{'patch_start'};
272    my $len;
273
274    $bdata .= "\xff" unless ($bdata eq '');
275    $adata .= "\xff" unless ($adata eq '');
276
277    $len = length($bdata) + length($adata);
278
279    if (defined($hdr->{'bufsize'})) {
280        return undef unless ($len <= $hdr->{'bufsize'});
281    }
282
283    return undef unless (seek($file, $patch_start, SEEK_SET));
284
285    if (length($bdata)) {
286        $boff = $patch_start;
287        return undef unless (print $file $bdata);
288        $patch_start += length($bdata);
289    }
290
291    if (length($adata)) {
292        $aoff = $patch_start;
293        return undef unless (print $file $adata);
294        $patch_start += length($adata);
295    }
296
297    if ($hdr->{'deep'}) {
298        return undef unless (print $file "\0" x ($hdr->{'bufsize'} - $len));
299        return undef unless (seek($file, $hdr->{'hdroffset'} + 16, SEEK_SET));
300        my $hdr = pack("VV", length($bdata), length($adata));
301        return undef unless (print $file $hdr);
302    } else {
303        my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
304
305        return undef unless (seek($file, 8+3*4, SEEK_SET));
306        return undef unless (print $file $hdr);
307
308        truncate($file, $patch_start);
309    }
310
311    return 1;
312}
313
314sub delete_option($$)
315{
316    my ($num, $block) = @_;
317    my $o, $l, $c, $x;
318
319    $x = 0;
320    while ($x < length($block)) {
321        ($o, $l) = unpack("CC", substr($block, $x, 2));
322        if ($o == $num) {
323            # Delete this option
324            substr($block, $x, $l+2) = '';
325        } elsif ($o == 0) {
326            # Delete a null option
327            substr($block, $x, 1) = '';
328        } elsif ($o == 255) {
329            # End marker - truncate block
330            $block = substr($block, 0, $x);
331            last;
332        } else {
333            # Skip to the next option
334            $x += $l+2;
335        }
336    }
337
338    return $block;
339}
340
341sub add_option($$$)
342{
343    my ($num, $data, $block) = @_;
344
345    $block = delete_option($num, $block);
346
347    if (length($data) == 0) {
348        return $block;
349    } elsif (length($data) > 255) {
350        die "$0: option $num has too much data (max 255 bytes)\n";
351    } else {
352        return $block . pack("CC", $num, length($data)) . $data;
353    }
354}
355
356sub list_options($$)
357{
358    my($pfx, $data) = @_;
359    my $x, $o, $l;
360
361    while ($x < length($data)) {
362        ($o, $l) = unpack("CC", substr($data, $x, 2));
363
364        if ($o == 0) {
365            $x++;
366        } elsif ($o == 255) {
367            last;
368        } else {
369            my $odata = substr($data, $x+2, $l);
370            last if (length($odata) != $l); # Incomplete option
371
372            printf "%s%-20s %s\n", $pfx,
373                $option_names{$o} || sprintf("%d", $o),
374                show_option($o, $odata);
375
376            $x += $l+2;
377        }
378    }
379}
380
381sub usage()
382{
383    my $i;
384
385    print STDERR "Usage: $0 options pxelinux.0\n";
386    print STDERR "Options:\n";
387    print STDERR "--before option value   -b   Add an option before DHCP data\n";
388    print STDERR "--after  option value   -a   Add an option after DHCP data\n";
389    print STDERR "--delete option         -d   Delete an option\n";
390    print STDERR "--list                  -l   List set options\n";
391    print STDERR "--dry-run               -n   Don't modify the target file\n";
392    print STDERR "--help                  -h   Display this help text\n";
393    print STDERR "\n";
394    print STDERR "The following DHCP options are currently recognized:\n";
395    printf STDERR "%-23s %-3s  %s\n", 'Name', 'Num', 'Value Format';
396
397    foreach $i (sort { $a <=> $b } keys(%option_names)) {
398        printf STDERR "%-23s %3d  %s\n",
399                $option_names{$i}, $i, $option_format{$i}[0];
400    }
401}
402
403%option_rnames = ();
404foreach $opt (keys(%option_names)) {
405    $option_rnames{$option_names{$opt}} = $opt;
406}
407
408%before   = ();
409%after    = ();
410@clear    = ();
411$usage    = 0;
412$err      = 0;
413$list     = 0;
414$no_write = 0;
415undef $file;
416
417while (defined($opt = shift(@ARGV))) {
418    if ($opt !~ /^-/) {
419        if (defined($file)) {
420            $err = $usage = 1;
421            last;
422        }
423        $file = $opt;
424    } elsif ($opt eq '-b' || $opt eq '--before') {
425        $oname = shift(@ARGV);
426        $odata = shift(@ARGV);
427
428        if (!defined($odata)) {
429            $err = $usage = 1;
430            last;
431        }
432
433        $onum = option_number($oname);
434        if (!defined($onum)) {
435            print STDERR "$0: unknown option name: $oname\n";
436            $err = 1;
437            next;
438        }
439
440        $odata = parse_option($onum, $odata);
441        if (!defined($odata)) {
442            print STDERR "$0: unable to parse data for option $oname\n";
443            $err = 1;
444            next;
445        }
446
447        delete $after{$onum};
448        $before{$onum} = $odata;
449        push(@clear, $onum);
450    } elsif ($opt eq '-a' || $opt eq '--after') {
451        $oname = shift(@ARGV);
452        $odata = shift(@ARGV);
453
454        if (!defined($odata)) {
455            $err = $usage = 1;
456            last;
457        }
458
459        $onum = option_number($oname);
460        if (!defined($onum)) {
461            print STDERR "$0: unknown option name: $oname\n";
462            $err = 1;
463            next;
464        }
465
466        $odata = parse_option($onum, $odata);
467        if (!defined($odata)) {
468            print STDERR "$0: unable to parse data for option $oname\n";
469            $err = 1;
470            next;
471        }
472
473        delete $before{$onum};
474        $after{$onum} = $odata;
475        push(@clear, $onum);
476    } elsif ($opt eq '-d' || $opt eq '--delete') {
477        $oname = shift(@ARGV);
478
479        if (!defined($oname)) {
480            $err = $usage = 1;
481            last;
482        }
483
484        $onum = option_number($oname);
485        if (!defined($onum)) {
486            print STDERR "$0: unknown option name: $oname\n";
487            $err = 1;
488            next;
489        }
490
491        push(@clear, $onum);
492        delete $before{$onum};
493        delete $after{$onum};
494    } elsif ($opt eq '-n' || $opt eq '--no-write' || $opt eq '--dry-run') {
495        $no_write = 1;
496    } elsif ($opt eq '-l' || $opt eq '--list') {
497        $list = 1;
498    } elsif ($opt eq '-h' || $opt eq '--help') {
499        $usage = 1;
500    } else {
501        print STDERR "Invalid option: $opt\n";
502        $err = $usage = 1;
503    }
504}
505
506if (!defined($file) && !$usage) {
507    $err = $usage = 1;
508}
509if ($usage) {
510    usage();
511}
512if ($err || $usage) {
513    exit($err);
514}
515
516if (!scalar(@clear)) {
517    $no_write = 1;              # No modifications requested
518}
519
520$mode = $no_write ? '<' : '+<';
521
522open(FILE, $mode, $file)
523    or die "$0: cannot open: $file: $!\n";
524($hdrinfo, @data) = read_optsets(\*FILE);
525if (!defined($hdrinfo)) {
526    die "$0: $file: patch block not found or file corrupt\n";
527}
528
529foreach $o (@clear) {
530    $data[0] = delete_option($o, $data[0]);
531    $data[1] = delete_option($o, $data[1]);
532}
533foreach $o (keys(%before)) {
534    $data[0] = add_option($o, $before{$o}, $data[0]);
535}
536foreach $o (keys(%after)) {
537    $data[1] = add_option($o, $after{$o}, $data[1]);
538}
539
540if ($list) {
541    list_options('-b ', $data[0]);
542    list_options('-a ', $data[1]);
543}
544
545if (!$no_write) {
546    if (!write_optsets(\*FILE, $hdrinfo, @data)) {
547        die "$0: $file: failed to write options: $!\n";
548    }
549}
550
551close(FILE);
552exit 0;
Note: See TracBrowser for help on using the repository browser.