| 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"; | 
|---|