5 whenjobs - A powerful but simple cron replacement
9 Editing the jobs script:
14 Get and set variables:
16 whenjobs --get variable
17 whenjobs --set variable=value [variable=value ...]
20 Start and stop the per-user daemon:
22 whenjobs --daemon-start
23 whenjobs --daemon-stop
24 whenjobs --daemon-status
25 whenjobs --daemon-restart
30 whenjobs --cancel serial
31 whenjobs --start "name"
32 whenjobs --tail serial
36 Whenjobs is a powerful but simple replacement for cron. It lets you
37 run jobs periodically like cron, but it also lets you trigger jobs to
38 run when user-defined variables are set or change value.
40 Periodic jobs are written like this:
44 # Get the current load average.
45 load=`awk '{print $1}' /proc/loadavg`
46 whenjobs --set --type float load=$load
49 When-statements let you create jobs that run based on variables set
54 mail -s "ALERT: high load average: $load" $LOGNAME < /dev/null
57 (When statements are "edge-triggered", meaning that this job will only
58 run when the load goes from under 6 to E<ge> 6).
60 Like L<crontab(5)>, whenjobs are controlled by a jobs file which can
61 be edited from the command line:
65 Whenjobs uses a daemon called L<whenjobsd(8)>. Unlike crond, this
66 daemon runs as the same user. Each user who wants to use whenjobs
67 starts their own daemon:
69 $ whenjobs --daemon-start
71 You can also have the daemon start as you when the machine boots by
72 adding the following line to a boot file such as C</etc/rc.local>.
73 Replace C<username> with your username:
75 su username -c /usr/sbin/whenjobsd
77 Variables are the key to expressing dependencies between whenjobs.
78 Variables are stored (per-user) in the daemon. You can use the
79 command line tool to examine and set variables:
81 $ whenjobs --variables
83 $ whenjobs --set cat=sushi
87 The act of setting a variable (using I<--set>) can trigger jobs to run.
93 =item B<--cancel> serial
95 Cancel the job with the given serial number.
97 Use I<--jobs> to list running jobs along with their serial numbers.
98 The serial number is also available in the job script (as
99 C<$JOBSERIAL>) and in the log file.
101 =item B<--daemon-start>
103 =item B<--daemon-stop>
105 Start and stop the per-user daemon.
107 =item B<--daemon-status>
109 Prints the status of the daemon: C<up> or C<down>.
111 =item B<--daemon-restart>
113 Restart the daemon. (If it is not running, then this command
120 Edit the jobs script. If you make changes to the jobs script, then it
121 is automatically uploaded to the daemon.
123 The C<$EDITOR> environment variable is used for editing. If not set,
126 =item B<--get> variable
128 Print the value of a variable.
132 List all running jobs.
134 Note that it is possible for the same job to be running more than once
135 (for example, a periodic job that takes longer than the period to run).
141 List the jobs script.
143 =item B<--lib> directory
145 Set the library directory which needs to contain the auxiliary files
146 C<pa_when.cmo> and C<whenlib.cma>. Normally you do not need to
147 specify this. However if you are running whenjobs without installing
148 it, then you need to point this to the C<lib/> directory from the
151 whenjobs --lib $builddir/lib -e
153 =item B<--set> variable=value [variable=value ...]
155 =item B<--type> bool|int|float|string|unit
157 I<--set> sets the variable named C<variable> to the new C<value>. The
158 variable is created if it does not already exist. Note that setting a
159 variable can cause jobs to run immediately.
161 To unset a variable, set it to the empty string like this:
165 By default variables are strings. You can also set the type of a
166 variable when setting it by adding the optional I<--type> parameter.
167 The I<--type> parameter should come I<before> the variable
168 declaration, like this:
170 whenjobs --set --type int free_space=10000
172 See the discussion of variable types in the L</REFERENCE> section
175 You can set multiple variables. When setting multiple variables in a
176 single command, the values are all changed in a single atomic
179 whenjobs --set cat=sushi food=fish
181 When using I<--type> and multiple variables, the type changes the
182 remaining command line parameters until the next I<--type>, eg:
184 whenjobs --set cat=sushi --type float weight=3.5 --type string food=fish
186 (C<cat> and C<food> are strings, and C<weight> is a float).
188 =item B<--start> "job name"
190 Start the job immediately and unconditionally.
192 This runs the job even if its normal preconditions are not met. This
193 may cause unexpected results, so use with caution.
195 =item B<--tail> serial
197 Tail the output of the running job identified by its serial number.
198 Use the I<--jobs> flag to get a list of running jobs.
202 Compile the jobs script and upload it to the daemon, without editing.
203 Note that the I<--edit> option does this automatically. Furthermore,
204 when the daemon is started it checks for a jobs script and loads it if
209 Display all the variables and their values, in the format C<name=value>.
215 Display the name and version of the program and exit.
221 Display brief usage and exit.
227 A whenjobs file consists of a series of one or more "every" or "when"
230 Comments in the file can be written using C<(* ... *)>. Comments
233 Shell script fragments are written using C<E<lt>E<lt> ... E<gt>E<gt>>.
234 Within shell script fragments, use C<#> for comments (as in ordinary
235 shell scripts). Because C<E<gt>E<gt>> has a special meaning, it
236 cannot be used in the shell script (ie. for redirection). You have to
237 write C<E<gt>\E<gt>> instead which is replaced with C<E<gt>E<gt>> when
238 the shell script is parsed.
240 =head2 EVERY STATEMENTS (PERIODIC JOBS)
242 An every statement has the form:
249 where C<E<lt>periodE<gt>> is a I<period expression>, which may take
250 one of the forms below. Don't forget the colon character between the
251 period expression and the shell script.
253 An every statement is a job which runs periodically.
255 =head3 PERIOD EXPRESSIONS
259 =item B<every second>
261 The job runs every second.
263 =item B<every minute>
265 The job runs every minute.
269 The job runs every hour.
273 The job runs every day, at midnight UTC.
277 The job runs every week, on a Thursday at midnight UTC.
281 The job runs every month, on the first of the month at midnight UTC.
285 The job runs every year, on the first day of the year at midnight UTC.
287 =item B<every decade>
289 =item B<every century>
291 =item B<every millenium>
293 The job runs every 10, 100 or 1000 years.
295 =item B<every I<N> seconds>
297 The job runs every I<N> seconds (I<N> is any number E<ge> 1).
299 =item B<every I<N> minutes>
301 The job runs every I<N> minutes.
303 =item B<every I<N> hours>
305 The job runs every I<N> hours.
307 =item B<every I<N> days>
309 The job runs every I<N> days.
311 =item B<every I<N> weeks>
313 The job runs every I<N> weeks.
315 =item B<every I<N> months>
317 The job runs every I<N> months.
319 =item B<every I<N> years>
321 =item B<every I<N> decades>
323 =item B<every I<N> centuries>
325 =item B<every I<N> millenia>
327 The job runs every I<N>, I<10*N>, I<100*N> or I<1000*N> years.
331 =head2 WHEN STATEMENTS (DEPENDENT JOBS)
333 A when statement has the form:
340 where C<E<lt>exprE<gt>> is a I<when expression>, described below.
341 Don't forget the colon character between the period expression and the
344 A when statement is a job which runs when the conditions described in
345 its when-expression become true.
347 When jobs are I<edge triggered>. This means that they run when the
348 condition changes from false to true (or in the case where the
349 expression has not been evaluated before, when it evaluates initially
352 =head3 WHEN EXPRESSIONS
354 When expressions are fully recursive expressions constructed from the
359 =item I<expr> B<&&> I<expr>
361 =item I<expr> B<||> I<expr>
363 The boolean "and" or "or" of the two sub-expressions.
365 =item I<expr> B<E<lt>> I<expr>
367 =item I<expr> B<E<lt>=> I<expr>
369 =item I<expr> B<==> I<expr>
371 =item I<expr> B<E<gt>=> I<expr>
373 =item I<expr> B<E<gt>> I<expr>
375 The two sub-expressions are evaluated and the usual comparison
376 operator is performed.
378 If the sub-expressions are numeric, then numeric comparison is done.
379 If either sub-expression is non-numeric, then both expressions are
380 converted (if necessary) to strings and string comparison is done.
384 Boolean negative of I<expr>.
386 =item I<expr> B<+> I<expr>
388 For numeric sub-expressions, this performs addition.
390 If both sub-expressions are strings, this performs string
393 Other types give an error.
395 =item I<expr> B<-> I<expr>
397 =item I<expr> B<*> I<expr>
399 =item I<expr> B</> I<expr>
401 =item I<expr> B<mod> I<expr>
403 Both sub-expressions are evaluated, and if both are numeric, then the
404 result is subtraction, multiplication, division or modulo.
406 Other types give an error. Note that I<mod> really is an infix
411 If I<expr> is a string, this returns the length of the string.
415 The value of the named variable.
417 Previously undefined variables are automatically initialized to the
420 =item B<prev> I<variable>
422 The I<previous> value of the named variable. This means, the value
423 that it had last time this when-job ran.
425 If the when-job has not run yet, then this returns C<"">.
427 Job state is preserved across file reloads, but I<only> for jobs that
428 are explicitly named. If you find that jobs using C<prev>, C<changes>
429 etc are running unnecessarily when the jobs file is edited or
430 uploaded, try giving the jobs an explicit name.
432 =item B<changes> I<variable>
434 If the named variable has changed since this job last ran, then this
435 evaluates to true, else false.
437 This is the same as writing C<prev variable == variable>.
439 =item B<increases> I<variable>
441 If the named variable has changed and increased since this job last
442 ran, then this evaluates to true, else false.
444 This is the same as writing C<prev variable E<lt> variable>.
446 =item B<decreases> I<variable>
448 If the named variable has changed and decreased since this job last
449 ran, then this evaluates to true, else false.
451 This is the same as writing C<prev variable E<gt> variable>.
453 B<Note:> There is a subtle gotcha with the I<decreases> operator: The
454 first time the expression is evaluated, the job has (by definition)
455 not yet run. Therefore C<prev variable> evaluates to C<""> (see
456 definition of I<prev> above). Since it is always true that
460 the I<decreases> operator evaluates to false, and since this usually
461 means the job does not run, the operator always evaluates to false.
463 To fix this, ensure that the variable is initialized (see
464 L</SETTING THE INITIAL VALUE OF VARIABLES> below).
468 This evaluates to true the first time the expression is evaluated
469 after the jobs file has been reloaded or the daemon restarted.
470 Thereafter it evaluates to false.
472 Don't use this to initialize variables: it won't do what you mean.
478 Constants that evaluate to boolean false or true respectively.
480 =item I<"any string">
484 In a boolean context, the empty string evaluates to false, and
485 non-empty strings evaluate to true.
489 Any integer. (Arbitrarily large integers are supported.)
491 In a boolean context, 0 evaluates to false, and non-zero evaluates to
502 Any floating point number.
504 In a boolean context, 0 evaluates to false, and non-zero evaluates to
511 The code between C<E<lt>E<lt> ... E<gt>E<gt>> is a shell script. It
512 is executed using C<$SHELL>, or if that environment variable is not
515 =head3 SHELL SCRIPT VARIABLES
517 Every variable that has been set (using the whenjobs I<--set> option)
518 is exported to the script, so you can simply get the value of any
519 variable by writing C<$name>.
521 In addition, there are some special variables available:
527 The name of the job. If the job has been named explicitly, then that
528 name is available through this variable, else it will be some implicit
533 The serial number of the job. This is simply a variable that
534 increments each time a job is run, and is unique to that run of the
539 Other environment variables such as C<$HOME>, C<$LOGNAME> etc are
542 =head3 SHELL SCRIPT TEMPORARY CURRENT DIRECTORY
544 The shell script runs with its current directory set to a temporary
545 directory. The temporary directory is removed when the shell script
546 exits. Therefore you can write temporary files here without worrying
547 about cleaning them up.
549 If you want to store permanent state, then you have to save it to a
550 well-known directory, eg. C<$HOME>, C</var> etc.
552 =head3 SHELL SCRIPT USER
554 The shell script runs as the ordinary user. It has no special
559 Jobs are given implicit names (C<job$1>, C<job$2> etc.). You can also
560 name jobs explicitly by preceeding the "every" or "when" statement
569 The job name is passed to the shell script in the C<$JOBNAME>
570 environment variable.
572 =head2 OCAML EXPRESSIONS
574 As well as simple "every" and "when" expressions, advanced users may
575 want to use arbitrary OCaml expressions, functions, etc in the jobs
576 script. These are useful for factoring common code or strings, for
577 setting the initial values of variables, or for defining pre and post
580 A simple example of an OCaml expression is:
582 let prefix = "daily_"
584 job (prefix ^ "virus_scan")
590 job (prefix ^ "disk_check")
596 which creates two jobs called C<"daily_virus_scan"> and
597 C<"daily_disk_check"> (C<^> is the OCaml string concatenation
600 OCaml expressions have access to a library of functions called
601 B<Whentools> which is described below. It lets you set variables,
602 create jobs algorithmically, etc.
604 The OCaml expressions run once, when the jobs file is being loaded or
607 =head3 SETTING THE INITIAL VALUE OF VARIABLES
609 Variables are created when they are referenced, and until set they
610 have the value empty string (just like the shell). Across file
611 reloads, the previous values of variables are preserved.
613 To initialize a variable to a known value when the jobs file is
614 loaded, call one of the C<Whentools.set_variable*> functions as in
618 Whentools.set_variable "name" "Richard";
619 Whentools.set_variable_int "counter" 0
623 Before a job runs, you can arrange that a C<pre> function is called.
624 This function may decide not to run the job (by returning C<false>).
626 One use for this is to prevent a particular job from running if there
627 is already an instance of the same job running:
630 pre (Whentools.one ())
633 # Takes longer than 10 seconds to run, but 'Whentools.one ()'
634 # will ensure only one is ever running.
638 When using pre functions, jobs must be given an explicit name, ie.
639 you must use the C<job> statement.
641 A number of pre functions are available in the library; see below.
643 You can also write your own post functions (in OCaml). The function
644 is passed one argument which is a C<Whentools.preinfo> struct, defined
645 below. It should return a boolean: C<true> if the job should run, and
646 C<false> if the job should not run.
648 Note that a fresh serial number (see L</JOBSERIAL>) is assigned to
649 each run, whether or not the job actually runs because of
652 =head3 POST FUNCTIONS
654 After a job runs, you can control what happens to its output by
655 writing a C<post> function. To write a post function you have to
656 name the job (ie. have an explicit C<job> statement). Put C<post ...>
657 after the job name like this:
660 post (Whentools.mailto "you@example.com")
666 A number of post functions are available in the library; see below.
668 You can also write your own post functions (in OCaml). The
669 function is passed one argument which is a C<Whentools.result> struct,
672 =head3 WHENTOOLS LIBRARY
678 =item B<Whentools.mailto> [I<~only_on_failure:true>]
679 [I<~from:from_address>] I<email_address> I<result>
681 This built-in post function sends the result of the script by email to
682 the given email address.
684 If the optional C<~only_on_failure:true> flag is set, then it is only
685 sent out if the script failed.
687 If the optional C<~from> flag is set, then the from address is set
688 accordingly. This is sometimes needed when sending mail.
690 Note the C<result> parameter is passed implicitly by the daemon. You
691 do not need to add it.
693 Here are some examples of using the mailto function:
696 post (Whentools.mailto "you@example.com")
703 post (Whentools.mailto ~only_on_failure:true
710 let from = "me@example.com"
711 let to_addr = "you@example.com"
714 post (Whentools.mailto ~from to_addr)
720 =item B<Whentools.max> I<n>
722 This built-in pre function ensures that a maximum of I<n> instances of
725 It checks the list of running jobs, and if I<n> or more instances are
726 already running, then it returns C<false>, which ensures that the new
729 =item B<Whentools.one> I<()>
731 This built-in pre function ensures that only one instance of the job
732 is running. It is the same as calling:
736 =item B<Whentools.set_variable> I<name> I<string>
738 Set variable I<name> to the string.
740 =item B<Whentools.set_variable_bool> I<name> I<b>
742 Set variable I<name> to the boolean value I<b>.
744 =item B<Whentools.set_variable_int> I<name> I<i>
746 Set variable I<name> to the integer value I<i>.
748 =item B<Whentools.set_variable_string> I<name> I<s>
750 Set variable I<name> to the string value <s>. This is
751 the same as I<Whentools.set_variable>.
753 =item B<Whentools.set_variable_float> I<name> I<f>
755 Set variable I<name> to the floating point value I<f>.
763 =item B<Whentools.preinfo>
765 This structure is passed to pre functions. It has the following
769 pi_job_name : string; # Job name.
770 pi_serial : Big_int.big_int; # Job serial number.
771 pi_variables : (string * variable) list; # Variables set in job.
772 pi_running : preinfo_running_job list; # List of running jobs.
774 and preinfo_running_job = {
775 pirun_job_name : string; # Running job name.
776 pirun_serial : Big_int.big_int; # Running job serial number.
777 pirun_start_time : float; # Running job start time.
778 pirun_pid : int; # Running job process ID.
781 =item B<Whentools.result>
783 This structure is passed to post functions. It has the following
787 res_job_name : string; # job name
788 res_serial : big_int; # job serial (same as $JOBSERIAL)
789 res_code : int; # return code from the shell script
790 res_tmpdir : string; # temporary directory script ran in
791 res_output : string; # filename of stdout/stderr output
792 res_start_time : float; # when the job started
801 =head1 ENVIRONMENT VARIABLES
811 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
815 Copyright (C) 2012 Red Hat Inc.
817 This program is free software; you can redistribute it and/or modify
818 it under the terms of the GNU General Public License as published by
819 the Free Software Foundation; either version 2 of the License, or
820 (at your option) any later version.
822 This program is distributed in the hope that it will be useful,
823 but WITHOUT ANY WARRANTY; without even the implied warranty of
824 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
825 GNU General Public License for more details.
827 You should have received a copy of the GNU General Public License
828 along with this program; if not, write to the Free Software
829 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.