let rec main () =
(* Parse the command line arguments. *)
let mode = ref None in
- let typ = ref "string" in
+ let typ = ref `String in
let set_mode m () = mode := Some m in
+ let set_type t =
+ typ :=
+ match t with
+ | "bool"|"boolean" -> `Bool
+ | "string" -> `String
+ | "int" -> `Int
+ | "float"|"double" -> `Float
+ | "unit" -> `Unit
+ | _ ->
+ eprintf "whenjobs: --type: unknown type (%s)\n" t;
+ exit 1
+ in
+
let display_version () =
printf "%s %s\n" Config.package_name Config.package_version;
exit 0
"--set", Arg.Unit (set_mode `Set), " Set the variable";
"--start", Arg.Unit (set_mode `Start), "name Start a job manually";
"--tail", Arg.Unit (set_mode `Tail), "serial Tail job output";
- "--type", Arg.Set_string typ, "bool|int|float|string|unit Set the variable type";
+ "--type", Arg.String set_type, "bool|int|float|string|.. Set the variable type";
"--upload", Arg.Unit (set_mode `Upload), " Upload the script";
"--variables", Arg.Unit (set_mode `Variables), " Display all variables and values";
"-V", Arg.Unit display_version, " Display version number and exit";
"--version", Arg.Unit display_version, " Display version number and exit";
] in
- let args = ref [] in
- let anon_fun str = args := str :: !args in
+ (* anon_fun normally just collects up the anonymous arguments as
+ * strings, and most modes just use 'args' as a list of strings.
+ * However for `Set mode we need to record the type of each argument
+ * as well, so we keep that in a separate list (argtypes).
+ *)
+ let argtypes = ref [] in
+ let anon_fun str = argtypes := (str, !typ) :: !argtypes in
let usage_msg = "\
Whenjobs is a powerful but simple cron replacement.
Get and set variables:
whenjobs --get variable
- whenjobs --set variable value
+ whenjobs --set variable=value
Start and stop the per-user daemon:
Arg.parse argspec anon_fun usage_msg;
let mode = !mode in
- let args = List.rev !args in
-
- let typ = match !typ with
- | "bool"|"boolean" -> `Bool
- | "string" -> `String
- | "int" -> `Int
- | "float"|"double" -> `Float
- | "unit" -> `Unit
- | t ->
- eprintf "whenjobs: --type: unknown type (%s)\n" t;
- exit 1 in
+ let argtypes = List.rev !argtypes in
+ let args = List.map fst argtypes in
let nr_args = List.length args in
let arg1 = match args with [] -> "" | a::_ -> a in
upload_file ()
| Some `Set ->
- if List.length args != 2 then (
- eprintf "whenjobs --set variable value\n";
- eprintf "If 'value' contains spaces, you may need to quote it.\n";
+ if nr_args = 2 && not (String.contains arg1 '=') then (
+ eprintf "'whenjobs --set variable value' is the old whenjobs <= 0.5 syntax!\n";
+ eprintf "You need to change this to:\n";
+ eprintf " whenjobs --set variable=value\n";
suggest_help ();
exit 1
);
- set_variable (List.hd args) (List.hd (List.tl args)) typ
+ (* Just ignore the case where no variables are defined, to make
+ * it easier to write shell scripts.
+ *)
+ if nr_args > 0 then
+ set_variables argtypes
| Some `Get ->
if nr_args != 1 then (
);
stop_client client
-and set_variable name value typ =
- let value = match typ with
- | `Bool ->
- (match value with
- | "true"|"t"|"yes"|"y"|"on"|"1" -> `bool_t true
- | "false"|"f"|"no"|"n"|"off"|"0" -> `bool_t false
- | _ ->
- eprintf "whenjobs: variable does not have a boolean value\n";
- exit 1
- )
- | `String -> `string_t value
- | `Int ->
- (try ignore (big_int_of_string value)
- with Failure _ ->
- eprintf "whenjobs: variable is not an integer\n";
- exit 1
- );
- `int_t value (* the string is what we pass over the wire *)
- | `Float ->
- (try `float_t (float_of_string value)
- with Failure _ ->
- eprintf "whenjobs: variable is not a floating point number\n";
- exit 1
- )
- | `Unit ->
- if value <> "" then (
- eprintf "whenjobs: unit variables must be empty strings\n";
- exit 1
- );
- `unit_t in
+and set_variables argtypes =
+ let vars = List.map (
+ fun (def, typ) ->
+ (* 'def' should have the form "name=value". The value part may
+ * be missing, but the equals sign is required.
+ *)
+ let i =
+ try String.index def '='
+ with Not_found ->
+ eprintf "whenjobs: set: missing = sign in variable definition\n";
+ suggest_help ();
+ exit 1 in
+ let name = String.sub def 0 i in
+ let value = String.sub def (i+1) (String.length def - (i+1)) in
+ let value = value_of_string value typ in
+ { Whenproto_aux.sv_name = name; sv_value = value }
+ ) argtypes in
+ let vars = Array.of_list vars in
let client = start_client () in
- (match Whenproto_clnt.When.V1.set_variable client (name, value) with
+ (match Whenproto_clnt.When.V1.set_variables client vars with
| `ok -> ()
| `error msg ->
eprintf "whenjobs: set: %s\n" msg;
| `int_t i -> i (* passed on the wire as a string *)
| `float_t f -> string_of_float f
+and value_of_string value = function
+ | `Bool ->
+ (match value with
+ | "true"|"t"|"yes"|"y"|"on"|"1" -> `bool_t true
+ | "false"|"f"|"no"|"n"|"off"|"0" -> `bool_t false
+ | _ ->
+ eprintf "whenjobs: variable does not have a boolean value\n";
+ exit 1
+ )
+ | `String -> `string_t value
+ | `Int ->
+ (try ignore (big_int_of_string value)
+ with Failure _ ->
+ eprintf "whenjobs: variable is not an integer\n";
+ exit 1
+ );
+ `int_t value (* the string is what we pass over the wire *)
+ | `Float ->
+ (try `float_t (float_of_string value)
+ with Failure _ ->
+ eprintf "whenjobs: variable is not a floating point number\n";
+ exit 1
+ )
+ | `Unit ->
+ if value <> "" then (
+ eprintf "whenjobs: unit variables must be empty strings\n";
+ exit 1
+ );
+ `unit_t
+
let () =
try main ()
with
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:
<<
# 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
$ whenjobs --variables
load=0.9
- $ whenjobs --set cat sushi
+ $ whenjobs --set cat=sushi
$ whenjobs --get cat
sushi
whenjobs --lib $builddir/lib -e
-=item B<--set> variable value
+=item B<--set> variable=value [variable=value ...]
=item B<--type> bool|int|float|string|unit
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.