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 libguestfs_build_local=1.17.16
84 libguestfs_commit=7e32d892d76a31f55e2a4151902623b9949e3efa
85 libguestfs_dist=1.17.16
86 libguestfs_release=1.17.16
87 libguestfs_stable_build_local=1.16.10
88 libguestfs_stable_commit=27433a0a335301441b1eb6244ba425c2c44b2d99
89 libguestfs_stable_dist=1.16.10
90 libguestfs_stable_release=1.16.10
91 libguestfs_stable_version=1.16.10
92 libguestfs_version=1.17.16
93 $ whenjobs --set cat=sushi
97 Note: The act of setting a variable (using I<--set>) can trigger jobs
100 You can also list out what jobs are running:
103 287 libguestfs-stable: fedora 16
104 running in: /tmp/whenjobsa2afc44fd757465f95438309f1a51609
105 started at: 2012-03-13 10:59:37
107 and you can 'tail' the output of running jobs which is useful for
110 $ whenjobs --tail 287
111 Uploading: 147496271972717053d46b82a07435ca libguestfs-1.16.10.tar.gz
113 You can start and cancel jobs manually:
115 $ whenjobs --start 'libguestfs: poll'
116 $ whenjobs --cancel 287
122 =item B<--cancel> serial
124 Cancel the job with the given serial number.
126 Use I<--jobs> to list running jobs along with their serial numbers.
127 The serial number is also available in the job script (as
128 C<$JOBSERIAL>) and in the log file.
130 =item B<--daemon-start>
132 =item B<--daemon-stop>
134 Start and stop the per-user daemon.
136 =item B<--daemon-status>
138 Prints the status of the daemon: C<up> or C<down>.
140 =item B<--daemon-restart>
142 Restart the daemon. (If it is not running, then this command
149 Edit the jobs script. If you make changes to the jobs script, then it
150 is automatically uploaded to the daemon.
152 The C<$EDITOR> environment variable is used for editing. If not set,
155 =item B<--get> variable
157 Print the value of a variable.
163 Display brief usage and exit.
167 List the names of all loaded jobs (whether they are running or not).
168 Use I<--jobs> to list running jobs.
172 List all running jobs.
174 Note that it is possible for the same job to be running more than once
175 (for example, a periodic job that takes longer than the period to run).
181 List the jobs script.
183 =item B<--lib> directory
185 Set the library directory which needs to contain the auxiliary files
186 C<pa_when.cmo> and C<whenlib.cma>. Normally you do not need to
187 specify this. However if you are running whenjobs without installing
188 it, then you need to point this to the C<lib/> directory from the
191 whenjobs --lib $builddir/lib -e
193 =item B<--set> variable=value [variable=value ...]
195 =item B<--type> bool|int|float|string|unit
197 I<--set> sets the variable named C<variable> to the new C<value>. The
198 variable is created if it does not already exist. Note that setting a
199 variable can cause jobs to run immediately.
201 To unset a variable, set it to the empty string like this:
205 By default variables are strings. You can also set the type of a
206 variable when setting it by adding the optional I<--type> parameter.
207 The I<--type> parameter should come I<before> the variable
208 declaration, like this:
210 whenjobs --set --type int free_space=10000
212 See the discussion of variable types in the L</REFERENCE> section
215 You can set multiple variables. When setting multiple variables in a
216 single command, the values are all changed in a single atomic
219 whenjobs --set cat=sushi food=fish
221 When using I<--type> and multiple variables, the type changes the
222 remaining command line parameters until the next I<--type>, eg:
224 whenjobs --set cat=sushi \
225 --type float weight=3.5 \
226 --type string food=fish
228 (C<cat> and C<food> are strings, and C<weight> is a float).
230 =item B<--start> "job name"
232 Start the job immediately and unconditionally.
234 This runs the job even if its normal preconditions are not met. This
235 may cause unexpected results, so use with caution.
237 =item B<--tail> serial
239 Tail the output of the running job identified by its serial number.
240 Use the I<--jobs> flag to get a list of running jobs.
242 =item B<--test> variable=value [variable=value ...]
244 This works the same way as the I<--set> option, but the difference is
245 that the variables are not set. Instead, it lists out the jobs that
246 I<would> run, I<if> the variables were updated to these new values.
248 The variables are not actually updated, and the jobs are not actually
251 The output is a list of job names that would run.
255 Compile the jobs file(s) and upload it to the daemon, without editing.
256 Note that the I<--edit> option does this automatically. Furthermore,
257 when the daemon is started it checks for a jobs script and loads it if
260 See also L</MULTIPLE JOBS FILES> below.
264 Display all the variables and their values, in the format C<name=value>.
270 Display the name and version of the program and exit.
272 =item B<--whisper> variable=value [variable=value ...]
274 This works the same way as the I<--set> option, but with the
275 difference that jobs' when clauses are not reevaluated. In other
276 words, the variables are set, but "quietly" so as not to trigger any
279 Note that this can lead to some unexpected results: one case is a
282 when changed a || changed b : << ... >>
284 If C<a> is changed using I<--whisper>, then the job will not run.
286 But later on, if C<b> is set but to the same value that it already has
287 (ie. not changed), the job will run because the whole when-clause is
288 reevaluated and C<a> is found to have changed since the last run of
295 A whenjobs file consists of a series of one or more "every" or "when"
298 Comments in the file can be written using C<(* ... *)>. Comments
301 Shell script fragments are written using C<E<lt>E<lt> ... E<gt>E<gt>>.
302 Within shell script fragments, use C<#> for comments (as in ordinary
303 shell scripts). Because C<E<gt>E<gt>> has a special meaning, it
304 cannot be used in the shell script (ie. for redirection). You have to
305 write C<E<gt>\E<gt>> instead which is replaced with C<E<gt>E<gt>> when
306 the shell script is parsed.
308 =head2 EVERY STATEMENTS (PERIODIC JOBS)
310 An every statement has the form:
317 where C<E<lt>periodE<gt>> is a I<period expression>, which may take
318 one of the forms below. Don't forget the colon character between the
319 period expression and the shell script.
321 An every statement is a job which runs periodically.
323 =head3 PERIOD EXPRESSIONS
327 =item B<every second>
329 The job runs every second.
331 =item B<every minute>
333 The job runs every minute.
337 The job runs every hour.
341 The job runs every day, at midnight UTC.
345 The job runs every week, on a Thursday at midnight UTC.
349 The job runs every month, on the first of the month at midnight UTC.
353 The job runs every year, on the first day of the year at midnight UTC.
355 =item B<every decade>
357 =item B<every century>
359 =item B<every millenium>
361 The job runs every 10, 100 or 1000 years.
363 =item B<every I<N> seconds>
365 The job runs every I<N> seconds (I<N> is any number E<ge> 1).
367 =item B<every I<N> minutes>
369 The job runs every I<N> minutes.
371 =item B<every I<N> hours>
373 The job runs every I<N> hours.
375 =item B<every I<N> days>
377 The job runs every I<N> days.
379 =item B<every I<N> weeks>
381 The job runs every I<N> weeks.
383 =item B<every I<N> months>
385 The job runs every I<N> months.
387 =item B<every I<N> years>
389 =item B<every I<N> decades>
391 =item B<every I<N> centuries>
393 =item B<every I<N> millenia>
395 The job runs every I<N>, I<10*N>, I<100*N> or I<1000*N> years.
399 =head2 WHEN STATEMENTS (DEPENDENT JOBS)
401 A when statement has the form:
408 where C<E<lt>exprE<gt>> is a I<when expression>, described below.
409 Don't forget the colon character between the period expression and the
412 A when statement is a job which runs when the conditions described in
413 its when-expression become true.
415 When jobs are I<edge triggered>. This means that they run when the
416 condition changes from false to true (or in the case where the
417 expression has not been evaluated before, when it evaluates initially
420 =head3 WHEN EXPRESSIONS
422 When expressions are fully recursive expressions constructed from the
427 =item I<expr> B<&&> I<expr>
429 =item I<expr> B<||> I<expr>
431 The boolean "and" or "or" of the two sub-expressions.
433 =item I<expr> B<E<lt>> I<expr>
435 =item I<expr> B<E<lt>=> I<expr>
437 =item I<expr> B<==> I<expr>
439 =item I<expr> B<E<gt>=> I<expr>
441 =item I<expr> B<E<gt>> I<expr>
443 The two sub-expressions are evaluated and the usual comparison
444 operator is performed.
446 If the sub-expressions are numeric, then numeric comparison is done.
447 If either sub-expression is non-numeric, then both expressions are
448 converted (if necessary) to strings and string comparison is done.
452 Boolean negative of I<expr>.
454 =item I<expr> B<+> I<expr>
456 For numeric sub-expressions, this performs addition.
458 If both sub-expressions are strings, this performs string
461 Other types give an error.
463 =item I<expr> B<-> I<expr>
465 =item I<expr> B<*> I<expr>
467 =item I<expr> B</> I<expr>
469 =item I<expr> B<mod> I<expr>
471 Both sub-expressions are evaluated, and if both are numeric, then the
472 result is subtraction, multiplication, division or modulo.
474 Other types give an error. Note that I<mod> really is an infix
479 If I<expr> is a string, this returns the length of the string.
483 The value of the named variable.
485 Previously undefined variables are automatically initialized to the
488 =item B<prev> I<variable>
490 The I<previous> value of the named variable. This means, the value
491 that it had last time this when-job ran.
493 If the when-job has not run yet, then this returns C<"">.
495 Job state is preserved across file reloads, but I<only> for jobs that
496 are explicitly named. If you find that jobs using C<prev>, C<changes>
497 etc are running unnecessarily when the jobs file is edited or
498 uploaded, try giving the jobs an explicit name.
500 =item B<changes> I<variable>
502 If the named variable has changed since this job last ran, then this
503 evaluates to true, else false.
505 This is the same as writing C<prev variable == variable>.
507 =item B<increases> I<variable>
509 If the named variable has changed and increased since this job last
510 ran, then this evaluates to true, else false.
512 This is the same as writing C<prev variable E<lt> variable>.
514 =item B<decreases> I<variable>
516 If the named variable has changed and decreased since this job last
517 ran, then this evaluates to true, else false.
519 This is the same as writing C<prev variable E<gt> variable>.
521 B<Note:> There is a subtle gotcha with the I<decreases> operator: The
522 first time the expression is evaluated, the job has (by definition)
523 not yet run. Therefore C<prev variable> evaluates to C<""> (see
524 definition of I<prev> above). Since it is always true that
528 the I<decreases> operator evaluates to false, and since this usually
529 means the job does not run, the operator always evaluates to false.
531 To fix this, ensure that the variable is initialized (see
532 L</SETTING THE INITIAL VALUE OF VARIABLES> below).
536 This evaluates to true the first time the expression is evaluated
537 after the jobs file has been reloaded or the daemon restarted.
538 Thereafter it evaluates to false.
540 Don't use this to initialize variables: it won't do what you mean.
546 Constants that evaluate to boolean false or true respectively.
548 =item I<"any string">
552 In a boolean context, the empty string evaluates to false, and
553 non-empty strings evaluate to true.
557 Any integer. (Arbitrarily large integers are supported.)
559 In a boolean context, 0 evaluates to false, and non-zero evaluates to
570 Any floating point number.
572 In a boolean context, 0 evaluates to false, and non-zero evaluates to
579 The code between C<E<lt>E<lt> ... E<gt>E<gt>> is a shell script. It
580 is executed using C<$SHELL>, or if that environment variable is not
583 =head3 SHELL SCRIPT VARIABLES
585 Every variable that has been set (using the whenjobs I<--set> option)
586 is exported to the script, so you can simply get the value of any
587 variable by writing C<$name>.
589 In addition, there are some special variables available:
595 The name of the job. If the job has been named explicitly, then that
596 name is available through this variable, else it will be some implicit
601 The serial number of the job. This is simply a variable that
602 increments each time a job is run, and is unique to that run of the
607 Other environment variables such as C<$HOME>, C<$LOGNAME> etc are
610 =head3 SHELL SCRIPT TEMPORARY CURRENT DIRECTORY
612 The shell script runs with its current directory set to a temporary
613 directory. The temporary directory is removed when the shell script
614 exits. Therefore you can write temporary files here without worrying
615 about cleaning them up.
617 If you want to store permanent state, then you have to save it to a
618 well-known directory, eg. C<$HOME>, C</var> etc.
620 =head3 SHELL SCRIPT USER
622 The shell script runs as the ordinary user. It has no special
627 Jobs are given implicit names (C<job$1>, C<job$2> etc.). You can also
628 name jobs explicitly by preceeding the "every" or "when" statement
637 The job name is passed to the shell script in the C<$JOBNAME>
638 environment variable.
640 =head2 OCAML EXPRESSIONS
642 As well as simple "every" and "when" expressions, advanced users may
643 want to use arbitrary OCaml expressions, functions, etc in the jobs
644 script. These are useful for factoring common code or strings, for
645 setting the initial values of variables, or for defining pre and post
648 A simple example of an OCaml expression is:
650 let prefix = "daily_"
652 job (prefix ^ "virus_scan")
658 job (prefix ^ "disk_check")
664 which creates two jobs called C<"daily_virus_scan"> and
665 C<"daily_disk_check"> (C<^> is the OCaml string concatenation
668 OCaml expressions have access to a library of functions called
669 B<Whentools> which is described below. It lets you set variables,
670 create jobs algorithmically, etc.
672 The OCaml expressions run once, when the jobs file is being loaded or
675 =head3 SETTING THE INITIAL VALUE OF VARIABLES
677 Variables are created when they are referenced, and until set they
678 have the value empty string (just like the shell). Across file
679 reloads, the previous values of variables are preserved.
681 To initialize a variable to a known value when the jobs file is
682 loaded, call one of the C<Whentools.set_variable*> functions as in
686 Whentools.set_variable "name" "Richard";
687 Whentools.set_variable_int "counter" 0
691 Before a job runs, you can arrange that a C<pre> function is called.
692 This function may decide not to run the job (by returning C<false>).
694 One use for this is to prevent a particular job from running if there
695 is already an instance of the same job running:
698 pre (Whentools.one ())
701 # Takes longer than 10 seconds to run, but 'Whentools.one ()'
702 # will ensure only one is ever running.
706 When using pre functions, jobs must be given an explicit name, ie.
707 you must use the C<job> statement.
709 A number of pre functions are available in the library; see below.
711 You can also write your own post functions (in OCaml). The function
712 is passed one argument which is a C<Whentools.preinfo> struct, defined
713 below. It should return a boolean: C<true> if the job should run, and
714 C<false> if the job should not run.
716 Note that a fresh serial number (see L</JOBSERIAL>) is assigned to
717 each run, whether or not the job actually runs because of
720 =head3 POST FUNCTIONS
722 After a job runs, you can control what happens to its output by
723 writing a C<post> function. To write a post function you have to
724 name the job (ie. have an explicit C<job> statement). Put C<post ...>
725 after the job name like this:
728 post (Whentools.mailto "you@example.com")
734 A number of post functions are available in the library; see below.
736 You can also write your own post functions (in OCaml). The
737 function is passed one argument which is a C<Whentools.result> struct,
740 =head3 WHENTOOLS LIBRARY
746 =item B<Whentools.mailto> [I<~only_on_failure:true>]
747 [I<~from:from_address>] I<email_address> I<result>
749 This built-in post function sends the result of the script by email to
750 the given email address.
752 If the optional C<~only_on_failure:true> flag is set, then it is only
753 sent out if the script failed.
755 If the optional C<~from> flag is set, then the from address is set
756 accordingly. This is sometimes needed when sending mail.
758 Note the C<result> parameter is passed implicitly by the daemon. You
759 do not need to add it.
761 Here are some examples of using the mailto function:
764 post (Whentools.mailto "you@example.com")
771 post (Whentools.mailto ~only_on_failure:true
778 let from = "me@example.com"
779 let to_addr = "you@example.com"
782 post (Whentools.mailto ~from to_addr)
788 =item B<Whentools.max> I<n>
790 This built-in pre function ensures that a maximum of I<n> instances of
793 It checks the list of running jobs, and if I<n> or more instances are
794 already running, then it returns C<false>, which ensures that the new
797 =item B<Whentools.one> I<()>
799 This built-in pre function ensures that only one instance of the job
800 is running. It is the same as calling:
804 =item B<Whentools.set_variable> I<name> I<string>
806 Set variable I<name> to the string.
808 =item B<Whentools.set_variable_bool> I<name> I<b>
810 Set variable I<name> to the boolean value I<b>.
812 =item B<Whentools.set_variable_int> I<name> I<i>
814 Set variable I<name> to the integer value I<i>.
816 =item B<Whentools.set_variable_string> I<name> I<s>
818 Set variable I<name> to the string value <s>. This is
819 the same as I<Whentools.set_variable>.
821 =item B<Whentools.set_variable_float> I<name> I<f>
823 Set variable I<name> to the floating point value I<f>.
831 =item B<Whentools.preinfo>
833 This structure is passed to pre functions. It has the following
837 pi_job_name : string; # Job name.
838 pi_serial : Big_int.big_int; # Job serial number.
839 pi_variables : (string * variable) list; # Variables set in job.
840 pi_running : preinfo_running_job list; # List of running jobs.
842 and preinfo_running_job = {
843 pirun_job_name : string; # Running job name.
844 pirun_serial : Big_int.big_int; # Running job serial number.
845 pirun_start_time : float; # Running job start time.
846 pirun_pid : int; # Running job process ID.
849 =item B<Whentools.result>
851 This structure is passed to post functions. It has the following
855 res_job_name : string; # job name
856 res_serial : big_int; # job serial (same as $JOBSERIAL)
857 res_code : int; # return code from the shell script
858 res_tmpdir : string; # temporary directory script ran in
859 res_output : string; # filename of stdout/stderr output
860 res_start_time : float; # when the job started
865 =head1 MULTIPLE JOBS FILES
867 The whenjobs I<-e> and I<-l> options edit and list a file called
868 C<$HOME/.whenjobs/jobs.ml>.
870 You can also edit C<$HOME/.whenjobs/jobs.ml> by other means (eg. your
871 own editor). After editing, to recompile and upload it, use:
875 When you have lots of jobs, it is convenient to split the jobs across
876 multiple files. Any C<*.ml> files located in C<$HOME/.whenjobs> can
877 be used (with some restrictions on filenames -- see below). These are
878 compiled and loaded into the daemon using the I<--upload> command.
880 To create multiple jobs files, you cannot use the I<-e> or I<-l>
881 options. Instead you have to create them yourself in
882 C<$HOME/.whenjobs>, and when you have finished creating or editing
885 =head2 FILENAME RESTRICTIONS ON JOBS FILES
887 In OCaml, a file called C<jobs.ml> corresponds to an OCaml module
888 called C<Jobs> (note the capitalization). OCaml module names can only
889 contain ASCII alphanumeric characters, underscore, and C<'> (single
890 quote), and they must begin with an alphabetic character. The same
891 rules apply to jobs files.
893 Furthermore, various OCaml module names are reserved (eg. C<Map>,
894 C<List>). It is therefore better to prefix any names with something
895 specific to your application.
897 Examples of legal filenames are:
904 Examples of illegal filenames are:
906 ann.txt # must end with .ml
907 123.ml # must begin with alphabetic
908 app!.ml # must contain alphanumeric or underscore
909 app-foo.ml # must contain alphanumeric or underscore
910 map.ml # reserved module name
916 =head1 ENVIRONMENT VARIABLES
926 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
930 Copyright (C) 2012 Red Hat Inc.
932 This program is free software; you can redistribute it and/or modify
933 it under the terms of the GNU General Public License as published by
934 the Free Software Foundation; either version 2 of the License, or
935 (at your option) any later version.
937 This program is distributed in the hope that it will be useful,
938 but WITHOUT ANY WARRANTY; without even the implied warranty of
939 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
940 GNU General Public License for more details.
942 You should have received a copy of the GNU General Public License
943 along with this program; if not, write to the Free Software
944 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.