aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ocaml/libs/xs/xs.ml
blob: 57575710729bc036a4dd951a2227374595296024 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
(*
 * Copyright (C) 2006-2007 XenSource Ltd.
 * Copyright (C) 2008      Citrix Ltd.
 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 only. with the special
 * exception on linking described in file LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *)

type perms = Xsraw.perms
type con = Xsraw.con
type domid = int

type xsh =
{
	con: con;
	debug: string list -> string;
	directory: string -> string list;
	read: string -> string;
	readv: string -> string list -> string list;
	write: string -> string -> unit;
	writev: string -> (string * string) list -> unit;
	mkdir: string -> unit;
	rm: string -> unit;
	getperms: string -> perms;
	setperms: string -> perms -> unit;
	setpermsv: string -> string list -> perms -> unit;
	introduce: domid -> nativeint -> int -> unit;
	release: domid -> unit;
	resume: domid -> unit;
	getdomainpath: domid -> string;
	watch: string -> string -> unit;
	unwatch: string -> string -> unit;
}

let get_operations con = {
	con = con;
	debug = (fun commands -> Xsraw.debug commands con);
	directory = (fun path -> Xsraw.directory 0 path con);
	read = (fun path -> Xsraw.read 0 path con);
	readv = (fun dir vec -> Xsraw.readv 0 dir vec con);
	write = (fun path value -> Xsraw.write 0 path value con);
	writev = (fun dir vec -> Xsraw.writev 0 dir vec con);
	mkdir = (fun path -> Xsraw.mkdir 0 path con);
	rm = (fun path -> Xsraw.rm 0 path con);
	getperms = (fun path -> Xsraw.getperms 0 path con);
	setperms = (fun path perms -> Xsraw.setperms 0 path perms con);
	setpermsv = (fun dir vec perms -> Xsraw.setpermsv 0 dir vec perms con);
	introduce = (fun id mfn port -> Xsraw.introduce id mfn port con);
	release = (fun id -> Xsraw.release id con);
	resume = (fun id -> Xsraw.resume id con);
	getdomainpath = (fun id -> Xsraw.getdomainpath id con);
	watch = (fun path data -> Xsraw.watch path data con);
	unwatch = (fun path data -> Xsraw.unwatch path data con);
}

let transaction xsh = Xst.transaction xsh.con

let has_watchevents xsh = Xsraw.has_watchevents xsh.con
let get_watchevent xsh = Xsraw.get_watchevent xsh.con

let read_watchevent xsh = Xsraw.read_watchevent xsh.con

let make fd = get_operations (Xsraw.open_fd fd)
let get_fd xsh = Xenbus.Xb.get_fd xsh.con.Xsraw.xb

exception Timeout

(* Should never be thrown, indicates a bug in the read_watchevent_timetout function *)
exception Timeout_with_nonempty_queue

(* Just in case we screw up: poll the callback every couple of seconds rather
   than wait for the whole timeout period *)
let max_blocking_time = 5. (* seconds *)

let read_watchevent_timeout xsh timeout callback =
	let start_time = Unix.gettimeofday () in
	let end_time = start_time +. timeout in

	let left = ref timeout in

	(* Returns true if a watch event in the queue satisfied us *)
	let process_queued_events () = 
		let success = ref false in
		while Xsraw.has_watchevents xsh.con && not(!success)
		do
			success := callback (Xsraw.get_watchevent xsh.con)
		done;
		!success in
	(* Returns true if a watch event read from the socket satisfied us *)
	let process_incoming_event () = 
		let fd = get_fd xsh in
		let r, _, _ = Unix.select [ fd ] [] [] (min max_blocking_time !left) in

		(* If data is available for reading then read it *)
		if r = []
		then false (* timeout, either a max_blocking_time or global *)
		else callback (Xsraw.read_watchevent xsh.con) in

	let success = ref false in
	while !left > 0. && not(!success)
	do
		(* NB the 'callback' might call back into Xs functions
		   and as a side-effect, watches might be queued. Hence
		   we must process the queue on every loop iteration *)

		(* First process all queued watch events *)
		if not(!success)
		then success := process_queued_events ();
		(* Then block for one more watch event *)
		if not(!success)
		then success := process_incoming_event ();
		(* Just in case our callback caused events to be queued
		   and this is our last time round the loop: this prevents
		   us throwing the Timeout_with_nonempty_queue spuriously *)
		if not(!success)
		then success := process_queued_events ();

		(* Update the time left *)
		let current_time = Unix.gettimeofday () in
		left := end_time -. current_time
	done;
	if not(!success) then begin
		(* Sanity check: it should be impossible for any
		   events to be queued here *)
		if Xsraw.has_watchevents xsh.con
		then raise Timeout_with_nonempty_queue
		else raise Timeout
	end


let monitor_paths xsh l time callback =
	let unwatch () =
		List.iter (fun (w,v) -> try xsh.unwatch w v with _ -> ()) l in
	List.iter (fun (w,v) -> xsh.watch w v) l;
	begin try
		read_watchevent_timeout xsh time callback;
	with
		exn -> unwatch (); raise exn;
	end;
	unwatch ()

let daemon_socket = "/var/run/xenstored/socket"

(** Throws this rather than a miscellaneous Unix.connect failed *)
exception Failed_to_connect

let daemon_open () =
	try
		let sockaddr = Unix.ADDR_UNIX(daemon_socket) in
		let sock = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in
		Unix.connect sock sockaddr;
		Unix.set_close_on_exec sock;
		make sock
	with _ -> raise Failed_to_connect

let domain_open () =
	let path = "/proc/xen/xenbus" in
	let fd = Unix.openfile path [ Unix.O_RDWR ] 0o550 in
	Unix.set_close_on_exec fd;
	make fd

let close xsh = Xsraw.close xsh.con