In comment: Cleanup -> Post.
[whenjobs.git] / tools / whenjobs.pod
index 99c38cf..b8c76a6 100644 (file)
@@ -24,6 +24,12 @@ Start and stop the per-user daemon:
  whenjobs --daemon-status
  whenjobs --daemon-restart
 
+Examine running jobs:
+
+ whenjobs --jobs
+ whenjobs --cancel serial
+ whenjobs --start "name"
+
 =head1 DESCRIPTION
 
 Whenjobs is a powerful but simple replacement for cron.  It lets you
@@ -83,6 +89,14 @@ The act of setting a variable (using I<--set>) can trigger jobs to run.
 
 =over 4
 
+=item B<--cancel> serial
+
+Cancel the job with the given serial number.
+
+Use I<--jobs> to list running jobs along with their serial numbers.
+The serial number is also available in the job script (as
+C<$JOBSERIAL>) and in the log file.
+
 =item B<--daemon-start>
 
 =item B<--daemon-stop>
@@ -112,6 +126,13 @@ C<vi> is used.
 
 Print the value of a variable.
 
+=item B<--jobs>
+
+List all running jobs.
+
+Note that it is possible for the same job to be running more than once
+(for example, a periodic job that takes longer than the period to run).
+
 =item B<-l>
 
 =item B<--list>
@@ -128,9 +149,16 @@ source, eg:
 
  whenjobs --lib $builddir/lib -e
 
+=item B<--start> "job name"
+
+Start the job immediately and unconditionally.
+
+This runs the job even if its normal preconditions are not met.  This
+may cause unexpected results, so use with caution.
+
 =item B<--set> variable value
 
-=item B<--type> bool|int|float|string
+=item B<--type> bool|int|float|string|unit
 
 I<--set> sets the variable named C<variable> to the new C<value>.  The
 variable is created if it does not already exist.  Note that setting a
@@ -140,9 +168,8 @@ To unset a variable, set it to the empty string:
 
  whenjobs --set var ""
 
-By default variables are untyped (more precisely, they are treated as
-strings).  You can also set the type of a variable when setting it by
-adding the optional I<--type> parameter:
+By default variables are strings.  You can also set the type of a
+variable when setting it by adding the optional I<--type> parameter:
 
  whenjobs --set free_space 10000 --type int
 
@@ -176,9 +203,505 @@ Display brief usage and exit.
 
 =head1 REFERENCE
 
+A whenjobs file consists of a series of one or more "every" or "when"
+statements.
+
+Comments in the file can be written using C<(* ... *)>.  Comments
+may be nested.
+
+Shell script fragments are written using C<E<lt>E<lt> ... E<gt>E<gt>>.
+Within shell script fragments, use C<#> for comments (as in ordinary
+shell scripts).  Because C<E<gt>E<gt>> has a special meaning, it
+cannot be used in the shell script (ie. for redirection).  You have to
+write C<E<gt>\E<gt>> instead which is replaced with C<E<gt>E<gt>> when
+the shell script is parsed.
+
+=head2 EVERY STATEMENTS (PERIODIC JOBS)
+
+An every statement has the form:
+
+ every <period> :
+ <<
+   # shell script
+ >>
+
+where C<E<lt>periodE<gt>> is a I<period expression>, which may take
+one of the forms below.  Don't forget the colon character between the
+period expression and the shell script.
+
+An every statement is a job which runs periodically.
+
+=head3 PERIOD EXPRESSIONS
+
+=over 4
+
+=item B<every second>
+
+The job runs every second.
+
+=item B<every minute>
+
+The job runs every minute.
+
+=item B<every hour>
+
+The job runs every hour.
+
+=item B<every day>
+
+The job runs every day, at midnight UTC.
+
+=item B<every week>
+
+The job runs every week, on a Thursday at midnight UTC.
+
+=item B<every month>
+
+The job runs every month, on the first of the month at midnight UTC.
+
+=item B<every year>
+
+The job runs every year, on the first day of the year at midnight UTC.
+
+=item B<every decade>
+
+=item B<every century>
+
+=item B<every millenium>
+
+The job runs every 10, 100 or 1000 years.
+
+=item B<every I<N> seconds>
+
+The job runs every I<N> seconds (I<N> is any number E<ge> 1).
+
+=item B<every I<N> minutes>
+
+The job runs every I<N> minutes.
+
+=item B<every I<N> hours>
+
+The job runs every I<N> hours.
+
+=item B<every I<N> days>
+
+The job runs every I<N> days.
+
+=item B<every I<N> weeks>
+
+The job runs every I<N> weeks.
+
+=item B<every I<N> months>
+
+The job runs every I<N> months.
+
+=item B<every I<N> years>
+
+=item B<every I<N> decades>
+
+=item B<every I<N> centuries>
+
+=item B<every I<N> millenia>
+
+The job runs every I<N>, I<10*N>, I<100*N> or I<1000*N> years.
+
+=back
+
+=head2 WHEN STATEMENTS (DEPENDENT JOBS)
+
+A when statement has the form:
+
+ when <expr> :
+ <<
+   # shell script
+ >>
+
+where C<E<lt>exprE<gt>> is a I<when expression>, described below.
+Don't forget the colon character between the period expression and the
+shell script.
+
+A when statement is a job which runs when the conditions described in
+its when-expression become true.
+
+When jobs are I<edge triggered>.  This means that they run when the
+condition changes from false to true (or in the case where the
+expression has not been evaluated before, when it evaluates initially
+to true).
+
+=head3 WHEN EXPRESSIONS
+
+When expressions are fully recursive expressions constructed from the
+following elements:
+
+=over 4
+
+=item I<expr> B<&&> I<expr>
+
+=item I<expr> B<||> I<expr>
+
+The boolean "and" or "or" of the two sub-expressions.
+
+=item I<expr> B<E<lt>> I<expr>
+
+=item I<expr> B<E<lt>=> I<expr>
+
+=item I<expr> B<==> I<expr>
+
+=item I<expr> B<E<gt>=> I<expr>
+
+=item I<expr> B<E<gt>> I<expr>
+
+The two sub-expressions are evaluated and the usual comparison
+operator is performed.
+
+If the sub-expressions are numeric, then numeric comparison is done.
+If either sub-expression is non-numeric, then both expressions are
+converted (if necessary) to strings and string comparison is done.
+
+=item B<!> I<expr>
+
+Boolean negative of I<expr>.
+
+=item I<expr> B<+> I<expr>
+
+For numeric sub-expressions, this performs addition.
+
+If both sub-expressions are strings, this performs string
+concatenation.
+
+Other types give an error.
+
+=item I<expr> B<-> I<expr>
+
+=item I<expr> B<*> I<expr>
+
+=item I<expr> B</> I<expr>
+
+=item I<expr> B<mod> I<expr>
+
+Both sub-expressions are evaluated, and if both are numeric, then the
+result is subtraction, multiplication, division or modulo.
+
+Other types give an error.  Note that I<mod> really is an infix
+operator.
+
+=item B<len> I<expr>
+
+If I<expr> is a string, this returns the length of the string.
+
+=item I<variable>
+
+The value of the named variable.
+
+Previously undefined variables are automatically initialized to the
+empty string.
+
+=item B<prev> I<variable>
+
+The I<previous> value of the named variable.  This means, the value
+that it had last time this when-job ran.
+
+If the when-job has not run yet, then this returns C<"">.
+
+=item B<changes> I<variable>
+
+If the named variable has changed since this job last ran, then this
+evaluates to true, else false.
+
+This is the same as writing C<prev variable == variable>.
+
+=item B<increases> I<variable>
+
+If the named variable has changed and increased since this job last
+ran, then this evaluates to true, else false.
+
+This is the same as writing C<prev variable E<lt> variable>.
+
+=item B<decreases> I<variable>
 
+If the named variable has changed and decreased since this job last
+ran, then this evaluates to true, else false.
 
+This is the same as writing C<prev variable E<gt> variable>.
 
+B<Note:> There is a subtle gotcha with the I<decreases> operator: The
+first time the expression is evaluated, the job has (by definition)
+not yet run.  Therefore C<prev variable> evaluates to C<""> (see
+definition of I<prev> above).  Since it is always true that
+
+ "" < anything
+
+the I<decreases> operator evaluates to false, and since this usually
+means the job does not run, the operator always evaluates to false.
+
+To fix this, ensure that the variable is initialized (see
+L</SETTING THE INITIAL VALUE OF VARIABLES> below).
+
+=item B<reloaded ()>
+
+This evaluates to true the first time the expression is evaluated
+after the jobs file has been reloaded or the daemon restarted.
+Thereafter it evaluates to false.
+
+Don't use this to initialize variables: it won't do what you mean.
+
+=item B<false>
+
+=item B<true>
+
+Constants that evaluate to boolean false or true respectively.
+
+=item I<"any string">
+
+Any string.
+
+In a boolean context, the empty string evaluates to false, and
+non-empty strings evaluate to true.
+
+=item I<N>
+
+Any integer.  (Arbitrarily large integers are supported.)
+
+In a boolean context, 0 evaluates to false, and non-zero evaluates to
+true.
+
+=item I<N.>
+
+=item I<.N>
+
+=item I<N.N>
+
+=item I<N.NeN>
+
+Any floating point number.
+
+In a boolean context, 0 evaluates to false, and non-zero evaluates to
+true.
+
+=back
+
+=head2 SHELL SCRIPTS
+
+The code between C<E<lt>E<lt> ... E<gt>E<gt>> is a shell script.  It
+is executed using C<$SHELL>, or if that environment variable is not
+set then C</bin/sh>.
+
+=head3 SHELL SCRIPT VARIABLES
+
+Every variable that has been set (using the whenjobs I<--set> option)
+is exported to the script, so you can simply get the value of any
+variable by writing C<$name>.
+
+In addition, there are some special variables available:
+
+=over 4
+
+=item C<$JOBNAME>
+
+The name of the job.  If the job has been named explicitly, then that
+name is available through this variable, else it will be some implicit
+name like C<job$1>.
+
+=item C<$JOBSERIAL>
+
+The serial number of the job.  This is simply a variable that
+increments each time a job is run, and is unique to that run of the
+job.
+
+=back
+
+Other environment variables such as C<$HOME>, C<$LOGNAME> etc are
+available as normal.
+
+=head3 SHELL SCRIPT TEMPORARY CURRENT DIRECTORY
+
+The shell script runs with its current directory set to a temporary
+directory.  The temporary directory is removed when the shell script
+exits.  Therefore you can write temporary files here without worrying
+about cleaning them up.
+
+If you want to store permanent state, then you have to save it to a
+well-known directory, eg. C<$HOME>, C</var> etc.
+
+=head3 SHELL SCRIPT USER
+
+The shell script runs as the ordinary user.  It has no special
+privileges.
+
+=head2 JOB NAMES
+
+Jobs are given implicit names (C<job$1>, C<job$2> etc.).  You can also
+name jobs explicitly by preceeding the "every" or "when" statement
+with C<job "name">:
+
+ job "poll source"
+ every 10 seconds :
+ <<
+   # ...
+ >>
+
+The job name is passed to the shell script in the C<$JOBNAME>
+environment variable.
+
+=head2 OCAML EXPRESSIONS
+
+As well as simple "every" and "when" expressions, advanced users may
+want to use arbitrary OCaml expressions, functions, etc in the jobs
+script.  These are useful for factoring common code or strings, for
+setting the initial values of variables, or for defining pre and post
+functions.
+
+A simple example of an OCaml expression is:
+
+ let prefix = "daily_"
+ job (prefix ^ "virus_scan")
+ every day :
+ <<
+   # ...
+ >>
+ job (prefix ^ "disk_check")
+ every day :
+ <<
+   # ...
+ >>
+
+which creates two jobs called C<"daily_virus_scan"> and
+C<"daily_disk_check"> (C<^> is the OCaml string concatenation
+operator).
+
+OCaml expressions have access to a library of functions called
+B<Whentools> which is described below.  It lets you set variables,
+create jobs algorithmically, etc.
+
+The OCaml expressions run once, when the jobs file is being loaded or
+reloaded.
+
+=head3 SETTING THE INITIAL VALUE OF VARIABLES
+
+Variables are created when they are referenced, and until set they
+have the value empty string (just like the shell).  Across file
+reloads, the previous values of variables are preserved.
+
+To initialize a variable to a known value when the jobs file is
+loaded, call one of the C<Whentools.set_variable*> functions as in
+this example:
+
+ let () =
+   Whentools.set_variable "name" "Richard";
+   Whentools.set_variable_int "counter" 0
+
+=head3 POST FUNCTIONS
+
+After a job runs, you can control what happens to its output by
+writing a C<post> function.  To write a post function you have to
+name the job (ie. have an explicit C<job> statement).  Put C<post ...>
+after the job name like this:
+
+ job "poll source"
+ post (Whentools.mailto "you@example.com")
+ every 10 seconds :
+ <<
+   # ...
+ >>
+
+A number of post functions are available in the library; see below.
+
+You can also write your own post functions (in OCaml).  The
+function is passed one argument which is a C<Whentools.result> struct,
+defined below.
+
+=head3 WHENTOOLS LIBRARY
+
+=head4 Functions
+
+=over 4
+
+=item B<Whentools.mailto> [I<~only_on_failure:true>]
+[I<~from:from_address>] I<email_address> I<result>
+
+This built-in post function sends the result of the script by email to
+the given email address.
+
+If the optional C<~only_on_failure:true> flag is set, then it is only
+sent out if the script failed.
+
+If the optional C<~from> flag is set, then the from address is set
+accordingly.  This is sometimes needed when sending mail.
+
+Note the C<result> parameter is passed implicitly by the daemon.  You
+do not need to add it.
+
+Here are some examples of using the mailto function:
+
+ job "ex.1"
+ post (Whentools.mailto "you@example.com")
+ every 10 seconds :
+ <<
+   # do something
+ >>
+
+ job "ex.2"
+ post (Whentools.mailto ~only_on_failure:true
+                        "you@example.com")
+ every 10 seconds :
+ <<
+   # do something
+ >>
+
+ let from = "me@example.com"
+ let to_addr = "you@example.com"
+ job "ex.3"
+ post (Whentools.mailto ~from to_addr)
+ every 10 seconds :
+ <<
+   # do something
+ >>
+
+=item B<Whentools.set_variable> I<name> I<string>
+
+Set variable I<name> to the string.
+
+=item B<Whentools.set_variable_bool> I<name> I<b>
+
+Set variable I<name> to the boolean value I<b>.
+
+=item B<Whentools.set_variable_int> I<name> I<i>
+
+Set variable I<name> to the integer value I<i>.
+
+=item B<Whentools.set_variable_string> I<name> I<s>
+
+Set variable I<name> to the string value <s>.  This is
+the same as I<Whentools.set_variable>.
+
+=item B<Whentools.set_variable_float> I<name> I<f>
+
+Set variable I<name> to the floating point value I<f>.
+
+=back
+
+=head4 Structures
+
+=over 4
+
+=item B<Whentools.result>
+
+This structure is passed to post functions.  It has the following
+fields:
+
+ type result = {
+   res_job_name : string;  # job name
+   res_serial : big_int;   # job serial (same as $JOBSERIAL)
+   res_code : int;         # return code from the shell script
+   res_tmpdir : string;    # temporary directory script ran in
+   res_output : string;    # filename of stdout/stderr output
+   res_start_time : float; # when the job started
+ }
+
+=back
 
 =head1 FILES