docs: Fix man entry for "changes" operator.
[whenjobs.git] / tools / whenjobs.pod
index 20f0be5..8455bcf 100644 (file)
@@ -14,7 +14,7 @@ Editing the jobs script:
 Get and set variables:
 
  whenjobs --get variable
- whenjobs --set variable value [--type bool|int|float|string]
+ whenjobs --set variable=value [variable=value ...]
  whenjobs --variables
 
 Start and stop the per-user daemon:
@@ -29,6 +29,7 @@ Examine running jobs:
  whenjobs --jobs
  whenjobs --cancel serial
  whenjobs --start "name"
+ whenjobs --tail serial
 
 =head1 DESCRIPTION
 
@@ -42,7 +43,7 @@ Periodic jobs are written like this:
  <<
    # Get the current load average.
    load=`awk '{print $1}' /proc/loadavg`
-   whenjobs --set load $load --type float
+   whenjobs --set --type float load=$load
  >>
 
 When-statements let you create jobs that run based on variables set
@@ -78,12 +79,41 @@ Variables are stored (per-user) in the daemon.  You can use the
 command line tool to examine and set variables:
 
  $ whenjobs --variables
- load=0.9
- $ whenjobs --set cat sushi
+ JOBSERIAL=297
+ libguestfs_build_local=1.17.16
+ libguestfs_commit=7e32d892d76a31f55e2a4151902623b9949e3efa
+ libguestfs_dist=1.17.16
+ libguestfs_release=1.17.16
+ libguestfs_stable_build_local=1.16.10
+ libguestfs_stable_commit=27433a0a335301441b1eb6244ba425c2c44b2d99
+ libguestfs_stable_dist=1.16.10
+ libguestfs_stable_release=1.16.10
+ libguestfs_stable_version=1.16.10
+ libguestfs_version=1.17.16
+ $ whenjobs --set cat=sushi
  $ whenjobs --get cat
  sushi
 
-The act of setting a variable (using I<--set>) can trigger jobs to run.
+Note: The act of setting a variable (using I<--set>) can trigger jobs
+to run.
+
+You can also list out what jobs are running:
+
+ $ whenjobs --jobs
+ 287 libguestfs-stable: fedora 16
+    running in: /tmp/whenjobsa2afc44fd757465f95438309f1a51609
+    started at: 2012-03-13 10:59:37
+
+and you can 'tail' the output of running jobs which is useful for
+debugging:
+
+ $ whenjobs --tail 287
+ Uploading: 147496271972717053d46b82a07435ca  libguestfs-1.16.10.tar.gz
+
+You can start and cancel jobs manually:
+
+ $ whenjobs --start 'libguestfs: poll'
+ $ whenjobs --cancel 287
 
 =head1 OPTIONS
 
@@ -126,6 +156,17 @@ C<vi> is used.
 
 Print the value of a variable.
 
+=item B<-help>
+
+=item B<--help>
+
+Display brief usage and exit.
+
+=item B<--job-names>
+
+List the names of all loaded jobs (whether they are running or not).
+Use I<--jobs> to list running jobs.
+
 =item B<--jobs>
 
 List all running jobs.
@@ -149,14 +190,7 @@ 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<--set> variable=value [variable=value ...]
 
 =item B<--type> bool|int|float|string|unit
 
@@ -164,25 +198,67 @@ 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
 variable can cause jobs to run immediately.
 
-To unset a variable, set it to the empty string:
+To unset a variable, set it to the empty string like this:
 
- whenjobs --set var ""
+ whenjobs --set var=
 
 By default variables are strings.  You can also set the type of a
-variable when setting it by adding the optional I<--type> parameter:
+variable when setting it by adding the optional I<--type> parameter.
+The I<--type> parameter should come I<before> the variable
+declaration, like this:
 
- whenjobs --set free_space 10000 --type int
+ whenjobs --set --type int free_space=10000
 
 See the discussion of variable types in the L</REFERENCE> section
 below.
 
+You can set multiple variables.  When setting multiple variables in a
+single command, the values are all changed in a single atomic
+operation.
+
+ whenjobs --set cat=sushi food=fish
+
+When using I<--type> and multiple variables, the type changes the
+remaining command line parameters until the next I<--type>, eg:
+
+ whenjobs --set cat=sushi \
+     --type float weight=3.5 \
+     --type string food=fish
+
+(C<cat> and C<food> are strings, and C<weight> is a float).
+
+=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<--tail> serial
+
+Tail the output of the running job identified by its serial number.
+Use the I<--jobs> flag to get a list of running jobs.
+
+=item B<--test> variable=value [variable=value ...]
+
+This works the same way as the I<--set> option, but the difference is
+that the variables are not set.  Instead, it lists out the jobs that
+I<would> run, I<if> the variables were updated to these new values.
+
+The variables are not actually updated, and the jobs are not actually
+run.
+
+The output is a list of job names that would run.
+
 =item B<--upload>
 
-Compile the jobs script and upload it to the daemon, without editing.
+Compile the jobs file(s) and upload it to the daemon, without editing.
 Note that the I<--edit> option does this automatically.  Furthermore,
 when the daemon is started it checks for a jobs script and loads it if
 found.
 
+See also L</MULTIPLE JOBS FILES> below.
+
 =item B<--variables>
 
 Display all the variables and their values, in the format C<name=value>.
@@ -193,11 +269,24 @@ Display all the variables and their values, in the format C<name=value>.
 
 Display the name and version of the program and exit.
 
-=item B<-help>
+=item B<--whisper> variable=value [variable=value ...]
 
-=item B<--help>
+This works the same way as the I<--set> option, but with the
+difference that jobs' when clauses are not reevaluated.  In other
+words, the variables are set, but "quietly" so as not to trigger any
+jobs to run.
 
-Display brief usage and exit.
+Note that this can lead to some unexpected results: one case is a
+job such as:
+
+ when changed a || changed b : << ... >>
+
+If C<a> is changed using I<--whisper>, then the job will not run.
+
+But later on, if C<b> is set but to the same value that it already has
+(ie. not changed), the job will run because the whole when-clause is
+reevaluated and C<a> is found to have changed since the last run of
+the job.
 
 =back
 
@@ -317,7 +406,7 @@ A when statement has the form:
  >>
 
 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
+Don't forget the colon character between the expression and the
 shell script.
 
 A when statement is a job which runs when the conditions described in
@@ -358,6 +447,12 @@ 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 I<expr> B<E<lt>E<gt>> I<expr>
+
+=item I<expr> B<!=> I<expr>
+
+Either form can be used to test the two expressions for inequality.
+
 =item B<!> I<expr>
 
 Boolean negative of I<expr>.
@@ -403,12 +498,17 @@ 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
 evaluates to true, else false.
 
-This is the same as writing C<prev variable == variable>.
+This is the same as writing C<prev variable != variable>.
 
 =item B<increases> I<variable>
 
@@ -592,6 +692,37 @@ this example:
    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
@@ -621,7 +752,8 @@ defined below.
 =item B<Whentools.mailto> [I<~only_on_failure:true>]
 [I<~from:from_address>] I<email_address> I<result>
 
-Send the result of the script by email to the given email address.
+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.
@@ -659,6 +791,22 @@ Here are some examples of using the mailto function:
    # 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.
@@ -686,6 +834,24 @@ Set variable I<name> to the floating point value I<f>.
 
 =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
@@ -702,6 +868,53 @@ fields:
 
 =back
 
+=head1 MULTIPLE JOBS FILES
+
+The whenjobs I<-e> and I<-l> options edit and list a file called
+C<$HOME/.whenjobs/jobs.ml>.
+
+You can also edit C<$HOME/.whenjobs/jobs.ml> by other means (eg.  your
+own editor).  After editing, to recompile and upload it, use:
+
+ whenjobs --upload
+
+When you have lots of jobs, it is convenient to split the jobs across
+multiple files.  Any C<*.ml> files located in C<$HOME/.whenjobs> can
+be used (with some restrictions on filenames -- see below).  These are
+compiled and loaded into the daemon using the I<--upload> command.
+
+To create multiple jobs files, you cannot use the I<-e> or I<-l>
+options.  Instead you have to create them yourself in
+C<$HOME/.whenjobs>, and when you have finished creating or editing
+them, upload them.
+
+=head2 FILENAME RESTRICTIONS ON JOBS FILES
+
+In OCaml, a file called C<jobs.ml> corresponds to an OCaml module
+called C<Jobs> (note the capitalization).  OCaml module names can only
+contain ASCII alphanumeric characters, underscore, and C<'> (single
+quote), and they must begin with an alphabetic character.  The same
+rules apply to jobs files.
+
+Furthermore, various OCaml module names are reserved (eg. C<Map>,
+C<List>).  It is therefore better to prefix any names with something
+specific to your application.
+
+Examples of legal filenames are:
+
+ foo.ml
+ app_foo.ml
+ app_123.ml
+ jobs.ml
+
+Examples of illegal filenames are:
+
+ ann.txt    # must end with .ml
+ 123.ml     # must begin with alphabetic
+ app!.ml    # must contain alphanumeric or underscore
+ app-foo.ml # must contain alphanumeric or underscore
+ map.ml     # reserved module name
+
 =head1 FILES