| [359] | 1 | #!/usr/bin/perl | 
|---|
 | 2 |  | 
|---|
 | 3 | # Author: <quentin@mit.edu> | 
|---|
 | 4 |  | 
|---|
 | 5 | use strict; | 
|---|
 | 6 | use warnings; | 
|---|
 | 7 |  | 
|---|
 | 8 | use File::Spec::Functions; | 
|---|
 | 9 | use Getopt::Long; | 
|---|
 | 10 |  | 
|---|
 | 11 | use constant { | 
|---|
 | 12 |     CRON_DIR => "cron_scripts", | 
|---|
 | 13 |     CRONTAB_FILE => "crontab", | 
|---|
 | 14 |     AUTO_DIR => "AUTO", | 
|---|
 | 15 |     SPOOL_DIR => "/mit/scripts/cron/crontabs", | 
|---|
 | 16 | }; | 
|---|
 | 17 |  | 
|---|
 | 18 | my $force = 0; | 
|---|
 | 19 | my $list = 0; | 
|---|
 | 20 | my $pretend = 0; | 
|---|
 | 21 |  | 
|---|
 | 22 | sub get_crontabs() { | 
|---|
 | 23 |     my $crontab = catfile($ENV{"HOME"}, CRON_DIR, CRONTAB_FILE); | 
|---|
 | 24 |     my $crontabdir = catdir($ENV{"HOME"}, CRON_DIR, AUTO_DIR); | 
|---|
 | 25 |      | 
|---|
 | 26 |     my @crontabs; | 
|---|
 | 27 |      | 
|---|
 | 28 |     opendir(CRONTABS, $crontabdir) or print "You don't have a ".CRON_DIR."/".AUTO_DIR."/ directory\n"; | 
|---|
 | 29 |     push(@crontabs, grep { -r $_ } map { catfile($crontabdir, $_) } grep { !/^[\.#]/ } readdir(CRONTABS)); | 
|---|
 | 30 |     closedir(CRONTABS); | 
|---|
 | 31 |      | 
|---|
 | 32 |     push (@crontabs, $crontab) if (-r $crontab); | 
|---|
 | 33 |     return @crontabs; | 
|---|
 | 34 | } | 
|---|
 | 35 |  | 
|---|
 | 36 | sub read_crontab($) { | 
|---|
 | 37 |     my ($file) = @_; | 
|---|
 | 38 |     # local $/; | 
|---|
 | 39 |      | 
|---|
 | 40 |     open(CRONTAB, $file) or die "Couldn't read crontab $file!"; | 
|---|
 | 41 |     my @lines = <CRONTAB>; | 
|---|
 | 42 |     close(CRONTAB); | 
|---|
 | 43 |      | 
|---|
 | 44 |     return @lines; | 
|---|
 | 45 | } | 
|---|
 | 46 |  | 
|---|
 | 47 | sub check_crontab(@) { | 
|---|
 | 48 |     my (@lines) = @_; | 
|---|
 | 49 |      | 
|---|
 | 50 |     my @errors; | 
|---|
 | 51 |      | 
|---|
 | 52 |     foreach my $line (@lines) { | 
|---|
 | 53 |         $line =~ s|#.*$||; # Remove comments | 
|---|
 | 54 |         $line =~ s|^\s*(.*?)\s*$|$1|; # Remove whitespace | 
|---|
 | 55 |          | 
|---|
 | 56 |         if ($line =~ m|^\w[\w\d]*=|) { | 
|---|
 | 57 |             # Comment | 
|---|
 | 58 |             next; | 
|---|
 | 59 |         } elsif ($line =~ m|^(?:(\S+)\s+){5}(.*)|) { | 
|---|
 | 60 |             # Crontab line | 
|---|
 | 61 |             my ($minute, $hour, $day, $month, $dow) = ($1,$2,$3,$4,$5); | 
|---|
 | 62 |             # FIXME: Validate the time fields. | 
|---|
 | 63 |             next; | 
|---|
 | 64 |         } elsif ($line =~ m|^\s*$|) { | 
|---|
 | 65 |             # Whitespace | 
|---|
 | 66 |             next; | 
|---|
 | 67 |         } else { | 
|---|
 | 68 |             push(@errors, "Unrecognized crontab line:\n$line\n"); | 
|---|
 | 69 |         } | 
|---|
 | 70 |     } | 
|---|
 | 71 |     return @errors; | 
|---|
 | 72 | } | 
|---|
 | 73 |  | 
|---|
 | 74 |  | 
|---|
 | 75 |  | 
|---|
 | 76 | GetOptions("force|f+" => \$force, | 
|---|
 | 77 |            "list|l" => \$list, | 
|---|
 | 78 |            "pretend|p" => \$pretend); | 
|---|
 | 79 |  | 
|---|
 | 80 | if ($list) { | 
|---|
 | 81 |     my $file = catfile(SPOOL_DIR, $ENV{"USER"}); | 
|---|
 | 82 |     local $/; | 
|---|
 | 83 |     open (CRONTAB, $file) or die "No crontab installed.\n"; | 
|---|
 | 84 |     print <CRONTAB>; | 
|---|
 | 85 |     close (CRONTAB); | 
|---|
 | 86 |     exit; | 
|---|
 | 87 | } | 
|---|
 | 88 |  | 
|---|
 | 89 | my @crontabs = get_crontabs(); | 
|---|
 | 90 | my @all_errors; | 
|---|
 | 91 | my @final_crontab; | 
|---|
 | 92 | my ($numvalid, $numinvalid) = (0,0); | 
|---|
 | 93 |  | 
|---|
 | 94 | foreach my $crontab (@crontabs) { | 
|---|
 | 95 |     push(@final_crontab, "### $crontab\n"); | 
|---|
 | 96 |     my @crontab = read_crontab($crontab); | 
|---|
 | 97 |     my @errors = check_crontab(@crontab); | 
|---|
 | 98 |     if (@errors == 0) { | 
|---|
 | 99 |         print "$crontab is a valid crontab\n"; | 
|---|
 | 100 |         push(@final_crontab, @crontab); | 
|---|
 | 101 |         $numvalid++; | 
|---|
 | 102 |     } else { | 
|---|
 | 103 |         print "$crontab has errors:\n"; | 
|---|
 | 104 |         push(@all_errors, scalar(@errors)." errors in $crontab:\n", @errors); | 
|---|
 | 105 |         print join("\n", @errors); | 
|---|
 | 106 |         $numinvalid++; | 
|---|
 | 107 |         if ($force >= 2) { | 
|---|
 | 108 |             push(@final_crontab, @crontab); | 
|---|
 | 109 |         } else { | 
|---|
 | 110 |             my $errors = join("\n", @errors); | 
|---|
 | 111 |             $errors =~ s|^|# |mg; | 
|---|
 | 112 |             push(@final_crontab, "## $crontab was not installed due to errors:\n", $errors); | 
|---|
 | 113 |         } | 
|---|
 | 114 |     } | 
|---|
 | 115 | } | 
|---|
 | 116 | if ($pretend) { | 
|---|
 | 117 |     print "Would install this crontab:\n"; | 
|---|
 | 118 |     print @final_crontab; | 
|---|
 | 119 |     exit; | 
|---|
 | 120 | } | 
|---|
 | 121 |  | 
|---|
 | 122 | if ($force < 1 && @all_errors) { | 
|---|
 | 123 |     print "Not loading new crontab. Use -f to force.\n"; | 
|---|
 | 124 |     exit; | 
|---|
 | 125 | } | 
|---|
 | 126 | if ($force >= 2 && @all_errors) { | 
|---|
 | 127 |     print "Loading $numvalid crontab ($numinvalid BROKEN!) files...\n"; | 
|---|
 | 128 | } else { | 
|---|
 | 129 |     print "Loading $numvalid crontab files...\n"; | 
|---|
 | 130 | } | 
|---|
 | 131 |  | 
|---|
 | 132 | # FIXME | 
|---|
 | 133 | # Load @final_crontab somehow | 
|---|
 | 134 |  | 
|---|
 | 135 | print "done.\n"; | 
|---|