1(* 2 * Copyright (C) 2006-2007 XenSource Ltd. 3 * Copyright (C) 2008 Citrix Ltd. 4 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published 8 * by the Free Software Foundation; version 2.1 only. with the special 9 * exception on linking described in file LICENSE. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 *) 16 17module Op = struct include Op end 18module Packet = struct include Packet end 19 20exception End_of_file 21exception Eagain 22exception Noent 23exception Invalid 24exception Reconnect 25 26let _ = 27 Callback.register_exception "Xb.Reconnect" Reconnect 28 29type backend_mmap = 30{ 31 mmap: Xenmmap.mmap_interface; (* mmaped interface = xs_ring *) 32 eventchn_notify: unit -> unit; (* function to notify through eventchn *) 33 mutable work_again: bool; 34} 35 36type backend_fd = 37{ 38 fd: Unix.file_descr; 39} 40 41type backend = Fd of backend_fd | Xenmmap of backend_mmap 42 43type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes 44 45type t = 46{ 47 backend: backend; 48 pkt_in: Packet.t Queue.t; 49 pkt_out: Packet.t Queue.t; 50 mutable partial_in: partial_buf; 51 mutable partial_out: string; 52} 53 54let init_partial_in () = NoHdr 55 (Partial.header_size (), Bytes.make (Partial.header_size()) '\000') 56 57let reconnect t = match t.backend with 58 | Fd _ -> 59 (* should never happen, so close the connection *) 60 raise End_of_file 61 | Xenmmap backend -> 62 Xs_ring.close backend.mmap; 63 backend.eventchn_notify (); 64 (* Clear our old connection state *) 65 Queue.clear t.pkt_in; 66 Queue.clear t.pkt_out; 67 t.partial_in <- init_partial_in (); 68 t.partial_out <- "" 69 70let queue con pkt = Queue.push pkt con.pkt_out 71 72let read_fd back _con b len = 73 let rd = Unix.read back.fd b 0 len in 74 if rd = 0 then 75 raise End_of_file; 76 rd 77 78let read_mmap back _con b len = 79 let s = Bytes.make len '\000' in 80 let rd = Xs_ring.read back.mmap s len in 81 Bytes.blit s 0 b 0 rd; 82 back.work_again <- (rd > 0); 83 if rd > 0 then 84 back.eventchn_notify (); 85 rd 86 87let read con b len = 88 match con.backend with 89 | Fd backfd -> read_fd backfd con b len 90 | Xenmmap backmmap -> read_mmap backmmap con b len 91 92let write_fd back _con b len = 93 Unix.write_substring back.fd b 0 len 94 95let write_mmap back _con s len = 96 let ws = Xs_ring.write_substring back.mmap s len in 97 if ws > 0 then 98 back.eventchn_notify (); 99 ws 100 101let write con s len = 102 match con.backend with 103 | Fd backfd -> write_fd backfd con s len 104 | Xenmmap backmmap -> write_mmap backmmap con s len 105 106(* NB: can throw Reconnect *) 107let output con = 108 (* get the output string from a string_of(packet) or partial_out *) 109 let s = if String.length con.partial_out > 0 then 110 con.partial_out 111 else if Queue.length con.pkt_out > 0 then 112 Packet.to_string (Queue.pop con.pkt_out) 113 else 114 "" in 115 (* send data from s, and save the unsent data to partial_out *) 116 if s <> "" then ( 117 let len = String.length s in 118 let sz = write con s len in 119 let left = String.sub s sz (len - sz) in 120 con.partial_out <- left 121 ); 122 (* after sending one packet, partial is empty *) 123 con.partial_out = "" 124 125(* NB: can throw Reconnect *) 126let input con = 127 let newpacket = ref false in 128 let to_read = 129 match con.partial_in with 130 | HaveHdr partial_pkt -> Partial.to_complete partial_pkt 131 | NoHdr (i, _) -> i in 132 133 (* try to get more data from input stream *) 134 let b = Bytes.make to_read '\000' in 135 let sz = if to_read > 0 then read con b to_read else 0 in 136 137 ( 138 match con.partial_in with 139 | HaveHdr partial_pkt -> 140 (* we complete the data *) 141 if sz > 0 then 142 Partial.append partial_pkt (Bytes.to_string b) sz; 143 if Partial.to_complete partial_pkt = 0 then ( 144 let pkt = Packet.of_partialpkt partial_pkt in 145 con.partial_in <- init_partial_in (); 146 Queue.push pkt con.pkt_in; 147 newpacket := true 148 ) 149 | NoHdr (i, buf) -> 150 (* we complete the partial header *) 151 if sz > 0 then 152 Bytes.blit b 0 buf (Partial.header_size () - i) sz; 153 con.partial_in <- if sz = i then 154 HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf) 155 ); 156 !newpacket 157 158let newcon backend = { 159 backend = backend; 160 pkt_in = Queue.create (); 161 pkt_out = Queue.create (); 162 partial_in = init_partial_in (); 163 partial_out = ""; 164 } 165 166let open_fd fd = newcon (Fd { fd = fd; }) 167 168let open_mmap mmap notifyfct = 169 (* Advertise XENSTORE_SERVER_FEATURE_RECONNECTION *) 170 Xs_ring.set_server_features mmap (Xs_ring.Server_features.singleton Xs_ring.Server_feature.Reconnection); 171 newcon (Xenmmap { 172 mmap = mmap; 173 eventchn_notify = notifyfct; 174 work_again = false; }) 175 176let close con = 177 match con.backend with 178 | Fd backend -> Unix.close backend.fd 179 | Xenmmap backend -> Xenmmap.unmap backend.mmap 180 181let is_fd con = 182 match con.backend with 183 | Fd _ -> true 184 | Xenmmap _ -> false 185 186let is_mmap con = not (is_fd con) 187 188let output_len con = Queue.length con.pkt_out 189let has_new_output con = Queue.length con.pkt_out > 0 190let has_old_output con = String.length con.partial_out > 0 191 192let has_output con = has_new_output con || has_old_output con 193 194let peek_output con = Queue.peek con.pkt_out 195 196let input_len con = Queue.length con.pkt_in 197let has_in_packet con = Queue.length con.pkt_in > 0 198let get_in_packet con = Queue.pop con.pkt_in 199let has_more_input con = 200 match con.backend with 201 | Fd _ -> false 202 | Xenmmap backend -> backend.work_again 203 204let is_selectable con = 205 match con.backend with 206 | Fd _ -> true 207 | Xenmmap _ -> false 208 209let get_fd con = 210 match con.backend with 211 | Fd backend -> backend.fd 212 | Xenmmap _ -> raise (Failure "get_fd") 213