commit adb27c97ca6881b235c352fdf9b9c723cf979564 from: Izzy Blacklock date: Mon Sep 25 23:58:39 2023 UTC Reworked how output files are generated renamed {configNow} to {gitWorkDir} in config hash $type added to module keys to allow specifying group vs user output filenames for modules now stripped of leading / write_file() and delete_file now responsible for prefixing path with {gitWorkDir} {lists}->{filenames} generated during IRCNOW::ConfigNow::load_mod() - hash of filenames => array of module objects wanting to write to file - write_config() now uses the filename hash to find output to write more cleanup needed but seems to work currently. commit - aacad1502bdc25fce80270271b31ead69c9c95b7 commit + adb27c97ca6881b235c352fdf9b9c723cf979564 blob - 75d6a2cd8392a418297e7d4e3d4c24a652381759 blob + fb082187e33ef3726cfa060c46263e39c2672aea --- bin/configNow.pl +++ bin/configNow.pl @@ -7,22 +7,23 @@ use IRCNOW::ConfigNow; use File::Basename; use File::Path qw(make_path); -my $shellname = shift || 'bnsnet'; -my $username = shift || 'izzyb'; +my $shellname = shift || 'blacklock'; +my @users = qw( izzyb nathan ashley ); my $domain = 'user.planetofnix.com'; my %config=( type=>'shell', shellname => $shellname, - username => $username, + users => \@users, gitAuthor => $shellname, gitEmail => $shellname . "@" . $domain, gitWorkDir => './configNow', - # configNow => './configNow', ipv4 => '38.87.162.191', ipv6 => '2602:fccf:1:1191::', domain => $domain, ); my $shellConfig = new IRCNOW::ConfigNow( %config ); +#use Data::Dumper; +#die Dumper($shellConfig->{lists}); if ($shellConfig->repo_ready()) { $shellConfig->write_config(); blob - b8ee5dca652b3cbb57b41bc62bc82d032d3441f9 blob + f70c6c921f3dfa9eb0326de299e1b0085ef0aff6 --- lib/IRCNOW/ConfigNow/Module/Prosody.pm +++ lib/IRCNOW/ConfigNow/Module/Prosody.pm @@ -12,6 +12,7 @@ sub new { prosody => { filename => "/etc/prosody/prosody.cfg.lua", varlist => [qw{shellname domain}], + type => 'shell', template => sub { my $shellname = shift; my $domain = shift; @@ -27,6 +28,7 @@ ssl = { dns => { filename => "/var/nsd/zones/master/$domain", varlist => [qw(shellname shellname)], + type => 'shell', template => qq{ _xmpp-client._tcp.%s 3600 IN SRV 0 5 5222 xmpp _xmpp-server._tcp.%s 3600 IN SRV 0 5 5269 xmpp blob - 78acf3dd7cdbe0e6862e5411ed3c1d4542295b4e blob + ae759ba2a3e862a95f975afa61fea0a891b938c4 --- lib/IRCNOW/ConfigNow/Module/SmtpDove.pm +++ lib/IRCNOW/ConfigNow/Module/SmtpDove.pm @@ -9,9 +9,10 @@ sub new { my $options = {@_}; my $domain = $options->{vars}->{domain} || die "{domain} is a reqired option for IRCNOW::ConfigNow::Module::SmtpDove"; return $class->SUPER::new( vars => $options->{vars}, files=>{ - nsd => { + dns => { filename => qq{/var/nsd/zones/master/$domain}, varlist => ['shellname'], + type => 'shell', template => sub { my $shellname = shift; return qq{ @@ -21,41 +22,40 @@ pop.$shellname 3600 IN CNAME pop pop3.$shellname 3600 IN CNAME pop3 smtp.$shellname 3600 IN CNAME smtp mail.$shellname 3600 IN CNAME mail - }; +}; }, }, mail_domains => { filename => qq{/etc/mail/domains}, varlist => [qw{shellname domain}], + type => 'shell', template => qq{%s.%s\n}, }, mail_mailname => { filename => qq{/etc/mail/mailname}, varlist => [qw{shellname domain}], + type => 'shell', template => qq{%s.%s\n}, }, mail_Virtual => { filename => qq{/etc/mail/virtuals}, varlist => [qw{username shellname domain}], + type => 'shelluser', template => qq{%s@%s.%s vmail\n}, }, mail_user => { filename => qq{/etc/mail/users}, varlist => [qw{username shellname domain username shellname domain}], + type => 'shelluser', template => qq{%s@%s.%s: %s@%s.%s\n}, }, mail_passwd => { filename => qq{/etc/mail/passwd}, varlist => [qw{username shellname domain password}], + type => 'shelluser', template => qq{%s@%s.%s:%s::::::userdb_quota_rule=*:storage=1G\n}, }, }); } -sub create_shell { -} -sub create_user { -} - - 1; blob - a80dc96608099a5dbf17dc77a5fd79e5c0168644 blob + 6124eb6253aa013fd2c751461cfa9120cb95fb17 --- lib/IRCNOW/ConfigNow/Module.pm +++ lib/IRCNOW/ConfigNow/Module.pm @@ -5,10 +5,20 @@ use Carp; # # Each module has a list of files it works with -# Each file has to have a filename, template, and varlist -# The template can be a sprintf string or a sub returning a string -# The varlist will be passed as params to either - +# Each file has to have a filename, template, varlist, and type +# {filename} filename with path of the system file being modified. +# it'll be prefixed with the git repo path. +# {template} can be a sprintf string or a sub returning a string +# {varlist} is an array of params to pass to sprintf or the sub() +# Its an array of param names to be taken from the {vars} +# passed from the programs %config. +# {type} is used to distinquish between group accounts or sub +# accounts of a group. Each module is expected to have +# a primary type and an optional user type. +# eg: shell accounts use 'shell', and 'shelluser' +# The 'user' part is hard coded as $type and $type . 'user'; +# +# # This can be used as a base class for more advanced or complicated # needs or by passing the params needed for a new module to new. @@ -16,8 +26,19 @@ sub new { my $class =shift; my $options = {@_}; my $self = { - vars => [], - files => {}, + vars => [], # array ref of data values for template vars. + lists => { # Hash of lists stored for each module + filenames => { # Hash of filenames + # filename => [], #list of files objects using this filename. + }, + }, + files => { #hash of files we can create + # file => { + # filename => /path/to/config/filename, + # template => sub(), or sprintf string + # varlist => [] # list of vars needed to output template + # type => string # template type - group/user + }, }; if (exists $options->{vars}) { $self->{vars}= $options->{vars}; @@ -27,53 +48,89 @@ sub new { for my $file (keys %{$options->{files}}) { # Insure we have the required parms; make a copy of them #XXX Not sure if a deep copy is needed but loop need to verify the keys. - for my $k (qw{filename varlist template}) { - croak "Required param $k missing" unless exists $options->{files}->{$file}->{$k}; + for my $k (qw{filename varlist template type}) { + croak "Required param $k missing for Module $class; file: $file" unless exists $options->{files}->{$file}->{$k}; + $self->{files}->{$file}->{$k} = $options->{files}->{$file}->{$k}; } + # build a hash of filenames manipulated + $self->{files}->{$file}->{filename} =~ s/^\/?//; # remove leading / if it exists + my $filename = $self->{files}->{$file}->{filename}; + $self->{lists}->{filenames}->{ $filename } = $self->{files}->{$file}; } return $self; } croak "Required list of files to manipulate is missing"; } +# + sub list { my $self = shift; - my @list = keys(%{$self->{files}}); + my $type = shift; + my @list; + for my $file (keys(%{$self->{files}})) { + if (defined $type and exists $self->{files}->{type}) { + next unless ($self->{files}->{$file}->{type} eq $type); + } + push @list, $file; + } return @list; } +#sub files { +# my $self = shift; +# return keys %{$self->{files}}; +#} + +sub filenames { + my $self = shift; + my @filenames; + for my $file (keys %{$self->{files}} ) { + push @filenames, $self->{files}->{$file}->{filename}; + } + return @filenames; +} + sub filename { my $self = shift; my $file = shift; return $self->{files}->{$file}->{filename}; } + +# returns output for filename sub output { my $self = shift; - my $file = shift || carp "File name required"; + my $filename = shift || carp "Filename Required"; + my $type = shift; my @params; - # Specified file doesn't exist in this module - return unless ( - exists $self->{files}->{$file} and - # Shouldn't have $file without {$file}->{template} - #XXX should track down where it's being defined into existance - exists $self->{files}->{$file}->{template} - ); - my $mod = $self->{files}->{$file}; - for my $param ( @{$mod->{varlist}}) { - # Don't have needed param so return undef - unless (defined $self->{vars}->{$param}) { - return undef; + # # Specified file doesn't exist in this module + # return unless ( + # exists $self->{files}->{$file} and + # # Shouldn't have $file without {$file}->{template} + # #XXX should track down where it's being defined into existance + # exists $self->{files}->{$file}->{template} + # ); + my $mod = $self->{lists}->{filenames}->{$filename}; + # If we have a type value then limit output to type specified. + # Default to outputting all if not defined. + #warn "Module::output() Filename: $filename \$type=$type - \$mod->{type} = ".$mod->{type}; + if (not defined $type or ($type eq $mod->{type})) { + for my $param ( @{$mod->{varlist}}) { + # Don't have needed param so return undef + unless (exists $self->{vars}->{$param}) { + return undef; + } + push @params, $self->{vars}->{$param}; } - push @params, $self->{vars}->{$param}; + if (ref $mod->{template} eq 'CODE') { + return $mod->{template}->(@params); + } + return sprintf($mod->{template}, @params); } - if (ref $mod->{template} eq 'CODE') { - return $mod->{template}->(@params); - } - - return sprintf($mod->{template}, @params); + return undef; #nothing to output } blob - 6c24bf169e69c41dde01da8fd89c8663cd5a2aee blob + c3976502956c91a82a41b22ea25efc9f8d448282 --- lib/IRCNOW/ConfigNow.pm +++ lib/IRCNOW/ConfigNow.pm @@ -20,9 +20,18 @@ sub new { # set sane defaults. my $self = { vars =>$options, # options could be hash template values + lists =>{ + # users =>[], + filenames =>{}, # Hash of filenames containing array of module objects + modules =>[], + }, modules =>{}, # ConfigNow Modules repo =>undef, # }; + my $vars=$self->{vars}; + # Verify we have critcal + $vars->{gitWorkDir} = './configNow' unless exists $vars->{gitWorkDir}; + bless $self, $class; # Load any modules passed in if (exists $options->{modules}) { @@ -38,63 +47,70 @@ sub new { return $self; } - -sub filename { +sub filenames { my $self = shift; - my $file = shift; - my $root = $self->{vars}->{configNow} || './configNow'; - for my $mod (keys %{$self->{modules}}) { - my $filename = $self->{modules}->{$mod}->filename($file); - return $root . $filename if defined $filename; - } + return keys %{$self->{lists}->{filenames}}; } sub list { my $self = shift; my $module = shift; + my $type = shift; my @list; - + if (defined $module and exists $self->{modules}->{$module}) { - @list = $self->{modules}->{$module}->list(); - + @list = $self->{modules}->{$module}->list($type); } else { @list = keys %{$self->{modules}}; } return @list; } -sub output { - my $self = shift; - my $file = shift || carp "!!!!!!!!!!! undef for file:"; - my $output = ""; - for my $mod (keys %{$self->{modules}}) { - $output .= $self->{modules}->{$mod}->output($file) || ""; - } +#sub output { +# my $self = shift; +# my $filename = shift || die "!!!!!!!!!!! undef for filename:"; +# my $output = ""; +# # call each modules output() +# for my $filename +# for my $mod (keys %{$self->{modules}}) { +# next unless exists $self->{modules}->{$mod}->{$file}; +# $output .= $self->{modules}->{$mod}->output($file) || ""; +# } +# return $output; +#} - return $output; -} - sub mod_load { my $self=shift; my $modName=shift; my $module=shift; + my $obj; if (ref $module and $module.isa('IRCNOW::ConfigNow::Module')) { # Already have an object so attach it. $self->{modules}->{$modName}=$module; + $obj = $module; } else { # Lets assume they passed us a full module name. if (not defined eval "require $module") { + warn $module . " - " . $@; # Failed so try prepending IRCNOW::ConfigNow:: $module = "IRCNOW::ConfigNow::$module"; if (not defined eval "require $module") { croak "Failed to load $module"; } } + # create module object and add to hash + $obj = $module->new(vars => $self->{vars}) || croak 'Failed to load Module: $module'; + $self->{modules}->{$modName}=$obj; } - # create module object and add to hash - my $obj = $module->new(vars => $self->{vars}) || croak 'Failed to load Module: $module'; - $self->{modules}->{$modName}=$obj; + # add to loaded modules list. + push @{$self->{lists}->{modules}}, $modName; + # Build hash of filenames pointing to array of module objects + my $filenameList=$self->{lists}->{filenames}; + for my $filename ($obj->filenames()) { + $filenameList->{$filename} = [] unless exists $filenameList->{$filename}; + push @{$filenameList->{$filename}}, $obj; + } return $obj; } @@ -107,26 +123,79 @@ sub vars { } } +sub write_file { + my $self = shift; + my $filename = shift; + my $workDir=$self->{vars}->{gitWorkDir}; + $filename = "$workDir/$filename"; + my $output = shift; + my $path = dirname($filename); +#print "Making Directory: $path\n"; + make_path($path); +print "Writing: $filename\n"; + #XXX add proper error handling + open my $FH, ">>$filename"; + print $FH $output; + close $FH; +} +# Delete specified file +sub delete_file { + my $self = shift; + my $filename = shift; + my $workDir=$self->{vars}->{gitWorkDir}; + $filename = "$workDir/$filename"; +print "Deleting $filename\n"; + unlink $filename; + # unlink $filename; + #unlink $self->{modules}->{$module}->filename($file); +} + sub write_config { - my $self=shift; + my $self = shift; + my $type = $self->{vars}->{type}; + # abort of the repo isn't ready return 0 unless $self->repo_ready(); - for my $module ($self->list()) { - for my $file ($self->list($module)) { - my $filename = $self->filename($file); - my $path = dirname($filename); - print "Making Directory: $path\n"; - make_path($path); - print "Writing: $filename\n"; - #XXX add proper error handling - open my $FH, ">>$filename"; - print $FH $self->output($file); - close $FH; + # get the output for each module for this filename. + for my $filename ($self->filenames()) { + #purge existing files before generation. + $self->delete_file($filename); + my $output=""; + # Output for main type. + for my $obj (@{$self->{lists}->{filenames}->{$filename}}) { + my $out = $obj->output($filename,$type); + $output .= $out if defined $out; + # Output for $type . "users" if we have a user list + if (exists $self->{vars}->{users}) { + for my $user (@{$self->{vars}->{users}}) { + # set the username var to this user. + $self->{vars}->{username} = $user; + # generate output for $type . 'user' + my $out = $obj->output($filename,$type . "user"); + $output .= $out if defined $out; + # delete the username so it doesn't bleed anywhere + delete $self->{vars}->{username}; + } + } } + $self->write_file($filename,$output); } return 1; } + +# or my $module ($self->list()) { +# #create the $type root entries. entries. +# for my $file ($self->list($module,$type)) { +#warn "write_config() \$type: $type, \$module: $module, \$file: $file\n"; +# $self->write_file($file); +# } +#warn "User Loop"; +# #Create entries for users where needed +# } +# return 1; +#} + sub repo_ready { my $self=shift; my $r = $self->{repo};