Viewing file: dhcpd-lib.pl (25.22 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# dhcpd-lib.pl # Functions for parsing the DHCPD config file
BEGIN { push(@INC, ".."); }; use WebminCore; &init_config();
# get_parent_config([file]) # Returns a dummy parent structure for the DHCP config sub get_parent_config { local ($file) = @_; $file ||= $config{'dhcpd_conf'}; return $get_parent_config_cache{$file} if ($get_parent_config_cache{$file}); return $get_parent_config_cache{$file} = { 'file' => $file, 'members' => &get_config($file), 'line' => -1, 'fline' => -1, 'eline' => $get_config_lines }; }
# get_config([file]) # Parses the DHCPD config file into a data structure sub get_config { local ($file) = @_; $file ||= $config{'dhcpd_conf'}; return $get_config_cache{$file} if ($get_config_cache{$file}); local @rv = &get_config_file($file, \$get_config_lines); $get_config_cache{$file} = \@rv; return $get_config_cache{$file}; }
# get_config_file(file, [&lines]) sub get_config_file { local (@tok, $i, $j, @rv, $c); $i = 0; $j = 0; local $lines = &tokenize_file($_[0], \@tok); ${$_[1]} = $lines if ($_[1]); while($i < @tok) { local $str = &parse_struct(\@tok, \$i, $j++, $_[0]); if ($str) { if ($str->{'name'} eq 'include') { # Expand the include directive local $p = $str->{'values'}->[0]; if ($p !~ /^\//) { $config{'dhcpd_conf'} =~ /^(\S+)\//; $p = "$1/$p"; } local @inc = &get_config_file($p); $j--; foreach $c (@inc) { $c->{'index'} += $j; } push(@rv, @inc); $j += scalar(@inc); } else { push(@rv, $str); } } } return @rv; }
# tokenize_file(file, &tokens) sub tokenize_file { local $lines = 0; local ($line, $cmode); open(FILE, $_[0]); while($line = <FILE>) { # strip comments $line =~ s/\r|\n//g; $line =~ s/^([^"#]*)#.*$/$1/g; $line =~ s/^([^"]*)\/\/.*$/$1/g; $line =~ s/^([^"]*)\s+#.*$/$1/g; # remove stuff after #, unless $line =~ s/^(.*".*".*)\s+#.*$/$1/g; # it is inside quotes $line =~ s/\\\\/\\134/g; # convert \\ into \134 $line =~ s/([^\\])\\"/$1\\042/g; # convert escaped quotes to \042 while(1) { if (!$cmode && $line =~ /\/\*/ && $line !~ /\".*\/\*.*\"/) { # start of a C-style comment $cmode = 1; $line =~ s/\/\*.*$//g; } elsif ($cmode) { if ($line =~ /\*\//) { # end of comment $cmode = 0; $line =~ s/^.*\*\///g; } else { $line = ""; last; } } else { last; } }
# split line into tokens while(1) { if ($line =~ /^\s*"([^"]*)"(.*)$/) { push(@{$_[1]}, [ $1, 1, $lines ]); $line = $2; } elsif ($line =~ /^\s*([{};,])(.*)$/) { push(@{$_[1]}, [ $1, 0, $lines ]); $line = $2; } elsif ($line =~ /^\s*([^{}; \t,]+)(.*)$/) { push(@{$_[1]}, [ $1, 0, $lines ]); $line = $2; } else { last; } } $lines++; } close(FILE); #print STDERR "tokenized $_[0] into $lines\n"; return $lines; }
# parse_struct(&tokens, &token_num, index, file) # A structure can either have one value, or a list of values. # Pos will end up at the start of the next structure sub parse_struct { local(%str, $i, $t, @vals, @quotes, $str, @text); local $lref = &read_file_lines($_[3]); $i = ${$_[1]}; $str{'name'} = $_[0]->[$i]->[0]; $str{'line'} = $_[0]->[$i]->[2]; if ($str{'line'} && $lref->[$str{'line'}-1] =~ /^\s*(#|\/\/)\s*(.*)/) { # Previous line is a comment, so include it in the directive $str{'line'}--; $str{'comment'} = $2; } #print STDERR "parsing at line $str{'line'} = $str{'name'}\n"; while(1) { # Add values between directive name and { or ; $t = $_[0]->[++$i]; if (($t->[0] eq "{" && $str{'name'} ne 'option') || $t->[0] eq ";") { last; } elsif (!defined($t->[0])) { ${$_[1]} = $i; return undef; } else { push(@vals, $t->[0]); push(@quotes, $t->[1]); } push(@text, $t->[1] ? "\"$t->[0]\"" : $t->[0]); } $str{'values'} = \@vals; $str{'quotes'} = \@quotes; $str{'value'} = $vals[0]; $str{'text'} = join(" ", @text); $str{'index'} = $_[2]; $str{'file'} = $_[3]; $str{'fline'} = $_[0]->[$i]->[2]; if ($t->[0] eq "{") { # contains sub-structures.. parse them local(@mems, $j); $i++; # skip { $str{'type'} = 1; $j = 0; while($_[0]->[$i]->[0] ne "}") { if (!defined($_[0]->[$i]->[0])) { ${$_[1]} = $i; return undef; } $str = &parse_struct($_[0], \$i, $j++, $_[3]); if ($str) { $str->{'parent'} = \%str; push(@mems, $str); } } $str{'members'} = \@mems; $i++; # skip trailing } } else { # only a single value.. $str{'type'} = 0; $i++; # skip trailing ; } $str{'eline'} = $_[0]->[$i-1]->[2]; # ending line is the line number the # trailing ; is on ${$_[1]} = $i; return \%str; }
# find(name, &array) sub find { local($c, @rv); foreach $c (@{$_[1]}) { if ($c->{'name'} eq $_[0]) { push(@rv, $c); } } return @rv ? wantarray ? @rv : $rv[0] : wantarray ? () : undef; }
# find_value(name, &array) sub find_value { local(@v); @v = &find($_[0], $_[1]); if (!@v) { return undef; } elsif (wantarray) { return map { $_->{'value'} } @v; } else { return $v[0]->{'value'}; } }
# choice_input(text, name, &config, [display, option]+) sub choice_input { local($rv, $v, $i, @ops); $rv = "<td><b>$_[0]</b></td> <td>"; $v = &find_value($_[1], $_[2]); for($i=3; $i<@_; $i+=2) { @ops = split(/,/, $_[$i+1]); $rv .= "<input type=radio name=$_[1] value=\"$ops[0]\" ". ($v eq $ops[0] ? "checked" : "").">$_[$i]\n"; } return $rv."</td>\n"; }
# wide_choice_input(text, name, &config, [display, option]+) sub wide_choice_input { local($rv, $v, $i, @ops); $rv = "<td><b>$_[0]</b></td> <td colspan=3>"; $v = &find_value($_[1], $_[2]); for($i=3; $i<@_; $i+=2) { @ops = split(/,/, $_[$i+1]); $rv .= "<input type=radio name=$_[1] value=\"$ops[0]\" ". ($v eq $ops[0] ? "checked" : "").">$_[$i]\n"; } return $rv."</td>\n"; }
# save_choice(name, &parent, indent) sub save_choice { local($nd); if ($in{$_[0]}) { $nd = { 'name' => $_[0], 'values' => [ $in{$_[0]} ] }; } &save_directive($_[1], $_[0], $nd ? [ $nd ] : [ ], $_[2], 1); }
# addr_match_input(text, name, &config) # A field for editing a list of addresses, ACLs and partial IP addresses sub addr_match_input { local($v, $rv, $av, @av); $v = &find($_[1], $_[2]); $rv = "<td><b>$_[0]</b></td> <td>"; $rv .= "<input type=radio name=$_[1]_def value=1 ". ($v ? "" : "checked").">Default "; $rv .= "<input type=radio name=$_[1]_def value=0 ". ($v ? "checked" : "").">Listed..<br>"; foreach $av (@{$v->{'members'}}) { push(@av, $av->{'name'}); } $rv .= "<textarea name=$_[1] rows=3 cols=15>". join("\n", @av)."</textarea></td>\n"; }
sub save_addr_match { local($addr, @vals, $dir); if ($in{"$_[0]_def"}) { &save_directive($_[1], $_[0], [ ], $_[2], 1); } else { foreach $addr (split(/\s+/, $in{$_[0]})) { push(@vals, { 'name' => $addr }); } $dir = { 'name' => $_[0], 'type' => 1, 'members' => \@vals }; &save_directive($_[1], $_[0], [ $dir ], $_[2], 1); } }
# address_input(text, name, &config, type) sub address_input { local($v, $rv, $av, @av); $v = &find($_[1], $_[2]); foreach $av (@{$v->{'members'}}) { push(@av, $av->{'name'}); } if ($_[3] == 0) { # text area $rv = "<td><b>$_[0]</b></td> <td>"; $rv .= "<textarea name=$_[1] rows=3 cols=15>". join("\n", @av)."</textarea></td>\n"; } else { $rv = "<td><b>$_[0]</b></td> <td colspan=3>"; $rv .= "<input name=$_[1] size=50 value=\"".join(' ',@av)."\"></td>\n"; } return $rv; }
sub save_address { local($addr, @vals, $dir); foreach $addr (split(/\s+/, $in{$_[0]})) { &check_ipaddress($addr) || &error("'$addr' is not a valid IP address"); push(@vals, { 'name' => $addr }); } $dir = { 'name' => $_[0], 'type' => 1, 'members' => \@vals }; &save_directive($_[1], $_[0], @vals ? [ $dir ] : [ ], $_[2], 1); }
# opt_input(text, name, &config, default, size, units) # Returns HTML for an optional text field sub opt_input { local($v, $rv); $v = &find($_[1], $_[2]); $rv = "<td><b>$_[0]</b></td> <td nowrap"; $rv .= $_[4] > 30 ? " colspan=3>\n" : ">\n"; $rv .= sprintf "<input type=radio name=$_[1]_def value=1 %s> $_[3]\n", $v ? "" : "checked"; $rv .= sprintf "<input type=radio name=$_[1]_def value=0 %s> ", $v ? "checked" : ""; $rv .= sprintf "<input name=$_[1] size=$_[4] value=\"%s\"> $_[5]</td>\n", $v ? $v->{'value'} : ""; return $rv; }
# save_opt(name, &func, &parent, [indent], [quote]) # Saves an optional text field sub save_opt { local($dir); if ($in{"$_[0]_def"}) { &save_directive($_[2], $_[0], [ ], $_[3], 1); } elsif ($_[1] && ($err = &{$_[1]}($in{$_[0]}))) { &error($err); } else { $dir = { 'name' => $_[0], 'values' => [ $in{$_[0]} ], 'quotes' => [ $_[4] ] }; &save_directive($_[2], $_[0], [ $dir ], $_[3], 1); } }
# save_directive(&parent, [name|&oldvalues], &values, indent, start, [after]) # Given a structure containing a directive name, type, values and members # add, update or remove that directive in config structure and data files. # Updating of files assumes that there is no overlap between directives - # each line in the config file must contain part or all of only one directive. sub save_directive { local(@oldv, @newv, $pm, $i, $o, $n, $lref, @nl); $pm = $_[0]->{'members'}; @oldv = ref($_[1]) ? @{$_[1]} : &find($_[1], $pm); @newv = @{$_[2]}; for($i=0; $i<@oldv || $i<@newv; $i++) { if ($i >= @oldv && $_[5]) { # a new directive is being added.. put it after some other $lref = $_[0]->{'file'} ? &read_file_lines($_[0]->{'file'}) : [ ]; @nl = &directive_lines($newv[$i], $_[3]); $nline = $_[5]->{'line'}+1; $nidx = &indexof($_[5], @$pm) + 1; splice(@$lref, $nline, 0, @nl); &renumber(&get_config(), $nline, $_[0]->{'file'}, scalar(@nl)); &renumber_index($_[0]->{'members'}, $nidx, 1); $newv[$i]->{'index'} = $nidx; $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $nline; $newv[$i]->{'eline'} = $nline + scalar(@nl); splice(@$pm, $nidx, 0, $newv[$i]); } elsif ($i >= @oldv && $_[4]) { # a new directive is being added.. put it at the start of # the parent $lref = $_[0]->{'file'} ? &read_file_lines($_[0]->{'file'}) : [ ]; @nl = &directive_lines($newv[$i], $_[3]); $nline = $_[0]->{'fline'}+1; splice(@$lref, $nline, 0, @nl); &renumber(&get_config(), $nline, $_[0]->{'file'}, scalar(@nl)); &renumber_index($_[0]->{'members'}, 0, 1); $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $nline; $newv[$i]->{'eline'} = $nline + scalar(@nl); unshift(@$pm, $newv[$i]); } elsif ($i >= @oldv) { # a new directive is being added.. put it at the end of # the parent $lref = $_[0]->{'file'} ? &read_file_lines($_[0]->{'file'}) : [ ]; @nl = &directive_lines($newv[$i], $_[3]); splice(@$lref, $_[0]->{'eline'}, 0, @nl); &renumber(&get_config(), $_[0]->{'eline'}, $_[0]->{'file'}, scalar(@nl)); $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $_[0]->{'eline'}; $newv[$i]->{'eline'} = $_[0]->{'eline'} + scalar(@nl) - 1; push(@$pm, $newv[$i]); } elsif ($i >= @newv) { # a directive was deleted $lref = $oldv[$i]->{'file'} ? &read_file_lines($oldv[$i]->{'file'}) : [ ]; $ol = $oldv[$i]->{'eline'} - $oldv[$i]->{'line'} + 1; splice(@$lref, $oldv[$i]->{'line'}, $ol); &renumber(&get_config(), $oldv[$i]->{'eline'}, $oldv[$i]->{'file'}, -$ol); &renumber_index($_[0]->{'members'}, $oldv[$i]->{'index'}, -1); splice(@$pm, &indexof($oldv[$i], @$pm), 1); } else { # updating some directive if (!defined($newv[$i]->{'comment'})) { $newv[$i]->{'comment'} = $oldv[$i]->{'comment'}; } $lref = $oldv[$i]->{'file'} ? &read_file_lines($oldv[$i]->{'file'}) : [ ]; @nl = &directive_lines($newv[$i], $_[3]); $ol = $oldv[$i]->{'eline'} - $oldv[$i]->{'line'} + 1; splice(@$lref, $oldv[$i]->{'line'}, $ol, @nl); &renumber(&get_config(), $oldv[$i]->{'eline'}, $oldv[$i]->{'file'}, scalar(@nl) - $ol); $newv[$i]->{'file'} = $_[0]->{'file'}; $newv[$i]->{'line'} = $oldv[$i]->{'line'}; $newv[$i]->{'eline'} = $oldv[$i]->{'line'} + scalar(@nl) - 1; $pm->[&indexof($oldv[$i], @$pm)] = $newv[$i]; } } }
# directive_lines(&directive, tabs) # Renders some directive into a number of lines of text sub directive_lines { local(@rv, $v, $m, $i); if ($_[0]->{'comment'}) { push(@rv, ("\t" x $_[1])."# ".$_[0]->{'comment'}); } local $first = "\t" x $_[1]; $first .= "$_[0]->{'name'}"; for($i=0; $i<@{$_[0]->{'values'}}; $i++) { $v = $_[0]->{'values'}->[$i]; if ($_[0]->{'quotes'}->[$i]) { $first .= " \"$v\""; } else { $first .= " $v"; } } push(@rv, $first); if ($_[0]->{'type'}) { # multiple values.. include them as well $rv[$#rv] .= " {"; foreach $m (@{$_[0]->{'members'}}) { push(@rv, &directive_lines($m, $_[1]+1)); } push(@rv, ("\t" x ($_[1]+1))."}"); } else { $rv[$#rv] .= ";"; } return @rv; }
# renumber(&directives, line, file, count) # Runs through the given array of directives and increases the line numbers # of all those greater than some line by the given count sub renumber { local($d); local ($list, $line, $file, $count) = @_; return if (!$count); foreach $d (@$list) { if ($d->{'file'} eq $file) { if ($d->{'line'} >= $line) { $d->{'line'} += $count; } if ($d->{'eline'} >= $line) { $d->{'eline'} += $count; } } if ($d->{'type'}) { &renumber($d->{'members'}, $line, $file, $count); } } }
# renumber_index(&directives, index, count) sub renumber_index { local($d); foreach $d (@{$_[0]}) { if ($d->{'index'} >= $_[1]) { $d->{'index'} += $_[2]; } } }
# directive_diff(&d1, &d2) # Do two directives differ? sub directive_diff { local $i; local ($d1, $d2) = @_; return 1 if ($d1->{'name'} ne $d2->{'name'}); local $l1 = @{$d1->{'values'}}; local $l2 = @{$d2->{'values'}}; return 1 if ($l1 != $l2); for($i=0; $i<$l1; $i++) { return 1 if ($d1->{'values'}->[$i] ne $d2->{'values'}->[$i]); } return 1 if ($d1->{'type'} != $d2->{'type'}); if ($d1->{'type'}) { $l1 = @{$d1->{'members'}}; $l2 = @{$d2->{'members'}}; return 1 if ($l1 != $l2); for($i=0; $i<$l1; $i++) { return 1 if (&directive_diff($d1->{'members'}->[$i], $d2->{'members'}->[$i])); } } return 0; }
# group_name(&members, &group) sub group_name { local @opts = &find("option", $_[1]->{'members'}); local ($dn) = grep { $_->{'values'}->[0] eq 'domain-name' } @opts; return $config{'group_name'} == 1 && $dn ? &text('index_gdom',$dn->{'values'}->[1]) : $config{'group_name'} == 2 && $_[1]->{'comment'} ? $_[1]->{'comment'} : $_[1]->{'values'}->[0] ? $_[1]->{'values'}->[0] : $_[0] == 0 ? $text{'index_nomemb'} : $_[0] == 1 ? $text{'index_1memb'} : $_[0] >= 2 && $_[0] <= 4 ? &text('index_234memb', $_[0]) : &text('index_memb', $_[0]);
}
# get_subnets_and_hosts() # returns the references to sorted lists of hosts and subnets sub get_subnets_and_hosts { return (\@get_subnets_cache, \@get_hosts_cache) if (@get_subnets_cache && @get_hosts_cache);
local(@subn,@host,@group,@shan, $s,$h,$g,$sn, $conf); $conf = &get_config();
# get top level hosts and groups @host = &find("host", $conf); foreach $h (&find("host", $conf)) { $h->{'order'} = $h->{'index'}; } @group = &find("group", $conf); foreach $g (@group) { foreach $h (&find("host", $g->{'members'})) { push(@host, $h); } } @subn = &find("subnet", $conf); foreach $u (@subn) { foreach $h (&find("host", $u->{'members'})) { push(@host, $h); } foreach $g (&find("group", $u->{'members'})) { push(@group, $g); foreach $h (&find("host", $g->{'members'})) { push(@host, $h); } } } @shan = &find("shared-network", $conf); foreach $s (@shan) { foreach $h (&find("host", $s->{'members'})) { push(@host, $h); } foreach $g (&find("group", $s->{'members'})) { push(@group, $g); foreach $h (&find("host", $g->{'members'})) { push(@host, $h); } } foreach $u (&find("subnet", $s->{'members'})) { push(@subn, $u); foreach $h (&find("host", $u->{'members'})) { push(@host, $h); } foreach $g (&find("group", $sn->{'members'})) { push(@group, $g); foreach $h (&find("host", $g->{'members'})) { push(@host, $h); } } } } @get_subnets_cache = sort { $a->{'order'} <=> $b->{'order'} } @subn; @get_hosts_cache = sort { $a->{'order'} <=> $b->{'order'} } @host;
return (\@get_subnets_cache, \@get_hosts_cache); }
sub get_subnets { local ($sr, $hr) = &get_subnets_and_hosts(); return @{$sr}; }
sub get_hosts { local ($sr, $hr) = &get_subnets_and_hosts(); return @{$hr}; }
sub get_host_shared_network { local ($h) = @_; local $shar; while($h) { if ($h->{'name'} eq 'shared-network') { return $h; } $h = $h->{'parent'}; } return undef; }
sub get_my_shared_network_hosts { local ($h) = @_; local $shar = &get_host_shared_network($h); local @rv; foreach my $oh (&get_hosts()) { if (&get_host_shared_network($oh) eq $shar) { push(@rv, $oh); } } return @rv; }
# hash that links objtypes shortcuts with object names %obj_names2types = qw(host hst group grp subnet sub shared-network sha);
# get_branch(objtype, [addmode]) # usefull for edit_*.cgi and save_*.cgi scripts # $objtype = one of 'hst' 'grp' 'sub' 'sha' sub get_branch { local %obj_types2names = reverse %obj_names2types; local $name = $obj_types2names{$_[0]}; local ($parnode, $nparnode, $node, $indent, $nindent); $parnode = $nparnode = &get_parent_config( $_[1] && $in{'sidx'} eq '' && $in{'uidx'} eq '' && $in{'gidx'} eq '' && $in{'parent'} eq '' ? $config{'add_file'} : undef); $indent = $nindent = 0; foreach ($in{'sidx'}, $in{'uidx'}, $in{'gidx'}) { if ($_ ne '') { $parnode = $parnode->{'members'}->[$_]; $indent++; } }
if (!($in{'delete'} && $in{'options'})) { if ($in{'assign'} > 0 && !defined($in{'parent'})) { # A quirk for not javascript-capable browser # New parent is undefined yet; we need 2nd step undef $nparnode; } else { foreach (split(/\,/, $in{'parent'})) { $nindent++; if ($_ < @{$nparnode->{'members'}}) { $nparnode = $nparnode->{'members'}->[$_]; } } } }
if (!$in{'new'}) { $node = $parnode->{'members'}->[$in{'idx'}]; } else { die "Wrong call to get_nodes: pass objtype for new object" unless $name; # Construct new node structure $node->{'name'} = $name; $node->{'type'} = 1; $node->{'members'} = [ ]; } return ($parnode, $node, $indent, $nparnode, $nindent); }
# can(permissions_string, \%access, \%config_node, smode) # this is a cached wrapper of can_noncached(...) sub can { local ($perm, $acc, $node, $smode) = @_; if (defined($can_cache) && ($can_perm_cache eq $perm) && ($can_node_cache eq $node) && ($can_smode_cache eq $smode)) { return $can_cache; } else { $can_perm_cache = $perm; $can_node_cache = $node; $can_smode_cache = $smode; return ($can_cache = &can_noncached(@_)); } }
# can_noncached(permissions_string, \%access, \%config_node, smode) # check global and per-object permissions: # # permissions_string= 'c' 'r' 'w' or any combination. # smode= 0 or undef - check only current, 1 - recursive childs check, # 2 - check parents, 3 - check parents and all childs # note: while deleting an object you must allways enforce smode=1 or 3 # because all child objects are deletes recursevly. # this maybe an optional parameter sub can_noncached { local $acl; local ($perm, $acc, $node, $smode) = @_; local @perm = split(//, $perm);
if ($node ne get_parent_config() && $node ne get_parent_config($config{'add_file'})) { foreach (@perm) { next if ($_ ne 'c') && ($_ ne 'r') && ($_ ne 'w'); return 0 unless $acc->{$_ . '_' . $obj_names2types{$node->{'name'}} }; }
# per-object permissions return 0 unless &can_node(\@perm, $acc, $node);
if (($acc->{'smode'} == 2) || ($smode == 2) || ($acc->{'smode'} == 3) || ($smode == 3)) { # check parents #$parnode=&get_parent_config(); #foreach ($in{'sidx'}, $in{'uidx'}, $in{'gidx'}) { # if ($_ ne '') { # $parnode = $parnode->{'members'}->[$_]; # return 0 unless &can_node(\@perm, $acc, $parnode); # } # } $parnode = $node->{'parent'}; while($parnode) { return 0 unless &can_node(\@perm, $acc, $parnode); $parnode = $parnode->{'parent'}; } } if (($acc->{'smode'} == 1) || ($smode == 1) || ($acc->{'smode'} == 3) || ($smode == 3)) { # check childs return 0 unless &can_subtree(\@perm, $acc, $node); } } return 1; }
# can_node(\@perm, $acc, $node) # checks object permissions for current node sub can_node { local ($rperm, $acc, $node)=@_; # per-object permissions local $otype=$obj_names2types{$node->{'name'}}; if ($acc->{'per_' . $otype . '_acls'}) { local $name = $node->{'values'}->[0]; if (!$name && $node->{'name'} eq 'group') { local @opts = &find("option", $node->{'members'}); local ($dn) = grep { $_->{'values'}->[0] eq 'domain-name' } @opts; if ($dn) { $name = $dn->{'values'}->[1]; } else { $name = $node->{'index'}; } } local $acl = $acc->{'ACL' . $otype . '_' . $name}; foreach (@{$rperm}) { next if $_ eq 'c'; # skip creation perms for per-obj acls return 0 if index($acl, $_) == -1; } } return 1; }
# can_subtree(\@perm, $acc, $node) # checks object permissions for subtree sub can_subtree { local ($rperm, $acc, $node)=@_; return 0 unless &can_node($rperm, $acc, $node); if($node->{'members'}) { # recursevly process this subtree foreach (@{$node->{'members'}}) { return 0 unless &can_subtree($rperm, $acc, $_); } } return 1; }
# save_dhcpd_acl(permissions_string, obj_type, \%access, obj_name) sub save_dhcpd_acl { $_[2]->{'ACL'.$_[1].'_'.$_[3]} = $_[0]; undef($can_cache); return &save_module_acl($_[2]); }
# drop_dhcpd_acl(obj_type, \%access, obj_name) sub drop_dhcpd_acl { delete($_[1]->{'ACL'.$_[0].'_'.$_[2]}); undef($can_cache); return &save_module_acl($_[1]); }
# find_recursive(name, &config, [parent]) # Returns a list of all config entries with some name, no matter where they # are in the heirarchy sub find_recursive { local ($c, @rv); foreach $c (@{$_[1]}) { if ($c->{'name'} eq $_[0]) { push(@rv, $c); } if ($c->{'type'}) { push(@rv, &find_recursive($_[0], $c->{'members'}, $c)); } } return @rv; }
# find_parents(&object) sub find_parents { local ($gidx, $uidx, $sidx); local $p = $_[0]->{'parent'}; while($p) { $gidx = $p->{'index'} if ($p->{'name'} eq 'group'); $uidx = $p->{'index'} if ($p->{'name'} eq 'subnet'); $sidx = $p->{'index'} if ($p->{'name'} eq 'shared-network'); $p = $p->{'parent'}; } return ($gidx, $uidx, $sidx); }
# get_dhcpd_version(&out) sub get_dhcpd_version { local $out = `$config{'dhcpd_path'} -v 2>&1`; ${$_[0]} = $out; return $config{'version'} if ($config{'version'}); return undef if ($out !~ /DHCP/ || $out =~ /V1/); return $out =~ /\sV([0-9\.]+)/ ? $1 : $out =~ /\sDHCP\s+Server\s+([0-9\.]+)/ ? $1 : $out =~ /-T/ ? 3 : 2; }
# restart_dhcpd() # Re-starts the DHCP server, and returns an error message if something fails sub restart_dhcpd { local $out; if ($config{'restart_cmd'}) { # Run the restart script $out = &backquote_logged("$config{'restart_cmd'} 2>&1"); } else { # Kill and re-run the server local $pid = &is_dhcpd_running(); $pid && &kill_logged('TERM', $pid) || return "$text{'restart_errmsg2'} $pid : $!"; if ($config{'start_cmd'}) { $out = &backquote_logged("$config{'start_cmd'} 2>&1"); } else { $out = &backquote_logged("$config{'dhcpd_path'} -cf $config{'dhcpd_conf'} -lf $config{'lease_file'} $config{'interfaces'} 2>&1"); } } if ($?) { return &parse_error_out($out); } return undef; }
# Find and add config file lines around those in an error message sub parse_error_out { local ($out) = @_; local $conftext; if ($out =~ /(\S+)\s+line\s+(\d+):/) { local ($file, $line) = ($1, $2); local $lref = &read_file_lines($file, 1); local $start = $line - 5; local $end = $line + 5; $start = 0 if ($start < 0); $end = @$lref-1 if ($end > @$lref-1); $conftext = &text('restart_conftext', $line, $file)."<br>". "<pre>".&html_escape(join("\n", @$lref[$start .. $end]))."</pre>"; } return "<pre>".&html_escape($out)."</pre>".$conftext; }
# stop_dhcpd() # Stop the running DHCP server. Returns undef on success, or an error message # on failure. sub stop_dhcpd { if ($config{'stop_cmd'}) { local $out = &backquote_logged("($config{'stop_cmd'} 2>&1)"); return $? ? "<pre>$out</pre>" : undef; } else { local $pid = &is_dhcpd_running(); if ($pid && &kill_logged('TERM', $pid)) { return undef; } else { return $text{'stop_ekill'}; } } }
# start_dhcpd() # Attempt to start the DHCP server, returning undef on success or an error # message on failure. sub start_dhcpd { if (!-r $config{'lease_file'}) { # first time.. need to create the lease file $config{'lease_file'} =~ /^(\S+)\/([^\/]+)$/; if (!-d $1) { mkdir($1, 0755); } open(LEASE, ">$config{'lease_file'}"); close(LEASE); } local $out; if ($config{'start_cmd'}) { $out = &backquote_logged("($config{'start_cmd'} 2>&1)"); } else { $out = &backquote_logged("$config{'dhcpd_path'} -cf $config{'dhcpd_conf'} -lf $config{'lease_file'} $config{'interfaces'} 2>&1"); } if ($? || $out =~ /error|failed/i) { return &parse_error_out($out); } else { return undef; } }
# search_re(value, match) sub search_re { if ($in{'match'} == 0) { return lc($_[0]) eq lc($_[1]); } elsif ($in{'match'} == 1) { return $_[0] =~ /\Q$_[1]\E/i; } else { return eval { $_[0] =~ /$_[1]/i }; } }
# get_pid_file() # Returns the DHCP server PID file sub get_pid_file { local $conf = &get_config(); local $file = &find_value("pid-file-name", $conf); return $file || $config{'pid_file'}; }
sub expand_ip_range { local ($s, $e) = @_; local @rs = split(/\./, $s); local @re = split(/\./, $e); local @rv; for(my $i=$rs[0]; $i<=$re[0]; $i++) { for(my $j=$rs[1]; $j<=$re[1]; $j++) { for(my $k=$rs[2]; $k<=$re[2]; $k++) { for(my $l=$rs[3]; $l<=$re[3]; $l++) { push(@rv, "$i.$j.$k.$l"); } } } } return @rv; }
# is_dhcpd_running() # Returns the pid if the DHCP server is running sub is_dhcpd_running { local $pidfile = &get_pid_file(); if ($pidfile) { return &check_pid_file($pidfile); } else { local ($pid) = &find_byname("dhcpd"); return $pid; } }
1;
|