Cosmetic changes to the help output and man page.
[whenjobs.git] / tools / whenjobs.pod
index 43fe847..25dff40 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>
@@ -148,6 +169,13 @@ variable when setting it by adding the optional I<--type> parameter:
 See the discussion of variable types in the L</REFERENCE> section
 below.
 
+=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<--upload>
 
 Compile the jobs script and upload it to the daemon, without editing.
@@ -375,6 +403,11 @@ that it had last time this when-job ran.
 
 If the when-job has not run yet, then this returns C<"">.
 
+Job state is preserved across file reloads, but I<only> for jobs that
+are explicitly named.  If you find that jobs using C<prev>, C<changes>
+etc are running unnecessarily when the jobs file is edited or
+uploaded, try giving the jobs an explicit name.
+
 =item B<changes> I<variable>
 
 If the named variable has changed since this job last ran, then this
@@ -396,14 +429,18 @@ 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 and difficult problem with using 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
-C<"" E<lt> everything>, the I<decreases> operator evaluates to
-false, and since this usually means the job does not run, the
-operator always evaluates to false.  A future version of whenjobs
-will address this problem.
+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 ()>
 
@@ -411,9 +448,7 @@ 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.
 
-You can use this to initialize variables, but note that this does not
-solve the I<decreases> operator problem described above, because
-variables are initialized too late to affect that.
+Don't use this to initialize variables: it won't do what you mean.
 
 =item B<false>
 
@@ -518,7 +553,7 @@ environment variable.
 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 cleanup
+setting the initial values of variables, or for defining pre and post
 functions.
 
 A simple example of an OCaml expression is:
@@ -541,15 +576,202 @@ 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 PRE FUNCTIONS
+
+Before a job runs, you can arrange that a C<pre> function is called.
+This function may decide not to run the job (by returning C<false>).
+
+One use for this is to prevent a particular job from running if there
+is already an instance of the same job running:
+
+ job "only one"
+ pre (Whentools.one ())
+ every 10 seconds :
+ <<
+   # Takes longer than 10 seconds to run, but 'Whentools.one ()'
+   # will ensure only one is ever running.
+   sleep 11
+ >>
+
+When using pre functions, jobs must be given an explicit name, ie.
+you must use the C<job> statement.
+
+A number of pre 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.preinfo> struct, defined
+below.  It should return a boolean: C<true> if the job should run, and
+C<false> if the job should not run.
+
+Note that a fresh serial number (see L</JOBSERIAL>) is assigned to
+each run, whether or not the job actually runs because of
+preconditions.
+
+=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.max> I<n>
+
+This built-in pre function ensures that a maximum of I<n> instances of
+the job are running.
+
+It checks the list of running jobs, and if I<n> or more instances are
+already running, then it returns C<false>, which ensures that the new
+job is not started.
+
+=item B<Whentools.one> I<()>
+
+This built-in pre function ensures that only one instance of the job
+is running.  It is the same as calling:
+
+ Whentools.max 1
+
+=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.preinfo>
+
+This structure is passed to pre functions.  It has the following
+fields:
+
+ type preinfo = {
+   pi_job_name : string;           # Job name.
+   pi_serial : Big_int.big_int;    # Job serial number.
+   pi_variables : (string * variable) list; # Variables set in job.
+   pi_running : preinfo_running_job list;   # List of running jobs.
+ }
+ and preinfo_running_job = {
+   pirun_job_name : string;        # Running job name.
+   pirun_serial : Big_int.big_int; # Running job serial number.
+   pirun_start_time : float;       # Running job start time.
+   pirun_pid : int;                # Running job process ID.
+ }
+
+=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