package Init::Freebsd::RCConf::Line; use strict; use warnings; use Class::Std; { my %raw_line : ATTR( :get :set ); my %key : ATTR( :get :set ); my %value : ATTR( :get :set ); sub BUILD { my ( $self, $ident, $args_ref ) = @_; $raw_line{$ident} = $args_ref->{'line'} || ''; $self->parse(); } sub parse { my $self = shift; if ( $self->get_raw_line() =~ /=/) { my ( $key, $value ) = split( /=/, $self->get_raw_line(), 2 ); $self->set_key($key); $self->set_value($value); } else { $self->set_key(''); $self->set_value( $self->get_raw_line() ); } } sub print_line { my $self = shift; if ( $self->get_key() ne '' ) { return sprintf( "%s=%s", $self->get_key(), $self->get_value() ); } else { return $self->get_value(); } } } 1; #### package Init::Freebsd::RCConf; use strict; use warnings; use Carp; use Fcntl qw(:flock); use FindBin; use Init::Freebsd::RCConf::Line; use File::Copy; use Class::Std; { my %file : ATTR( :get :set ); my %file_name : ATTR( :get :set ); sub BUILD { my ( $self, $ident, $args_ref ) = @_; $file_name{$ident} = (-f '/etc/rc.conf') ? '/etc/rc.conf' : $FindBin::Bin . '/rc.conf'; $file{$ident} = []; $self->load(); } sub add_line : RESTRICTED { my $self = shift; my $line = shift; my $ident = ident($self); push @{ $file{$ident} }, $line; } sub load { my $self = shift; my $ident = ident($self); open my $read_fh, '<', $self->get_file_name() or croak('Can not find ' . $self->get_file_name() . ': ' . $!); flock($read_fh, LOCK_SH); while ( <$read_fh> ) { chomp; #print $_ . "\n"; my $line = Init::Freebsd::RCConf::Line->new( { line => $_ } ); $self->add_line($line); } close($read_fh); } sub set_value { my $self = shift; my $key = shift; my $value = shift; my $ident = ident($self); $key .= '_enable'; $value = sprintf(qq{"%s"}, $value); for my $line (@{ $file{$ident} }) { if ( $line->get_key() eq $key ) { $line->set_value($value); return; } } # If we've made it this far, it must be new line and # will be added to the end of the file my $new_line = Init::Freebsd::RCConf::Line->new(); $new_line->set_key($key); $new_line->set_value($value); $self->add_line($new_line); } sub write_file { my $self = shift; my $ident = ident($self); my $temp_file = '/tmp/temp.rc.conf'; open my $write_fh, '>', $temp_file or die 'Cannot open '. $temp_file . ': ' . $!; flock($write_fh, LOCK_EX); for my $line ( @{ $file{$ident} } ) { print {$write_fh} $line->print_line() . "\n"; } close($write_fh); File::Copy::copy($temp_file, $self->get_file_name()) or croak 'Unable to copy file ' . "$!"; unlink $temp_file if (-s $self->get_file_name() ); } } 1; #### #!perl use Test::More tests => 8; use FindBin; use Init::Freebsd::RCConf; my @methods = qw{ load add_line set_value write_file }; my $file_name = $FindBin::Bin . '/rc.conf2'; build_conf($file_name); my $conf = Init::Freebsd::RCConf->new(); $conf->set_file_name($file_name); isa_ok( $conf, 'Init::Freebsd::RCConf' ); can_ok( $conf, $_ ) for (@methods); $conf->load(); $conf->set_value( 'sshd', 'NO' ); $conf->write_file(); my $sshd = look_for( $file_name, 'sshd' ); ok( $sshd =~ /NO/, 'SSH set to "NO"' ); $conf->set_value( 'ssl-something', 'YES' ); $conf->write_file(); my $ssl = look_for( $file_name, 'ssl-something' ); ok( $ssl =~ /YES/, 'ssl-something set to YES' ); $conf->set_value( 'ssl-something', 'NO' ); $conf->write_file(); my $ssl = look_for( $file_name, 'ssl-something' ); ok( $ssl =~ /NO/, 'ssl-something set to NO' ); unlink($file_name); ############################################################################### sub build_conf { my $file_name = shift; open( my $file, '>', $file_name ) or die 'Cannot open file' . "\n"; for my $line () { print {$file} $line; } close $file; } sub look_for { my $file_name = shift; my $service_name = shift; open( my $file, '<', $file_name ) or die 'Cannot open file' . "\n"; for my $line (<$file>) { return $line if $line =~ /$service_name/; } } __DATA__ #This is a example file. sshd_enable="YES" named_enable="YES" pureftpd_enable="YES" mysql_enable="YES"