#!/usr/local/bin/perl # save_class.cgi # Create, update or delete a class in a section require './cfengine-lib.pl'; &ReadParse(); $conf = $in{'cfd'} ? &get_cfd_config() : &get_config(); $sec = $conf->[$in{'idx'}] if ($in{'idx'} ne ''); $cls = $sec->{'cls'}->[$in{'cidx'}] if ($in{'cidx'} ne ''); if ($in{'manualmode'}) { # Redirect back to the edit form, but in manual mode &redirect("edit_class.cgi?cfd=$in{'cfd'}&idx=$in{'idx'}&cidx=$in{'cidx'}&new=$in{'new'}&manual=1"); } elsif ($in{'delete'}) { # Just delete the class, and maybe the section too &lock_file($sec->{'file'}); if (@{$sec->{'cls'}} == 1) { &save_directive($conf, $sec, undef); } else { &save_directive($sec->{'cls'}, $cls, undef); } &flush_file_lines(); &unlock_file($sec->{'file'}); &webmin_log("delete", @{$sec->{'cls'}} == 1 ? "section" : "class", $sec->{'name'}); &redirect($in{'cfd'} ? "edit_cfd.cgi" : ""); } else { # Validate and save inputs $type = $in{'idx'} eq '' ? $in{'type'} : $sec->{'name'}; if (!$sec) { $sec = { 'name' => $type, 'type' => 'section', 'cls' => [ $cls = { 'type' => 'class' } ] }; } elsif (!$cls) { $cls = { 'type' => 'class' }; } &error_setup($text{'save_err'}); $in{'class_def'} || $in{'class'} =~ /^\S+$/ || &error($text{'save_eclass'}); $cls->{'name'} = $in{'class_def'} ? 'any' : $in{'class'}; $cls->{'implied'} = 0 if (!$in{'class_def'}); if (defined($in{'manual'})) { # Just save manually edited text $in{'manual'} =~ s/\r//g; $cls->{'text'} = $in{'manual'}; } elsif ($type eq 'links') { # Save link creation lines local @olinks = &parse_links($cls); local @links; for($i=0; defined($in{"from_$i"}); $i++) { next if (!$in{"from_$i"} && !$in{"to_$i"}); local $link = $olinks[$i]; $in{"from_$i"} =~ /^\S+$/ || &error(&text('save_elinkfrom', $i+1)); $link->{'_linkfrom'} = $in{"from_$i"}; $in{"to_$i"} =~ /^\S+$/ || &error(&text('save_elinkto', $i+1)); $link->{'_linkto'} = $in{"to_$i"}; $link->{'_linktype'} = $in{"type_$i"} ? "+>" : "->"; $link->{'_linktype'} .= "!" if ($in{"over_$i"}); push(@links, $link); } &unparse_links($cls, @links); } elsif ($type eq 'directories') { # Save directory creation lines local @odirs = &parse_directories($cls); local @dirs; for($i=0; defined($in{"dir_$i"}); $i++) { next if (!$in{"dir_$i"}); local $dir = $odirs[$i]; $in{"dir_$i"} =~ /^\S+$/ || &error(&text('save_edir', $i+1)); $dir->{'_dir'} = $in{"dir_$i"}; &sdelete($dir, 'mode'); if ($in{"mode_$i"} ne "") { $in{"mode_$i"} =~ /^[0-9]{3,4}$/ || &error(&text('save_edirmode', $i+1)); $dir->{'mode'} = $in{"mode_$i"}; } &sdelete($dir, 'owner'); if ($in{"owner_$i"} ne "") { $in{"owner_$i"} =~ /^\S+$/ || &error(&text('save_edirowner', $i+1)); $dir->{'owner'} = $in{"owner_$i"}; } &sdelete($dir, 'group'); if ($in{"group_$i"} ne "") { $in{"group_$i"} =~ /^\S+$/ || &error(&text('save_edirgroup', $i+1)); $dir->{'group'} = $in{"group_$i"}; } push(@dirs, $dir); } &unparse_directories($cls, @dirs); } elsif ($type eq "control" && !$in{'cfd'}) { # Save actionsequence and other global definitions local ($sp, $qu) = &split_str($in{'seq'}); push(@defs, { 'name' => 'actionsequence', 'values' => $sp, 'valuequotes' => $qu } ); for($i=0; defined($in{"def_$i"}); $i++) { next if (!$in{"def_$i"}); $in{"def_$i"} =~ /^\S+$/ || &error(&text('save_econtroldef', $i+1)); local ($sp, $qu) = &split_str($in{"value_$i"}); push(@defs, { 'name' => $in{"def_$i"}, 'values' => $sp, 'valuequotes' => $qu } ); } $cls->{'defs'} = \@defs; } elsif ($type eq "control" && $in{'cfd'}) { # Save cfd-specific control options $in{'run_def'} || ($in{'run'} =~ /^(\S+)/ && &has_command("$1")) || &error(&text('save_econtrolrun', "$1")); &save_define($cls->{'defs'}, "cfrunCommand", $in{'run_def'} ? undef : [ $in{'run'} ]); $in{'elapsed_def'} || $in{'elapsed'} =~ /^\d+$/ || &error($text{'save_econtrolelapsed'}); &save_define($cls->{'defs'}, "IfElapsed", $in{'elapsed_def'} ? undef : [ $in{'elapsed'} ]); $in{'max_def'} || $in{'max'} =~ /^\d+$/ || &error($text{'save_econtrolmax'}); &save_define($cls->{'defs'}, "MaxConnections", $in{'max_def'} ? undef : [ $in{'max'} ]); $in{'auto_def'} || ($in{'auto'} =~ /^(\S+)/ && &has_command("$1")) || &error(&text('save_econtrolauto', "$1")); &save_define($cls->{'defs'}, "AutoExecCommand", $in{'auto_def'} ? undef : [ $in{'auto'} ]); $in{'interval_def'} || $in{'interval'} =~ /^\d+$/ || &error($text{'save_econtrolinterval'}); &save_define($cls->{'defs'}, "AutoExecInterval", $in{'interval_def'} ? undef : [ $in{'interval'} ]); $in{'dom_def'} || $in{'dom'} =~ /^[A-Za-z0-9\.\-]+$/ || &error($text{'save_econtroldomain'}); &save_define($cls->{'defs'}, "domain", $in{'dom_def'} ? undef : [ $in{'dom'} ]); &save_define($cls->{'defs'}, "LogAllConnections", $in{'log'} == 1 ? [ "true" ] : $in{'log'} == 0 ? [ "false" ] : undef); $in{'allow_def'} || $in{'allow'} =~ /\S/ || &error($text{'save_econtrolallow'}); &save_define($cls->{'defs'}, "AllowConnectionsFrom", $in{'allow_def'} ? undef : [ split(/\s+/, $in{'allow'}) ] ); $in{'deny_def'} || $in{'deny'} =~ /\S/ || &error($text{'save_econtroldeny'}); &save_define($cls->{'defs'}, "DenyConnectionsFrom", $in{'deny_def'} ? undef : [ split(/\s+/, $in{'deny'}) ] ); $in{'skip_def'} || $in{'skip'} =~ /\S/ || &error($text{'save_econtrolskip'}); &save_define($cls->{'defs'}, "SkipVerify", $in{'skip_def'} ? undef : [ split(/\s+/, $in{'skip'}) ] ); } elsif ($type eq "admit" || $type eq "grant" || $type eq "deny") { # Save allowed or denied directories local $vl = 0; for($i=0; defined($in{"dir_$i"}); $i++) { next if (!$in{"dir_$i"}); $in{"dir_$i"} =~ /^\S+$/ || &error(&text('save_egrantdir', $i+1)); push(@values, $in{"dir_$i"}); push(@valuelines, $vl++); local @hosts = split(/\s+/, $in{"hosts_$i"}); @hosts || &error(&text('save_egranthosts', $in{"dir_$i"})); foreach $h (@hosts) { &to_ipaddress($h) || $h =~ /\*/ || $h =~ /=/ || &error(&text('save_egranthost', $h)); push(@values, $h); push(@valuelines, $vl++); } $vl++; } $cls->{'values'} = \@values; $cls->{'valuelines'} = \@valuelines; } elsif ($type eq "groups" || $type eq "classes") { # Save group definitions for($i=0,$j=0; defined($in{"name_$i"}); $i++) { next if (!$in{"name_$i"}); $in{"name_$i"} =~ /^\S+$/ || &error(&text('save_egroupname', $i+1)); local ($st, $qu) = &split_str($in{"mems_$i"}); push(@defs, { 'name' => $in{"name_$i"}, 'values' => $st, 'valuequotes' => $qu } ); $j++; } $cls->{'defs'} = \@defs; } elsif ($type eq "files") { # Save all the files lines local @ofiles = &parse_directories($cls); local @files; for($i=0; defined($d = $in{"dir_$i"}); $i++) { next if ($in{"dir_def_$i"}); local $file = $ofiles[$i]; $file->{'_dir'} = $d; $d =~ /\S/ || &error(&text('save_efilesdir', $i+1)); &sdelete($file, 'owner'); if (!$in{"owner_def_$i"}) { $in{"owner_$i"} =~ /^\S+$/ || &error(&text('save_efilesowner', $d)); $file->{'owner'} = $in{"owner_$i"}; } &sdelete($file, 'group'); if (!$in{"group_def_$i"}) { $in{"group_$i"} =~ /^\S+$/ || &error(&text('save_efilesgroup', $d)); $file->{'group'} = $in{"group_$i"}; } &sdelete($file, 'mode'); if (!$in{"mode_def_$i"}) { $in{"mode_$i"} =~ /^\S+$/ || &error(&text('save_efilesmode', $d)); $file->{'mode'} = $in{"mode_$i"}; } &sdelete($file, 'recurse'); if ($in{"rec_def_$i"} == 2) { $file->{'recurse'} = 'inf'; } elsif ($in{"rec_def_$i"} == 0) { $in{"rec_$i"} =~ /^\d+$/ || &error(&text('save_efilesrec', $d)); $file->{'recurse'} = $in{"rec_$i"}; } &sdelete($file, 'include'); if (!$in{"include_def_$i"}) { $in{"include_$i"} =~ /^\S+$/ || &error(&text('save_efilesinclude', $d)); $file->{'include'} = $in{"include_$i"}; } &sdelete($file, 'exclude'); if (!$in{"exclude_def_$i"}) { $in{"exclude_$i"} =~ /^\S+$/ || &error(&text('save_efilesexclude', $d)); $file->{'exclude'} = $in{"exclude_$i"}; } &sdelete($file, 'acl'); if (!$in{"acl_def_$i"}) { $in{"acl_$i"} =~ /^\S+$/ || &error(&text('save_efileacl', $d)); $file->{'acl'} = $in{"acl_$i"}; } &sdelete($file, 'action'); if ($in{"act_$i"}) { $file->{'action'} = $in{"act_$i"}; } push(@files, $file); } &unparse_directories($cls, @files); } elsif ($type eq "copy") { # Save copy lines local @ocopies = &parse_directories($cls); local @copies; for($i=0; defined($d = $in{"dir_$i"}); $i++) { next if ($in{"dir_def_$i"}); local $copy = $ocopies[$i]; $copy->{'_dir'} = $d; $d =~ /\S/ || &error(&text('save_ecopydir', $i+1)); &sdelete($copy, "dest"); $in{"dest_$i"} =~ /\S/ || &error(&text('save_ecopydest', $d)); $copy->{'dest'} = $in{"dest_$i"}; &sdelete($copy, "server"); if (!$in{"server_def_$i"}) { &to_ipaddress($in{"server_$i"}) || &error(&text('save_ecopyserver', $d)); $copy->{'server'} = $in{"server_$i"}; } &sdelete($copy, 'owner'); if (!$in{"owner_def_$i"}) { $in{"owner_$i"} =~ /^\S+$/ || &error(&text('save_ecopyowner', $d)); $copy->{'owner'} = $in{"owner_$i"}; } &sdelete($copy, 'group'); if (!$in{"group_def_$i"}) { $in{"group_$i"} =~ /^\S+$/ || &error(&text('save_ecopygroup', $d)); $copy->{'group'} = $in{"group_$i"}; } &sdelete($copy, 'mode'); if (!$in{"mode_def_$i"}) { $in{"mode_$i"} =~ /^\S+$/ || &error(&text('save_ecopymode', $d)); $copy->{'mode'} = $in{"mode_$i"}; } &sdelete($copy, 'recurse'); if ($in{"rec_def_$i"} == 2) { $copy->{'recurse'} = 'inf'; } elsif ($in{"rec_def_$i"} == 0) { $in{"rec_$i"} =~ /^\d+$/ || &error(&text('save_ecopyrec', $d)); $copy->{'recurse'} = $in{"rec_$i"}; } &sdelete($copy, "size"); if ($in{"size_mode_$i"} == 1) { $in{"size1_$i"} ne '' || &error(&text('save_ecopysize', $d)); $copy->{'size'} = $in{"size1_$i"}; } elsif ($in{"size_mode_$i"} == 2) { $in{"size2_$i"} ne '' || &error(&text('save_ecopysize', $d)); $copy->{'size'} = "<".$in{"size2_$i"}; } elsif ($in{"size_mode_$i"} == 3) { $in{"size3_$i"} ne '' || &error(&text('save_ecopysize', $d)); $copy->{'size'} = ">".$in{"size3_$i"}; } &sdelete($copy, "backup"); $copy->{'backup'} = 'false' if (!$in{"backup_$i"}); &sdelete($copy, "force"); $copy->{'force'} = 'true' if ($in{"force_$i"}); &sdelete($copy, "purge"); $copy->{'purge'} = 'true' if ($in{"purge_$i"}); &sdelete($copy, "action"); if ($in{"act_$i"}) { $copy->{'action'} = $in{"act_$i"}; } push(@copies, $copy); } &unparse_directories($cls, @copies); } elsif ($type eq "disable") { # Save disable lines local @odis = &parse_directories($cls); local @dis; for($i=0; defined($d = $in{"dir_$i"}); $i++) { next if ($in{"dir_def_$i"}); local $dis = $odis[$i]; $dis->{'_dir'} = $d; $d =~ /\S/ || &error(&text('save_edisfile', $i+1)); &sdelete($dis, "rotate"); if ($in{"rot_mode_$i"} == 1) { $dis->{'rotate'} = 'empty'; } elsif ($in{"rot_mode_$i"} == 2) { $in{"rot_$i"} =~ /^\d+$/ || &error(&text('save_edisrot', $d)); $dis->{'rotate'} = $in{"rot_$i"}; } &sdelete($dis, "type"); if ($in{"type_$i"}) { $dis->{'type'} = $in{"type_$i"}; } &sdelete($dis, "size"); if ($in{"size_mode_$i"} == 1) { $in{"size1_$i"} ne '' || &error(&text('save_edissize', $d)); $dis->{'size'} = $in{"size1_$i"}; } elsif ($in{"size_mode_$i"} == 2) { $in{"size2_$i"} ne '' || &error(&text('save_edissize', $d)); $dis->{'size'} = "<".$in{"size2_$i"}; } elsif ($in{"size_mode_$i"} == 3) { $in{"size3_$i"} ne '' || &error(&text('save_edissize', $d)); $dis->{'size'} = ">".$in{"size3_$i"}; } push(@dis, $dis); } &unparse_directories($cls, @dis); } elsif ($type eq "editfiles") { # Save file-editing scripts for($i=0; defined($d = $in{"edit_$i"}); $i++) { local (@values, @valuelines); next if ($in{"edit_def_$i"}); $d =~ /\S/ || &error(&text('save_eeditfile', $i+1)); push(@values, $d); push(@valuelines, 0); push(@valuequotes, undef); $in{"script_$i"} =~ s/\r//g; local @lines = split(/\n/, $in{"script_$i"}); for($j=0; $j<@lines; $j++) { local ($st, $qu) = &split_str($lines[$j]); push(@values, @$st); push(@valuequotes, @$qu); push(@valuelines, map { $j+1 } @$st); } @values > 1 || &error(&text('save_eeditscript', $d)); push(@lists, { 'values' => \@values, 'valuelines' => \@valuelines, 'valuequotes' => \@valuequotes } ); } $cls->{'lists'} = \@lists; } elsif ($type eq "ignore") { # Save list of ignored files local ($st, $qu) = &split_str($in{"ignore"}); for($i=0; $i<@$st; $i++) { push(@values, $st->[$i]); push(@valuelines, $i); push(@valuequotes, $qu->[$i]); } $cls->{'values'} = \@values; $cls->{'valuelines'} = \@valuelines; $cls->{'valuequotes'} = \@valuequotes; } elsif ($type eq "processes") { # Save managed processes list local $ostr; local @oprocs = &parse_processes($cls); local @procs; for($i=0; defined($p = $in{"proc_$i"}) || $i<@oprocs; $i++) { next if ($in{"proc_def_$i"}); local $proc = $oprocs[$i]; if ($proc->{'_options'}) { push(@procs, $proc); next; } $proc->{'_match'} = $p; $p =~ /\S/ || &error(&text('save_eproc', $i+1)); &sdelete($proc, "signal"); if ($in{"sig_$i"}) { $proc->{'signal'} = $in{"sig_$i"}; } &sdelete($proc, "action"); if ($in{"act_$i"}) { $proc->{'action'} = $in{"act_$i"}; } &sdelete($proc, "matches"); if ($in{"mat_mode_$i"} == 1) { $in{"mat1_$i"} ne '' || &error(&text('save_eprocmat', $d)); $proc->{'matches'} = $in{"mat1_$i"}; } elsif ($in{"mat_mode_$i"} == 2) { $in{"mat2_$i"} ne '' || &error(&text('save_eprocmat', $d)); $proc->{'matches'} = "<".$in{"mat2_$i"}; } elsif ($in{"mat_mode_$i"} == 3) { $in{"mat3_$i"} ne '' || &error(&text('save_eprocmat', $d)); $proc->{'matches'} = ">".$in{"mat3_$i"}; } delete($proc->{'_restart'}); if (!$in{"restart_def_$i"}) { $in{"restart_$i"} =~ /\S/ || &error(&text('save_eprocrestart', $p)); $proc->{'_restart'} = $in{"restart_$i"}; } &sdelete($proc, 'owner'); if (!$in{"owner_def_$i"}) { $in{"owner_$i"} =~ /^\S+$/ || &error(&text('save_eprocowner', $d)); $proc->{'owner'} = $in{"owner_$i"}; } &sdelete($proc, 'group'); if (!$in{"group_def_$i"}) { $in{"group_$i"} =~ /^\S+$/ || &error(&text('save_eprocgroup', $d)); $proc->{'group'} = $in{"group_$i"}; } push(@procs, $proc); } &unparse_processes($cls, @procs); } elsif ($type eq "shellcommands") { # Save commands to execute local @ocmds = &parse_directories($cls); local @cmds; for($i=0; defined($in{"cmd_$i"}); $i++) { next if (!$in{"cmd_$i"}); local $cmd = $ocmd[$i]; $in{"cmd_$i"} =~ /\S/ || &error(&text('save_ecmd', $i+1)); $cmd->{'_dir'} = $in{"cmd_$i"}; &sdelete($cmd, 'owner'); if ($in{"owner_$i"} ne "") { $in{"owner_$i"} =~ /^\S+$/ || &error(&text('save_ecmdowner', $i+1)); $cmd->{'owner'} = $in{"owner_$i"}; } &sdelete($cmd, 'group'); if ($in{"group_$i"} ne "") { $in{"group_$i"} =~ /^\S+$/ || &error(&text('save_ecmdgroup', $i+1)); $cmd->{'group'} = $in{"group_$i"}; } &sdelete($cmd, "timeout"); if ($in{"timeout_$i"} ne '') { $in{"timeout_$i"} =~ /^\d+$/ || &error(&text('save_ecmdtimeout', $i+1)); $cmd->{'timeout'} = $in{"timeout_$i"}; } push(@cmds, $cmd); } &unparse_shellcommands($cls, @cmds); } elsif ($type eq "tidy") { # Save tidied directories local @otidy = &parse_directories($cls); local @tidy; for($i=0; defined($d = $in{"dir_$i"}); $i++) { next if ($in{"dir_def_$i"}); local $tidy = $otidy[$i]; $d =~ /^\S+$/ || &error(&text('save_etidy', $i+1)); $tidy->{'_dir'} = $d; &sdelete($tidy, "pattern"); if (!$in{"pat_def_$i"}) { $in{"pat_$i"} =~ /^\S+$/ || &error(&text('save_etidypat', $d)); $tidy->{'pattern'} = $in{"pat_$i"}; } &sdelete($tidy, "size"); if ($in{"smode_$i"} == 1) { $tidy->{'size'} = 'empty'; } elsif ($in{"smode_$i"} == 2) { $in{"size_$i"} =~ /^\S+$/ || &error(&text('save_etidysize', $d)); $tidy->{'size'} = $in{"size_$i"}; } &sdelete($tidy, "age"); &sdelete($tidy, "type"); if ($in{"type_$i"}) { $tidy->{'type'} = $in{"type_$i"}; } if (!$in{"age_def_$i"}) { $in{"age_$i"} =~ /^\d+$/ || &error(&text('save_etidyage', $d)); $tidy->{'age'} = $in{"age_$i"}; } &sdelete($tidy, 'recurse'); if ($in{"rec_def_$i"} == 2) { $tidy->{'recurse'} = 'inf'; } elsif ($in{"rec_def_$i"} == 0) { $in{"rec_$i"} =~ /^\d+$/ || &error(&text('save_etidyrec', $d)); $tidy->{'recurse'} = $in{"rec_$i"}; } push(@tidy, $tidy); } &unparse_directories($cls, @tidy); } elsif ($type eq "miscmounts") { # Save mounted NFS filesystems local @omnts = &parse_miscmounts($cls); local @mnts; for($i=0; defined($d = $in{"src_$i"}); $i++) { next if (!$d); local $mnt = $omnts[$i]; $d =~ /^\S+$/ || &error(&text('save_emiscsrc', $i+1)); $mnt->{'_src'} = $d; $in{"dest_$i"} =~ /^\S+$/ || &error(&text('save_emiscdest', $d)); $mnt->{'_dest'} = $in{"dest_$i"}; &sdelete($mnt, "mode"); $in{"mode_$i"} =~ /^\S*$/ || &error(&text('save_emiscmode', $d)); $mnt->{'mode'} = $in{"mode_$i"} if ($in{"mode_$i"}); push(@mnts, $mnt); } &unparse_miscmounts($cls, @mnts); } elsif ($type eq "resolve") { # Save nameserver options $in{'ns'} =~ s/\r//g; local @ns = split(/\n/, $in{'ns'}); $in{'other'} =~ s/\r//g; local @other = split(/\n/, $in{'other'}); local $vl = 0; foreach $n (@ns) { push(@values, $n); push(@valuelines, $vl++); push(@valuequotes, ""); } foreach $n (@other) { push(@values, $n); push(@valuelines, $vl++); push(@valuequotes, '"'); } $cls->{'values'} = \@values; $cls->{'valuelines'} = \@valuelines; $cls->{'valuequotes'} = \@valuequotes; } elsif ($type eq "defaultroute") { # Save default router options $in{'route'} =~ /^\S+$/ || &error($text{'save_eroute'}); $cls->{'values'} = [ $in{'route'} ]; $cls->{'valuelines'} = 0; $cls->{'valuequotes'} = [ ]; } elsif ($type eq "required" || $type eq "disks") { # Save filesystems to check local @oreqs = &parse_directories($cls); local @reqs; for($i=0; defined($d = $in{"fs_$i"}); $i++) { next if (!$d); local $req = $oreqs[$i]; $d =~ /^\S+$/ || &error(&text('save_ereq', $i+1)); $req->{'_dir'} = $d; &sdelete($req, "freespace"); if (!$in{"free_def_$i"}) { $in{"free_$i"} =~ /^\S+$/ || &error(&text('save_ereqfree', $d)); $req->{'freespace'} = $in{"free_$i"}; } push(@reqs, $req); } &unparse_directories($cls, @reqs); } # Write to the config file if ($in{'cidx'} ne '') { # Updating an existing class &lock_file($sec->{'file'}); &save_directive($conf, $cls, $cls); &flush_file_lines(); &unlock_file($sec->{'file'}); &webmin_log("modify", "class", $sec->{'name'}); } elsif ($in{'idx'} ne '') { # Adding a class to an existing section &lock_file($sec->{'file'}); &save_directive($sec->{'cls'}, undef, $cls); &flush_file_lines(); &unlock_file($sec->{'file'}); &webmin_log("create", "class", $sec->{'name'}); } else { # Creating a new section and class &lock_file($conf->[0]->{'file'}); &save_directive($conf, undef, $sec); &flush_file_lines(); &unlock_file($conf->[0]->{'file'}); &webmin_log("create", "section", $sec->{'name'}); } &redirect($in{'cfd'} ? "edit_cfd.cgi" : ""); } # save_define(&config, name, &values|undef) sub save_define { local ($i, $old); for($i=0; $i<@{$_[0]}; $i++) { if ($_[0]->[$i]->{'name'} eq $_[1]) { $old = $_[0]->[$i]; last; } } if ($old && $_[2]) { $_[0]->[$i]->{'values'} = $_[2]; } elsif ($old) { splice(@{$_[0]}, $i, 1); } elsif ($_[2]) { push(@{$_[0]}, { 'name' => $_[1], 'values' => $_[2] } ); } } # sdelete(&conf, name) sub sdelete { local $i; for($i=length($_[1]); $i>0; $i--) { local $s = substr($_[1], 0, $i); if (defined($_[0]->{$s})) { delete($_[0]->{$s}); last; } } }