More hacking on webmin cron
authorJamie Cameron <jcameron@webmin.com>
Sun, 30 May 2010 06:28:26 +0000 (23:28 -0700)
committerJamie Cameron <jcameron@webmin.com>
Sun, 30 May 2010 06:28:26 +0000 (23:28 -0700)
time/apply.cgi
time/index.cgi
time/postinstall.pl
time/time-lib.pl
webmincron/lang/en [new file with mode: 0644]
webmincron/webmincron-lib.pl
webmincron/webmincron.pl

index 9a1bfa1..52388a4 100755 (executable)
@@ -63,28 +63,16 @@ if( $in{ 'action' } eq $text{ 'action_apply' } )
   &unlock_file($module_config_file);
 
   # Create, update or delete the syncing cron job
-  &foreign_require("cron", "cron-lib.pl");
-  $oldjob = $job = &find_cron_job();
-  $job ||= { 'command' => $cron_cmd,
-            'active' => 1,
-            'user' => 'root' };
-  &cron::parse_times_input($job, \%in) if ($in{'sched'});
-  &lock_file(&cron::cron_file($job));
-  &cron::create_wrapper($cron_cmd, $module_name, "sync.pl");
-  if ($in{'sched'} && $oldjob) {
-       # Update job
-       &cron::change_cron_job($job);
+  $job = &find_webmin_cron_job();
+  if ($in{'sched'}) {
+       $job ||= { 'module' => $module_name,
+                    'func' => 'sync_time_cron' };
+       &webmincron::parse_times_input($job, \%in);
+       &webmincron::create_webmin_cron($job);
        }
-  elsif ($in{'sched'} && !$oldjob) {
-       # Create wrapper script and job
-       &cron::create_cron_job($job);
+  elsif ($job) {
+       &webmincron::delete_webmin_cron($job);
        }
-  elsif (!$in{'sched'} && $oldjob) {
-       # Delete job
-       &cron::delete_cron_job($job);
-       }
-  &unlock_file(&cron::cron_file($job));
-
   &webmin_log("remote", $in{'action'} eq $text{'action_timeserver_sys'} ?  "date" : "hwclock", $rawtime, \%in);
   $mode = "sync";
 }
index f044723..459b78f 100755 (executable)
@@ -161,8 +161,7 @@ if ( ( !$access{ 'sysdate' } && &has_command( "date" ) || !$access{ 'hwdate' } &
                }
 
        # Show schedule input
-       &foreign_require("cron", "cron-lib.pl");
-       $job = &find_cron_job();
+       $job = &find_webmin_cron_job();
        print &ui_table_row($text{'index_sched'},
                &ui_radio("sched", $job ? 1 : 0,
                  [ [ 0, $text{'no'} ], [ 1, $text{'index_schedyes'} ] ]));
@@ -172,9 +171,8 @@ if ( ( !$access{ 'sysdate' } && &has_command( "date" ) || !$access{ 'hwdate' } &
                   'days' => '*',
                   'months' => '*',
                   'weekdays' => '*' };
-       print &ui_table_row(undef, "<table border width=100%>".
-               &capture_function_output(\&cron::show_times_input, $job).
-               "</table>", 2);
+       print &ui_table_row(undef,
+               &webmincron::show_times_input($job), 2);
 
        print &ui_table_end();
        print &ui_form_end([ [ "action", $text{'index_sync'} ] ]);
index fd96afa..97deb10 100755 (executable)
@@ -4,20 +4,21 @@ require 'time-lib.pl';
 # Convert existing cron job to webmin cron
 sub module_install
 {
-&foreign_require("cron", "cron-lib.pl");
-local $job = &find_cron_job();
-if ($job) {
-       &foreign_require("webmincron");
-       $wcron = { 'module' => $module_name,
-                  'func' => 'sync_time_cron',
-                  'special' => $job->{'special'},
-                  'mins' => $job->{'mins'},
-                  'hours' => $job->{'hours'},
-                  'days' => $job->{'days'},
-                  'months' => $job->{'months'},
-                  'weekdays' => $job->{'weekdays'},
-               };
-       &webmincron::create_webmin_cron($wcron, $job->{'command'});
+if (&foreign_check("cron")) {
+       &foreign_require("cron", "cron-lib.pl");
+       local $job = &find_cron_job();
+       if ($job) {
+               $wcron = { 'module' => $module_name,
+                          'func' => 'sync_time_cron',
+                          'special' => $job->{'special'},
+                          'mins' => $job->{'mins'},
+                          'hours' => $job->{'hours'},
+                          'days' => $job->{'days'},
+                          'months' => $job->{'months'},
+                          'weekdays' => $job->{'weekdays'},
+                       };
+               &webmincron::create_webmin_cron($wcron, $job->{'command'});
+               }
        }
 }
 
index 2df9489..163a719 100755 (executable)
@@ -8,6 +8,7 @@ $cron_cmd = "$module_config_directory/sync.pl";
 if ($config{'zone_style'}) {
        do "$config{'zone_style'}-lib.pl";
        }
+&foreign_require("webmincron");
 
 sub find_cron_job
 {
@@ -18,6 +19,11 @@ local ($job) = grep { $_->{'command'} eq $cron_cmd &&
 return $job;
 }
 
+sub find_webmin_cron_job
+{
+return &webmincron::find_webmin_cron($module_name, 'sync_time_cron');
+}
+
 # sync_time(server, hardware-too)
 # Syncs the system and maybe hardware time with some server. Returns undef
 # on success, or an error message on failure.
diff --git a/webmincron/lang/en b/webmincron/lang/en
new file mode 100644 (file)
index 0000000..0292e30
--- /dev/null
@@ -0,0 +1,18 @@
+edit_mins=Minutes
+edit_hours=Hours
+edit_days=Days
+edit_months=Months
+edit_weekdays=Weekdays
+edit_all=All
+edit_selected=Selected ..
+edit_run=Run Now
+edit_clone=Clone Job
+edit_return=cron job
+edit_special1=Simple schedule ..
+edit_special0=Times and dates selected below ..
+edit_special_hourly=Hourly
+edit_special_daily=Daily (at midnight)
+edit_special_weekly=Weekly (on Sunday)
+edit_special_monthly=Monthly (on the 1st)
+edit_special_yearly=Yearly (on 1st Jan)
+
index 1906d93..8be75cc 100644 (file)
@@ -13,7 +13,9 @@ Functions for creating and listing Webmin scheduled functions.
 BEGIN { push(@INC, ".."); };
 use WebminCore;
 &init_config();
+
 $webmin_crons_directory = "$module_config_directory/crons";
+@special_modes = ( 'hourly', 'daily', 'weekly', 'monthly', 'yearly' );
 
 =head2 list_webmin_crons
 
@@ -28,7 +30,7 @@ with keys :
 
 =item func - Name of the function to call
 
-=item arg0,arg1,etc.. - Strings to pass to the function as parameters
+=item args - Array ref of strings to pass to the function as parameters
 
 =item interval - Number of seconds between runs (optional)
 
@@ -54,6 +56,14 @@ foreach my $f (readdir(CRONS)) {
                my %cron;
                &read_file_cached("$webmin_crons_directory/$f", \%cron);
                $cron{'id'} = $1;
+               my @args;
+               for(my $i=0; defined($cron{'arg'.$i}); $i++) {
+                       push(@args, $cron{'arg'.$i});
+                       delete($cron{'arg'.$i});
+                       }
+               if (@args) {
+                       $cron{'args'} = \@args;
+                       }
                push(@rv, \%cron);
                }
        }
@@ -75,8 +85,15 @@ if (!$cron->{'id'}) {
        $cron->{'id'} = time().$$;
        }
 my $file = "$webmin_crons_directory/$cron->{'id'}.cron";
+my %wcron = %$cron;
+if ($wcron{'args'}) {
+       for(my $i=0; $i<@{$wcron{'args'}}; $i++) {
+               $wcron{'arg'.$i} = $wcron{'args'}->[$i];
+               }
+       delete($wcron{'args'});
+       }
 &lock_file($file);
-&write_file($file, $cron);
+&write_file($file, \%wcron);
 &unlock_file($file);
 &reload_miniserv();
 }
@@ -96,6 +113,31 @@ my $file = "$webmin_crons_directory/$cron->{'id'}.cron";
 &reload_miniserv();
 }
 
+=head2 find_webmin_cron(module, function, [&args])
+
+Returns a Webmin cron hash ref matching the given module and function
+
+=cut
+sub find_webmin_cron
+{
+my ($module, $func, $args) = @_;
+my @crons = &list_webmin_crons();
+foreach my $oc (@crons) {
+       next if ($oc->{'module'} ne $module);
+       next if ($oc->{'func'} ne $func);
+       if ($args) {
+               my $sameargs = 1;
+               for(my $i=0; $i < scalar(@{$oc->{'args'}}) ||
+                            $i < scalar(@$args); $i++) {
+                       $sameargs = 0 if ($oc->{'args'}->[$i] ne $args->[$i]);
+                       }
+               next if (!$sameargs);
+               }
+       return $oc;
+       }
+return undef;
+}
+
 =head2 create_webmin_cron(&cron, [old-cron-command])
 
 Create or update a webmin cron job that calls some function.
@@ -108,20 +150,8 @@ sub create_webmin_cron
 my ($cron, $old_cmd) = @_;
 
 # Find and replace existing cron with same module, function and args
-my @crons = &list_webmin_crons();
-my $already;
-foreach my $oc (@crons) {
-       next if ($oc->{'module'} ne $cron->{'module'});
-       next if ($oc->{'func'} ne $cron->{'func'});
-       my $sameargs = 1;
-       for(my $i=0; defined($oc->{'arg'.$i}) ||
-                    defined($cron->{'arg'.$i}); $i++) {
-               $sameargs = 0 if ($oc->{'arg'.$i} ne $cron->{'arg'.$i});
-               }
-       next if (!$sameargs);
-       $already = $oc;
-       last;
-       }
+my $already = &find_webmin_cron($cron->{'module'}, $cron->{'func'},
+                               $cron->{'args'});
 if ($already) {
        # Update existing, possibly with new interval
        $cron->{'id'} = $already->{'id'};
@@ -144,5 +174,195 @@ if ($old_cmd && &foreign_installed("cron")) {
        }
 }
 
+=head2 show_times_input(&job, [special])
+
+Returns HTML for inputs for selecting the schedule for a cron job, defined
+by the first parameter which must be a hash ref returned by list_cron_jobs.
+
+=cut
+sub show_times_input
+{
+my ($job, $special) = @_;
+$special = 0 if (!defined($special));
+my $rv = "<table width=100%>\n";
+if ($special || $job->{'special'}) {
+       # Allow selection of special @ times
+       $rv .= "<tr $cb> <td colspan=6>\n";
+       $rv .= &ui_radio("special_def", $job->{'special'} ? 1 : 0,
+               [ [ 1, $text{'edit_special1'}." ".
+                      &ui_select("special", $job->{'special'},
+                                 [ map { [ $_, $text{'edit_special_'.$_} ] }
+                                       @special_modes ]) ],
+                 [ 0, $text{'edit_special0'} ] ]);
+       $rv .= "</td></tr>\n";
+       }
+
+# Javascript to disable and enable fields
+$rv .= <<EOF;
+<script>
+function enable_cron_fields(name, form, ena)
+{
+var els = form.elements[name];
+els.disabled = !ena;
+for(i=0; i<els.length; i++) {
+  els[i].disabled = !ena;
+  }
+}
+</script>
+EOF
+
+$rv .= "<tr $tb>\n";
+$rv .= "<td><b>$text{'edit_mins'}</b></td> ".
+       "<td><b>$text{'edit_hours'}</b></td> ".
+       "<td><b>$text{'edit_days'}</b></td> ".
+       "<td><b>$text{'edit_months'}</b></td> ".
+       "<td><b>$text{'edit_weekdays'}</b></td>";
+$rv .= "</tr> <tr $cb>\n";
+
+my @mins = (0..59);
+my @hours = (0..23);
+my @days = (1..31);
+my @months = map { $text{"month_$_"}."=".$_ } (1 .. 12);
+my @weekdays = map { $text{"day_$_"}."=".$_ } (0 .. 6);
+my $arrmap = { 'mins' => \@mins,
+              'hours' => \@hours,
+              'days' => \@days,
+              'months' => \@months,
+              'weekdays' => \@weekdays };
+
+foreach my $arr ("mins", "hours", "days", "months", "weekdays") {
+       # Find out which ones are being used
+       my %inuse;
+       my $min = ($arr =~ /days|months/ ? 1 : 0);
+       my $max = $min+scalar(@{$arrmap->{$arr}})-1;
+       foreach my $w (split(/,/ , $job->{$arr})) {
+               if ($w eq "*") {
+                       # all values
+                       for(my $j=$min; $j<=$max; $j++) { $inuse{$j}++; }
+                       }
+               elsif ($w =~ /^\*\/(\d+)$/) {
+                       # only every Nth
+                       for(my $j=$min; $j<=$max; $j+=$1) { $inuse{$j}++; }
+                       }
+               elsif ($w =~ /^(\d+)-(\d+)\/(\d+)$/) {
+                       # only every Nth of some range
+                       for(my $j=$1; $j<=$2; $j+=$3) { $inuse{int($j)}++; }
+                       }
+               elsif ($w =~ /^(\d+)-(\d+)$/) {
+                       # all of some range
+                       for(my $j=$1; $j<=$2; $j++) { $inuse{int($j)}++; }
+                       }
+               else {
+                       # One value
+                       $inuse{int($w)}++;
+                       }
+               }
+       if ($job->{$arr} eq "*") { undef(%inuse); }
+
+       # Output selection list
+       $rv .= "<td valign=top>\n";
+        $rv .= sprintf
+               "<input type=radio name=all_$arr value=1 %s %s %s> %s<br>\n",
+                $arr eq "mins" && $hourly_only ? "disabled" : "",
+               $job->{$arr} eq "*" ||  $job->{$arr} eq "" ? "checked" : "",
+               "onClick='enable_cron_fields(\"$arr\", form, 0)'",
+               $text{'edit_all'};
+       $rv .= sprintf
+               "<input type=radio name=all_$arr value=0 %s %s> %s<br>\n",
+               $job->{$arr} eq "*" || $job->{$arr} eq "" ? "" : "checked",
+               "onClick='enable_cron_fields(\"$arr\", form, 1)'",
+               $text{'edit_selected'};
+       $rv .= "<table> <tr>\n";
+       my @arrlist = @{$arrmap->{$arr}};
+        for(my $j=0; $j<@arrlist; $j+=12) {
+                my $jj = $j + 11;
+               if ($jj >= @arrlist) { $jj = @arrlist - 1; }
+               my @sec = @arrlist[$j .. $jj];
+                $rv .= sprintf
+                       "<td valign=top><select %s size=%d name=$arr %s>\n",
+                        $arr eq "mins" && $hourly_only ? "" : "multiple",
+                        @sec > 12 ? 12 : scalar(@sec),
+                       $job->{$arr} eq "*" ||  $job->{$arr} eq "" ?
+                               "disabled" : "";
+               foreach my $v (@sec) {
+                       if ($v =~ /^(.*)=(.*)$/) { $disp = $1; $code = $2; }
+                       else { $disp = $code = $v; }
+                       $rv .= sprintf "<option value=\"$code\" %s>$disp\n",
+                               $inuse{$code} ? "selected" : "";
+                       }
+               $rv .= "</select></td>\n";
+               }
+       $rv .= "</tr></table></td>\n";
+       }
+$rv .= "</tr></table>\n";
+return $rv;
+}
+
+=head2 parse_times_input(&job, &in)
+
+Parses inputs from the form generated by show_times_input, and updates a cron
+job hash ref. The in parameter must be a hash ref as generated by the 
+ReadParse function.
+
+=cut
+sub parse_times_input
+{
+my $job = $_[0];
+my %in = %{$_[1]};
+my @pers = ("mins", "hours", "days", "months", "weekdays");
+if ($in{'special_def'}) {
+       # Job time is a special period
+       foreach my $arr (@pers) {
+               delete($job->{$arr});
+               }
+       $job->{'special'} = $in{'special'};
+       }
+else {
+       # User selection of times
+       foreach my $arr (@pers) {
+               if ($in{"all_$arr"}) {
+                       # All mins/hrs/etc.. chosen
+                       $job->{$arr} = "*";
+                       }
+               elsif (defined($in{$arr})) {
+                       # Need to work out and simplify ranges selected
+                       my @range = split(/\0/, $in{$arr});
+                       my @range = sort { $a <=> $b } @range;
+                       my @newrange;
+                       my $start = -1;
+                       for(my $i=0; $i<@range; $i++) {
+                               if ($i && $range[$i]-1 == $range[$i-1]) {
+                                       # ok.. looks like a range
+                                       if ($start < 0) { $start = $i-1; }
+                                       }
+                               elsif ($start < 0) {
+                                       # Not in a range at all
+                                       push(@newrange, $range[$i]);
+                                       }
+                               else {
+                                       # End of the range.. add it
+                                       $newrange[@newrange - 1] =
+                                               "$range[$start]-".$range[$i-1];
+                                       push(@newrange, $range[$i]);
+                                       $start = -1;
+                                       }
+                               }
+                       if ($start >= 0) {
+                               # Reached the end while in a range
+                               $newrange[@newrange - 1] =
+                                       "$range[$start]-".$range[$i-1];
+                               }
+                       $job->{$arr} = join(',' , @newrange);
+                       }
+               else {
+                       &error(&text('save_enone', $text{"edit_$arr"}));
+                       }
+               }
+       delete($job->{'special'});
+       }
+}
+
+
+
 1;
 
index b5a1ec1..a65d4fd 100755 (executable)
@@ -8,7 +8,9 @@ $cron = $ARGV[0];
 
 # Require the module, call the function
 &foreign_require($cron->{'module'}, $cron->{'file'});
-for($i=0; defined($cron->{'arg'.$i}); $i++) {
-       push(@args, $cron->{'arg'.$i});
+if ($cron->{'args'}) {
+       &foreign_call($cron->{'module'}, $cron->{'func'});
+       }
+else {
+       &foreign_call($cron->{'module'}, $cron->{'func'}, @{$cron->{'args'}});
        }
-&foreign_call($cron->{'module'}, $cron->{'func'}, @args);