File Coverage

lib/Schedule/Chronic/Tab.pm
Criterion Covered Total %
statement 3 102 2.9
branch 0 52 0.0
condition 0 15 0.0
subroutine 1 5 20.0
pod 0 3 0.0
total 4 177 2.2


line stmt bran cond sub pod time code
1             ##
2             ## Serialize schedule to chrontab and vice versa.
3             ## Author: Vipul Ved Prakash .
4             ## $Id: Tab.pm,v 1.10 2004/06/30 21:50:38 hackworth Exp $
5             ##
6              
7              
8             package Schedule::Chronic::Tab;
9 1     1   1379 use English;
  1         7067  
  1         7  
10              
11              
12             sub read_tabs {
13              
14 0     0 0   my ($self, $tab) = @_;
15              
16 0 0         if ($tab) {
17              
18             # Read the specified chrontab
19 0           $self->read_chrontab($tab, $UID);
20 0           return $self;
21              
22             }
23            
24             # A chrontab was not specified so we will find all
25             # appropriate chrontabs and load 'em up.
26              
27 0 0         if ($UID == 0) {
28              
29             # If the user is root, look for /etc/chrontab and then
30             # all user specific chrontabs.
31              
32 0 0         if (-e '/etc/chrontab') {
33 0           $self->read_chrontab('/etc/chrontab', 0);
34             } else {
35 0           $self->fatal("No chrontabs found. Was exepecting one in /etc/chrontab.");
36             }
37              
38             # To look for user specific chrontabs, we need to walk
39             # over all the users in /etc/passwd and search for
40             # $HOME/.chrontab under their user directories. We also
41             # need to discover their UIDs. All this information is
42             # available in /etc/passwd.
43             #
44             # FIX!
45              
46             } else {
47              
48             # The user is not root, we'll look for $HOME/.chrontab
49              
50 0           my $tab = "$ENV{HOME}/.chrontab";
51              
52 0 0         if (-e $tab) {
53            
54 0           $self->read_chrontab($tab, $UID);
55              
56             } else {
57              
58 0           $self->fatal("No chrontabs found. Was expecting one in $tab.");
59              
60             }
61              
62             }
63              
64              
65             }
66              
67              
68             sub read_chrontab {
69              
70 0     0 0   my ($self, $tab, $uid) = @_;
71              
72 0 0         open TAB, $tab or die "$tab: $!";
73 0           $self->debug("reading chrontab ``$tab''...");
74              
75             # A function to remove leading and trailing spaces from tokens.
76 0 0   0     my $normalize = sub { $_ = shift; return unless $_; s/^\s+//; s/\s+$//; return $_; };
  0            
  0            
  0            
  0            
  0            
77              
78             # Test vectors:
79             #
80             # 1. command = "/usr/bin/updatedb"; constraint = Inactivity, 600;
81             # 2. command = "/usr/bin/emerge rsync"; \
82             # constraint = DiskIO, 600; contraint = Loadavg, 600, 0.05;
83             # 3. command = "/usr/bin/emerge rsync"; \
84             # constraint = DiskIO, 600; contraint = Loadavg, 600, 0.05; \
85             # last_ran = 1082709815;
86              
87 0           my $last_entry = ''; # To keep track of continuations
88 0           my $tasks = 0;
89 0           my $linecursor = 0;
90              
91 0           while ($_ = ) {
92              
93 0           $linecursor++;
94 0 0         next unless /\S/;
95 0 0         next if /^\s*#/;
96 0           chomp;
97              
98 0           my $entry = $normalize->($_);
99              
100 0 0         if ($entry =~ m|\\$|) {
    0          
101              
102             # This is a continuation, save in $last_entry
103             # so it can be concatenated.
104              
105 0           $last_entry .= $entry;
106 0           $last_entry =~ s|\\$||;
107 0           next;
108              
109             } elsif ($last_entry) {
110              
111             # If there's a continuation, roll it in, and
112             # set $last_entry to empty string.
113              
114 0           $entry = "$last_entry $entry";
115 0           $last_entry = '';
116              
117             }
118              
119 0           my %task;
120 0           my @pairs = split /;/, $entry;
121 0           my $good = 0; # track is this is a good pair
122              
123 0           for (@pairs) {
124              
125             # Extract key = value; pairs
126 0           my ($key, $value) = split /=/, $_, 2;
127              
128             # Normalize key and value
129 0           $key = $normalize->($key);
130 0           $value = $normalize->($value);
131 0 0 0       next unless $key and $value;
132              
133 0           $good = 1;
134              
135 0 0         if ($key eq 'command') {
    0          
136              
137             # Remove quotes from the command
138 0           $value =~ s/^"//;
139 0           $value =~ s/"$//;
140 0           $task{$key} = $value;
141              
142             } elsif ($key eq 'constraint') {
143              
144             # A constraint contains a constrain name followed by an
145             # optional list of parameters for the constraint.
146              
147 0           my ($constraint, @thresholds) = split /,/, $value;
148              
149 0           my @n_thresholds;
150 0           $constraint = $normalize->($constraint);
151 0           for (@thresholds) {
152 0           push @n_thresholds, $normalize->($_);
153             }
154            
155 0           $task{constraints}->{$constraint} = {};
156 0 0         $task{constraints}->{$constraint}{thresholds} = [@n_thresholds] if
157             scalar @n_thresholds;
158             }
159              
160             else {
161              
162             # All other keys are read in verbatim.
163 0           $task{$key} = $value;
164            
165             }
166              
167             }
168              
169             # If there's no command, this task is useless to us.
170 0 0         $good = 0 unless exists $task{command};
171              
172 0 0         if ($good) {
173              
174             # Initialize the task.
175             #
176             # Add a last_ran of 0 (execute soon as possible)
177             # if a last_ran is not available. Create a
178             # task_wait timer and initialize other task
179             # parameters.
180              
181 0 0         $task{last_ran} = 0 unless exists $task{last_ran};
182 0 0         $task{only_once} = 0 unless exists $task{only_once};
183 0           $task{_task_wait} = new Schedule::Chronic::Timer ('down');
184 0           $task{_task_wait}->set(0);
185 0           $task{_uid} = $uid;
186 0           $task{_chrontab} = $tab;
187 0           $task{_last_rv} = 0;
188              
189 0           push @{$self->{_schedule}}, {%task};
  0            
190 0           $tasks++;
191              
192             } else {
193              
194             # This entry is b0rken. Show it to the user.
195             # We should probably barf here and ask the user
196             # to correct the error. FIX.
197              
198 0           $self->debug("Syntax error in line $linecursor of $tab - ignoring.");
199              
200             }
201              
202             }
203              
204 0           $self->debug("$tasks task(s) loaded.");
205              
206 0           close TAB;
207              
208             }
209              
210              
211             sub write_chrontab {
212              
213 0     0 0   my ($self, $tab) = @_;
214 0 0         open TAB, ">$tab" or die "$tab: $!\n";
215              
216             # Walk over the _schedule and write all tasks to the config
217             # file. This essentially serializes the _schedule in a format
218             # as close as the original file as possible.
219              
220 0           for (@{$self->{_schedule}}) {
  0            
221              
222 0           my $task = $_;
223              
224 0 0         unless ($$task{_chrontab} eq $tab) {
225              
226             # This task belongs to another chrontab,
227             # skip over it.
228              
229 0           next;
230              
231             }
232              
233 0 0 0       if ($$task{only_once} == 1 and $$task{last_ran} > 0) {
234              
235             # This was an ``only_once'' task that has been
236             # executed once. Don't write back to the
237             # chrontab.
238              
239 0           next;
240              
241             }
242              
243 0           for my $key (keys %$task) {
244              
245 0 0 0       if ($key eq 'command') {
    0 0        
    0          
246            
247             # Quote the command before writing it to disk.
248 0           print TAB "$key = \"$task->{$key}\"; ";
249              
250             } elsif ($key eq 'constraints') {
251              
252             # Serialize the constraint. Format is:
253             #`` constraint = name, thresholds''
254             # where thresholds is a comma separated list.
255            
256 0           my $constraints_set = $task->{constraints};
257              
258 0           for (keys %$constraints_set) {
259              
260 0           print TAB "constraint = $_";
261              
262 0 0         if (exists $constraints_set->{$_}->{thresholds}) {
263 0           $" = ", "; print TAB ", ";
  0            
264 0           print TAB "@{$constraints_set->{$_}->{thresholds}}";
  0            
265             }
266              
267 0           print TAB "; ";
268              
269             }
270              
271             # All verbatim fields go here.
272              
273             } elsif ($key eq 'last_ran' or $key eq 'notify' or $key eq 'only_once') {
274              
275 0 0 0       unless ($key eq 'only_once' and $task->{$key} == 0) {
276 0           print TAB "$key = $task->{$key}; ";
277             }
278              
279             } else {
280              
281             # Unrecognized key, which is used for the
282             # internal data structures.
283              
284 0           next;
285              
286             }
287              
288             }
289            
290 0           print TAB "\n";
291              
292             }
293              
294 0           $self->debug("wrote $tab");
295              
296 0           close TAB;
297              
298             }
299            
300              
301             1;
302