open Printf
+let kvpair s =
+ match Stringext.String.split ~limit:2 '=' s with
+ | k :: v :: [] -> Some (k, v)
+ | _ -> None
+
+let valid_kvpairs args =
+ List.rev (List.fold_left (fun acc x -> match kvpair x with Some x -> x :: acc | None -> acc) [] args)
+
let _ =
if Array.length Sys.argv < 3 then (
eprintf "usage: %s <uuid> <cmd> [cmd args]\n" Sys.argv.(0);
);
let uuid = Sys.argv.(1) in
- let cmds = Array.sub Sys.argv 2 (Array.length Sys.argv - 2) in
- let s = (String.concat " " (Array.to_list cmds)) in
+ let cmds = Array.to_list (Array.sub Sys.argv 2 (Array.length Sys.argv - 2)) in
+ let query =
+ match cmds with
+ | cmd :: args -> cmd, valid_kvpairs args
+ | [] -> eprintf "error: missing query\n"; exit 1
+ in
+
try
- match Xenvmlib.request ~timeout:60.0 uuid s with
+ match Xenvmlib.request ~timeout:60.0 uuid query with
| Xenvmlib.Ok -> ()
| Xenvmlib.Timeout -> eprintf "timeout\n"; exit 1
| Xenvmlib.Error error -> eprintf "error: %s\n" error; exit 1
* GNU Lesser General Public License for more details.
*)
-open Stringext
+open Pervasiveext
+(*open Stringext*)
+type query = (string * ((string * string) list))
type answer = Ok | Error of string | Msg of string | Unknown of string | Timeout
-let string_of_answer answer =
- match answer with
- | Ok -> "CMD_OK"
- | Timeout -> "CMD_TIMEOUT"
- | Error s -> "CMD_ERROR " ^ s
- | Msg s -> "CMD_MSG " ^ s
- | Unknown s -> s
-
-let answer_of_string s =
- if s = "CMD_OK" then
- Ok
- else if s = "CMD_TIMEOUT" then
- Timeout
- else if String.startswith "CMD_ERROR " s then
- Error (String.sub s 10 (String.length s - 10))
- else if String.startswith "CMD_MSG " s then
- Msg (String.sub s 8 (String.length s - 8))
- else
- Unknown s
-
+exception Partial_Write
+exception Partial_Read
exception Write_timeout
+exception Read_timeout
exception Connect_refused of string
let path_of_socket id =
raise Write_timeout;
let wr = Unix.write fd buf (len - !left) (!left) in
+ if wr = 0 then
+ raise Partial_Write;
left := !left - wr
done
-let doread ?(timeout=(-1.0)) fd =
- let b = Buffer.create 128 in
- let buf = String.create 128 in
+let doread_eof ?(timeout=(-1.0)) fd =
+ let buf = Buffer.create 1024 in
+ let s = String.create 1024 in
let quit = ref false in
while not !quit
do
try Unix.select [ fd ] [] [] timeout
with _ -> [], [], []
in
- if not (List.mem fd r) then (
- Buffer.clear b;
- Buffer.add_string b (string_of_answer Timeout);
- quit := true;
- ) else (
- let rd = Unix.read fd buf 0 128 in
- if rd = 0 then
- quit := true
- else
- Buffer.add_substring b buf 0 rd
+ if not (List.mem fd r) then
+ raise Read_timeout;
+
+ let rd = Unix.read fd s 0 (String.length s) in
+ if rd = 0 then
+ quit := true
+ else (
+ Buffer.add_substring buf s 0 rd
)
done;
- Buffer.contents b
+ Buffer.contents buf
-(** request s from a running xenvm identified by id (uuid or name) *)
-let request ?timeout id s =
- let fd = connect id in
+let doread ?(timeout=(-1.0)) fd len =
+ let buf = String.create len in
+ let offset = ref 0 in
+ let left = ref len in
+ while !left > 0
+ do
+ let r, _, _ =
+ try Unix.select [ fd ] [] [] timeout
+ with _ -> [], [], []
+ in
+ if not (List.mem fd r) then
+ raise Read_timeout;
+
+ let rd = Unix.read fd buf !offset !left in
+ if rd = 0 then
+ raise Partial_Read;
+ offset := !offset + rd;
+ left := !left - rd;
+ done;
+ buf
+
+let send_query ?timeout fd query =
+ let query_name, query_params = query in
+ let jparams = Json.Object (Array.of_list (List.map (fun (k, v) -> k, Json.String v) query_params)) in
+ let req = {
+ Jsonrpc.request_id = Some (Json.Int 1L);
+ Jsonrpc.method_name = query_name;
+ Jsonrpc.params = jparams;
+ } in
+ let s = Jsonrpc.request_to_string req in
+ let len = String.length s in
+ let header = String.create 4 in
- dowrite ?timeout fd (s ^ "!");
- let answer = doread ?timeout fd in
- Unix.close fd;
+ (* make header *)
+ let char_of_int len shift = Char.chr ((len lsr shift) land 0xff) in
+ header.[0] <- char_of_int len 24;
+ header.[1] <- char_of_int len 16;
+ header.[2] <- char_of_int len 8;
+ header.[3] <- char_of_int len 0;
- answer_of_string answer
+ dowrite ?timeout fd header;
+ dowrite ?timeout fd s;
+ ()
+
+let recv_resp ?timeout fd =
+ let s = doread_eof ?timeout fd in
+ let jsonrpc = Jsonrpc.response_of_string s in
+ match jsonrpc.Jsonrpc.response with
+ | Jsonrpc.Result r -> (
+ match r with
+ | Json.Null -> Ok
+ | Json.String m -> Msg m
+ | _ -> Error "unknown return value in success"
+ )
+ | Jsonrpc.Error (code, message, optdata) ->
+ Error message
+
+let request ?timeout id query =
+ let fd = connect id in
+ finally (fun () ->
+ send_query ?timeout fd query;
+ recv_resp ?timeout fd
+ ) (fun () -> Unix.close fd)
let code_ping = 0x0000
let code_hup = 0x0001