2 * Copyright (C) 2012 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 (* Ensures that Whentools module is linked to the whenjobs tool. *)
24 let _ = Whentools.set_variable
26 let libdir = ref Libdir.libdir
29 (* Running the program as root is a mistake. It must be run as a
32 let euid = geteuid () in
34 eprintf "whenjobs: this program must not be run as root\n";
38 (* $HOME must be defined and must exist and be a directory and must be
39 * owned by the current user.
44 eprintf "whenjobs: $HOME environment variable must be defined\n";
49 with Unix_error (err, fn, _) ->
50 eprintf "whenjobs: %s: %s ($HOME): %s\n" fn home (error_message err);
52 if stat.st_kind != S_DIR then (
53 eprintf "whenjobs: %s ($HOME): not a directory\n" home;
57 if stat.st_uid != euid then (
58 eprintf "whenjobs: %s ($HOME): not owned by the current user (uid %d)\n"
63 (* Make the $HOME/.whenjobs directory if it doesn't exist. *)
64 let jobsdir = sprintf "%s/.whenjobs" home in
65 (try mkdir jobsdir 0o700 with Unix_error _ -> ());
70 (* Parse the command line arguments. *)
71 let mode = ref None in
72 let typ = ref "string" in
74 let set_mode m () = mode := Some m in
76 let display_version () =
77 printf "%s %s\n" Config.package_name Config.package_version;
81 let argspec = Arg.align [
82 "--daemon-start", Arg.Unit (set_mode `Daemon_start), " Start the daemon";
83 "--daemon-status", Arg.Unit (set_mode `Daemon_status), " Display the status of the daemon";
84 "--daemon-stop", Arg.Unit (set_mode `Daemon_stop), " Stop the daemon";
85 "--daemon-restart", Arg.Unit (set_mode `Daemon_restart), " Restart the daemon";
86 "-e", Arg.Unit (set_mode `Edit), " Edit and upload the script";
87 "--edit", Arg.Unit (set_mode `Edit), " Edit and upload the script";
88 "--get", Arg.Unit (set_mode `Get), " Display the variable";
89 "-l", Arg.Unit (set_mode `List), " List the script";
90 "--list", Arg.Unit (set_mode `List), " List the script";
91 "--lib", Arg.Set_string libdir, "dir Specify directory that contains pa_when.cmo";
92 "--set", Arg.Unit (set_mode `Set), " Set the variable";
93 "--type", Arg.Set_string typ, "bool|int|float|string|unit Set the variable type";
94 "--upload", Arg.Unit (set_mode `Upload), " Upload the script";
95 "--variables", Arg.Unit (set_mode `Variables), " Display all variables and values";
96 "-V", Arg.Unit display_version, " Display version number and exit";
97 "--version", Arg.Unit display_version, " Display version number and exit";
101 let anon_fun str = args := str :: !args in
104 Whenjobs is a powerful but simple cron replacement.
105 Whenjobs copyright (C) 2012 Red Hat Inc.
112 Get and set variables:
114 whenjobs --get variable
115 whenjobs --set variable value
117 Start and stop the per-user daemon:
119 whenjobs --daemon-start | --daemon-stop | --daemon-status
121 For documentation see the whenjobs(1) man page.
126 Arg.parse argspec anon_fun usage_msg;
129 let args = List.rev !args in
131 let typ = match !typ with
132 | "bool"|"boolean" -> `Bool
133 | "string" -> `String
135 | "float"|"double" -> `Float
138 eprintf "whenjobs: --type: unknown type (%s)\n" t;
141 (* Depending on the selected mode, perform a different action. *)
144 eprintf "whenjobs: no operation selected.\n";
149 unused_error args "-e";
153 unused_error args "-l";
157 unused_error args "--upload";
161 if List.length args != 2 then (
162 eprintf "whenjobs --set variable value\n";
163 eprintf "If 'value' contains spaces, you may need to quote it.\n";
167 set_variable (List.hd args) (List.hd (List.tl args)) typ
170 if List.length args != 1 then (
171 eprintf "whenjobs --get variable\n";
175 get_variable (List.hd args)
178 unused_error args "--variables";
181 | Some `Daemon_start ->
182 unused_error args "--daemon-start";
185 | Some `Daemon_stop ->
186 unused_error args "--daemon-stop";
189 | Some `Daemon_restart ->
190 unused_error args "--daemon-restart";
193 | Some `Daemon_status ->
194 unused_error args "--daemon-status";
198 (* If there is no initial file, create an empty one containing the
201 let file = get_jobs_filename () in
202 if not (Sys.file_exists file) then
203 create_tutorial file;
205 (* Is $EDITOR set? If not, use a default. *)
206 let editor = try getenv "EDITOR" with Not_found -> "vi" in
208 (* Get the (size, MD5) of the file to tell if it changed. *)
210 try (lstat file).st_size, Digest.file file
211 with Unix_error (err, fn, _) ->
212 eprintf "whenjobs: %s: %s: %s\n" fn file (error_message err);
215 let old_stamp = file_stamp () in
217 let cmd = sprintf "%s %s" editor file in
218 if Sys.command cmd != 0 then (
219 eprintf "whenjobs: error editing file (is $EDITOR set correctly?)\n";
223 let new_stamp = file_stamp () in
225 if old_stamp <> new_stamp then
229 let file = get_jobs_filename () in
230 if not (Sys.file_exists file) then (
231 eprintf "whenjobs: there is no jobs file, use 'whenjobs -e' to create one\n";
234 let chan = open_in file in
236 printf "%s\n" (input_line chan);
239 (try loop () with End_of_file -> ());
243 (* Recompile the jobs file. *)
244 let file = get_jobs_filename () in
245 let cmo_file = sprintf "%s/jobs.cmo" jobsdir in
246 let cmd = sprintf "ocamlfind ocamlc -I +camlp4 -I %s -package unix,camlp4.lib -pp 'camlp4o %s/pa_when.cmo' -c %s -o %s"
247 !libdir !libdir file cmo_file in
248 if Sys.command cmd <> 0 then (
249 eprintf "whenjobs: could not compile jobs script, see earlier error messages\n";
250 eprintf "compile command was:\n%s\n" cmd;
254 (* Test-load the jobs file to ensure it makes sense. *)
255 Whenfile.init Whenstate.empty;
257 Dynlink.loadfile cmo_file
260 eprintf "whenjobs: %s\n" (Dynlink.error_message err);
261 (* Since it failed, unlink it. *)
262 (try unlink cmo_file with Unix_error _ -> ());
266 (* OK now let's tell the daemon to reload it. *)
267 let client = start_client () in
268 (match Whenproto_clnt.When.V1.reload_file client () with
271 eprintf "whenjobs: reload: %s\n" msg;
272 suggest_check_server_logs ();
277 and set_variable name value typ =
278 let value = match typ with
281 | "true"|"t"|"yes"|"y"|"on"|"1" -> `bool_t true
282 | "false"|"f"|"no"|"n"|"off"|"0" -> `bool_t false
284 eprintf "whenjobs: variable does not have a boolean value\n";
287 | `String -> `string_t value
289 (try ignore (big_int_of_string value)
291 eprintf "whenjobs: variable is not an integer\n";
294 `int_t value (* the string is what we pass over the wire *)
296 (try `float_t (float_of_string value)
298 eprintf "whenjobs: variable is not a floating point number\n";
302 if value <> "" then (
303 eprintf "whenjobs: unit variables must be empty strings\n";
308 let client = start_client () in
309 (match Whenproto_clnt.When.V1.set_variable client (name, value) with
312 eprintf "whenjobs: set: %s\n" msg;
313 suggest_check_server_logs ();
318 and get_variable name =
319 let client = start_client () in
320 let value = Whenproto_clnt.When.V1.get_variable client name in
321 print_endline (string_of_variable value);
324 and list_variables () =
325 let client = start_client () in
326 let names = Whenproto_clnt.When.V1.get_variable_names client () in
329 let value = Whenproto_clnt.When.V1.get_variable client name in
330 printf "%s=%s\n" name (string_of_variable value)
334 and daemon_start () =
338 let client = start_client () in
339 (match Whenproto_clnt.When.V1.exit_daemon client () with
342 eprintf "whenjobs: daemon-stop: %s\n" msg;
343 suggest_check_server_logs ();
348 and daemon_restart () =
351 and daemon_status () =
354 and unused_error args op =
356 eprintf "whenjobs %s: unused parameters on the command line.\n" op;
361 and suggest_help () =
362 eprintf "Use 'whenjobs --help' for a summary of options or read whenjobs(1) man page.\n"
364 and suggest_check_server_logs () =
365 eprintf "Look at the server logs (/var/log/cron or /var/log/syslog usually) for\n";
366 eprintf "further information on why this daemon operation failed.\n"
368 and get_jobs_filename () =
369 sprintf "%s/jobs.ml" jobsdir
371 and create_tutorial file =
372 let chan = open_out file in
373 output_string chan Tutorial.tutorial;
376 and start_client () =
377 let addr = sprintf "%s/socket" jobsdir in
380 Whenproto_clnt.When.V1.create_client
381 (Rpc_client.Unix addr)
382 Rpc.Tcp (* not TCP, this is the same as SOCK_STREAM *)
384 | Unix_error ((ECONNREFUSED|ENOENT), _, _) ->
385 eprintf "whenjobs: error: the daemon ('whenjobsd') is not running\n";
386 eprintf "Use 'whenjobs --daemon-start' to start the daemon.\n";
388 | Unix_error (err, fn, _) ->
389 eprintf "whenjobs: %s: %s: %s\n" fn addr (error_message err);
393 and stop_client client =
394 Rpc_client.shut_down client
396 and string_of_variable = function
398 | `bool_t b -> string_of_bool b
400 | `int_t i -> i (* passed on the wire as a string *)
401 | `float_t f -> string_of_float f
406 (* Pretty print some of the exceptions that main can throw. *)
407 | Rpc.Rpc_server err ->
408 eprintf "whenjobs: rpc error: %s\n" (Rpc.string_of_server_error err);
409 suggest_check_server_logs ();
412 eprintf "whenjobs: error: %s\n" msg;
414 | Invalid_argument msg ->
415 eprintf "whenjobs: invalid argument: %s\n" msg;
418 eprintf "whenjobs: error: %s\n" (Printexc.to_string exn);