home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / bin / pxelinux-options < prev    next >
Encoding:
Text File  |  2010-10-14  |  9.7 KB  |  500 lines

  1. #!/usr/bin/perl
  2. #
  3. # Set PXELINUX hard-coded options
  4. #
  5.  
  6. use Socket;            # For gethostbyname
  7. use Fcntl;
  8. use 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.  
  34. sub 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.  
  42. sub 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.  
  63. sub 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.  
  81. sub parse_string($)
  82. {
  83.     return $_[0];
  84. }
  85.  
  86. sub 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.  
  105. sub 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.  
  116. sub 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.  
  127. sub 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.  
  145. sub 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.  
  158. sub 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.  
  171. sub 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.  
  184. sub 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.  
  197. sub read_optsets($)
  198. {
  199.     my($file) = @_;
  200.     my $data, $bdata, $adata;
  201.     my $patch_start = (stat($file))[7];
  202.  
  203.     return undef unless (seek($file, 8, SEEK_SET));
  204.     return undef unless (read($file, $data, 7*4) == 7*4);
  205.  
  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);
  210.  
  211.     if ($blen == 0) {
  212.     $bdata = '';
  213.     } else {
  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);
  217.     }
  218.  
  219.     if ($alen == 0) {
  220.     $adata = '';
  221.     } else {
  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);
  225.     }
  226.  
  227.     return ($patch_start, $bdata, $adata);
  228. }
  229.  
  230. sub write_optsets($$@)
  231. {
  232.     my($file, $patch_start, $bdata, $adata) = @_;
  233.     my $boff = 0;
  234.     my $aoff = 0;
  235.  
  236.     if (length($bdata) > 0) {
  237.     $bdata .= "\xff";
  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);
  242.     }
  243.  
  244.     if (length($adata) > 0) {
  245.     $adata .= "\xff";
  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);
  250.     }
  251.  
  252.     my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
  253.  
  254.     return undef unless (seek($file, 8+3*4, SEEK_SET));
  255.     return undef unless (print $file $hdr);
  256.  
  257.     truncate($file, $patch_start);
  258.     return 1;
  259. }
  260.  
  261. sub delete_option($$)
  262. {
  263.     my ($num, $block) = @_;
  264.     my $o, $l, $c, $x;
  265.  
  266.     $x = 0;
  267.     while ($x < length($block)) {
  268.     ($o, $l) = unpack("CC", substr($block, $x, 2));
  269.     if ($o == $num) {
  270.         # Delete this option
  271.         substr($block, $x, $l+2) = '';
  272.     } elsif ($o == 0) {
  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);
  278.         last;
  279.     } else {
  280.         # Skip to the next option
  281.         $x += $l+2;
  282.     }
  283.     }
  284.  
  285.     return $block;
  286. }
  287.  
  288. sub add_option($$$)
  289. {
  290.     my ($num, $data, $block) = @_;
  291.  
  292.     $block = delete_option($num, $block);
  293.  
  294.     if (length($data) == 0) {
  295.     return $block;
  296.     } elsif (length($data) > 255) {
  297.     die "$0: option $num has too much data (max 255 bytes)\n";
  298.     } else {
  299.     return $block . pack("CC", $num, length($data)) . $data;
  300.     }
  301. }
  302.  
  303. sub list_options($$)
  304. {
  305.     my($pfx, $data) = @_;
  306.     my $x, $o, $l;
  307.  
  308.     while ($x < length($data)) {
  309.     ($o, $l) = unpack("CC", substr($data, $x, 2));
  310.  
  311.     if ($o == 0) {
  312.         $x++;
  313.     } elsif ($o == 255) {
  314.         last;
  315.     } else {
  316.         my $odata = substr($data, $x+2, $l);
  317.         last if (length($odata) != $l); # Incomplete option
  318.  
  319.         printf "%s%-20s %s\n", $pfx,
  320.         $option_names{$o} || sprintf("%d", $o),
  321.         show_option($o, $odata);
  322.  
  323.         $x += $l+2;
  324.     }
  325.     }
  326. }
  327.  
  328. sub usage()
  329. {
  330.     my $i;
  331.  
  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";
  340.     print STDERR "\n";
  341.     print STDERR "The following DHCP options are currently recognized:\n";
  342.     printf STDERR "%-23s %-3s  %s\n", 'Name', 'Num', 'Value Format';
  343.  
  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];
  347.     }
  348. }
  349.  
  350. %option_rnames = ();
  351. foreach $opt (keys(%option_names)) {
  352.     $option_rnames{$option_names{$opt}} = $opt;
  353. }
  354.  
  355. %before   = ();
  356. %after    = ();
  357. @clear    = ();
  358. $usage    = 0;
  359. $err      = 0;
  360. $list     = 0;
  361. $no_write = 0;
  362. undef $file;
  363.  
  364. while (defined($opt = shift(@ARGV))) {
  365.     if ($opt !~ /^-/) {
  366.     if (defined($file)) {
  367.         $err = $usage = 1;
  368.         last;
  369.     }
  370.     $file = $opt;
  371.     } elsif ($opt eq '-b' || $opt eq '--before') {
  372.     $oname = shift(@ARGV);
  373.     $odata = shift(@ARGV);
  374.  
  375.     if (!defined($odata)) {
  376.         $err = $usage = 1;
  377.         last;
  378.     }
  379.  
  380.     $onum = option_number($oname);
  381.     if (!defined($onum)) {
  382.         print STDERR "$0: unknown option name: $oname\n";
  383.         $err = 1;
  384.         next;
  385.     }
  386.  
  387.     $odata = parse_option($onum, $odata);
  388.     if (!defined($odata)) {
  389.         print STDERR "$0: unable to parse data for option $oname\n";
  390.         $err = 1;
  391.         next;
  392.     }
  393.  
  394.     delete $after{$onum};
  395.     $before{$onum} = $odata;
  396.     push(@clear, $onum);
  397.     } elsif ($opt eq '-a' || $opt eq '--after') {
  398.     $oname = shift(@ARGV);
  399.     $odata = shift(@ARGV);
  400.  
  401.     if (!defined($odata)) {
  402.         $err = $usage = 1;
  403.         last;
  404.     }
  405.  
  406.     $onum = option_number($oname);
  407.     if (!defined($onum)) {
  408.         print STDERR "$0: unknown option name: $oname\n";
  409.         $err = 1;
  410.         next;
  411.     }
  412.  
  413.     $odata = parse_option($onum, $odata);
  414.     if (!defined($odata)) {
  415.         print STDERR "$0: unable to parse data for option $oname\n";
  416.         $err = 1;
  417.         next;
  418.     }
  419.  
  420.     delete $before{$onum};
  421.     $after{$onum} = $odata;
  422.     push(@clear, $onum);
  423.     } elsif ($opt eq '-d' || $opt eq '--delete') {
  424.     $oname = shift(@ARGV);
  425.  
  426.     if (!defined($oname)) {
  427.         $err = $usage = 1;
  428.         last;
  429.     }
  430.  
  431.     $onum = option_number($oname);
  432.     if (!defined($onum)) {
  433.         print STDERR "$0: unknown option name: $oname\n";
  434.         $err = 1;
  435.         next;
  436.     }
  437.  
  438.     push(@clear, $onum);
  439.     delete $before{$onum};
  440.     delete $after{$onum};
  441.     } elsif ($opt eq '-n' || $opt eq '--no-write' || $opt eq '--dry-run') {
  442.     $no_write = 1;
  443.     } elsif ($opt eq '-l' || $opt eq '--list') {
  444.     $list = 1;
  445.     } elsif ($opt eq '-h' || $opt eq '--help') {
  446.     $usage = 1;
  447.     } else {
  448.     print STDERR "Invalid option: $opt\n";
  449.     $err = $usage = 1;
  450.     }
  451. }
  452.  
  453. if (!defined($file) && !$usage) {
  454.     $err = $usage = 1;
  455. }
  456. if ($usage) {
  457.     usage();
  458. }
  459. if ($err || $usage) {
  460.     exit($err);
  461. }
  462.  
  463. if (!scalar(@clear)) {
  464.     $no_write = 1;        # No modifications requested
  465. }
  466.  
  467. $mode = $no_write ? '<' : '+<';
  468.  
  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";
  474. }
  475.  
  476. foreach $o (@clear) {
  477.     $data[0] = delete_option($o, $data[0]);
  478.     $data[1] = delete_option($o, $data[1]);
  479. }
  480. foreach $o (keys(%before)) {
  481.     $data[0] = add_option($o, $before{$o}, $data[0]);
  482. }
  483. foreach $o (keys(%after)) {
  484.     $data[1] = add_option($o, $after{$o}, $data[1]);
  485. }
  486.  
  487. if ($list) {
  488.     list_options('-b ', $data[0]);
  489.     list_options('-a ', $data[1]);
  490. }
  491.  
  492. if (!$no_write) {
  493.     if (!write_optsets(\*FILE, $patch_start, @data)) {
  494.     die "$0: $file: failed to write options: $!\n";
  495.     }
  496. }
  497.  
  498. close(FILE);
  499. exit 0;
  500.