PageRenderTime 42ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ocaml/xapi/console.ml

https://github.com/jamesbulpin/xen-api
OCaml | 135 lines | 89 code | 18 blank | 28 comment | 9 complexity | d5a6b452e2fc27e4199dafd6b338f0ee MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. (*
  2. * Copyright (C) 2006-2009 Citrix Systems Inc.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published
  6. * by the Free Software Foundation; version 2.1 only. with the special
  7. * exception on linking described in file LICENSE.
  8. *
  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 Lesser General Public License for more details.
  13. *)
  14. (*
  15. * HTTP handler for connecting to a VM's VNC console.
  16. * Handler should be passed a reference to either a VM (in which case the 'default' VNC
  17. * console will be chosen) or a console object.
  18. *)
  19. open Http
  20. open Xenops_helpers
  21. module D = Debug.Debugger(struct let name="console" end)
  22. open D
  23. exception Failure
  24. let real_proxy __context console s =
  25. let vm = Db.Console.get_VM __context console in
  26. let domid = Helpers.domid_of_vm __context vm in
  27. let vnc_port =
  28. try
  29. int_of_string (with_xc_and_xs (fun xc xs -> xs.Xs.read (Device.vnc_port_path xc xs domid)))
  30. with Xb.Noent ->
  31. error "Failed to read vnc-port from xenstore";
  32. raise Failure in
  33. debug "VM %s has console on port %d" (Ref.string_of vm) vnc_port;
  34. begin try
  35. let vnc_sock = Unixext.open_connection_fd "127.0.0.1" vnc_port in
  36. (* Unixext.proxy closes fds itself so we must dup here *)
  37. let s' = Unix.dup s in
  38. debug "Connected; running proxy (between fds: %d and %d)"
  39. (Unixext.int_of_file_descr vnc_sock) (Unixext.int_of_file_descr s');
  40. Unixext.proxy vnc_sock s';
  41. debug "Proxy exited"
  42. with
  43. exn -> debug "error: %s" (ExnHelper.string_of_exn exn)
  44. end
  45. let fake_proxy __context console s =
  46. Rfb_randomtest.server s
  47. let default_console_of_vm ~__context ~self =
  48. try
  49. let consoles = Db.VM.get_consoles ~__context ~self in
  50. let protocols = List.map (fun self -> Db.Console.get_protocol ~__context ~self) consoles in
  51. fst (List.find (fun (_, p) -> p = `rfb) (List.combine consoles protocols))
  52. with _ ->
  53. error "Failed to find default VNC console for VM";
  54. raise Failure
  55. let console_of_request __context req =
  56. (* First check the request looks valid *)
  57. if not(List.mem_assoc "ref" req.query) && not(List.mem_assoc "uuid" req.query) then begin
  58. error "HTTP request for console forwarding lacked 'ref' or 'uuid' parameter";
  59. raise Failure
  60. end;
  61. let _ref =
  62. if List.mem_assoc "uuid" req.query
  63. then
  64. let uuid = List.assoc "uuid" req.query in
  65. (try Ref.string_of(Db.VM.get_by_uuid ~__context ~uuid)
  66. with _ -> Ref.string_of(Db.Console.get_by_uuid ~__context ~uuid))
  67. else List.assoc "ref" req.query in
  68. (* The _ref may be either a VM ref in which case we look for a
  69. default VNC console or it may be a console ref in which case we
  70. go for that. *)
  71. let db = Context.database_of __context in
  72. let is_vm, is_console =
  73. let module DB = (val (Db_cache.get db) : Db_interface.DB_ACCESS) in
  74. match DB.get_table_from_ref db _ref with
  75. | Some c when c = Db_names.vm -> true, false
  76. | Some c when c = Db_names.console -> false, true
  77. | _ ->
  78. error "%s is neither a VM ref or a console ref" _ref;
  79. raise Failure in
  80. if is_vm then default_console_of_vm ~__context ~self:(Ref.of_string _ref) else (Ref.of_string _ref)
  81. let rbac_check_for_control_domain __context (req:request) console_id permission =
  82. let is_control_domain =
  83. let vm_id = Db.Console.get_VM ~__context ~self:console_id in
  84. Db.VM.get_is_control_domain ~__context ~self:vm_id
  85. in
  86. if is_control_domain then
  87. let extra_dmsg = Printf.sprintf "for host console %s" (Ref.string_of console_id) in
  88. let session_id = Xapi_http.get_session_id req in
  89. Rbac.check_with_new_task ~extra_dmsg session_id permission ~fn:Rbac.nofn
  90. ~args:(Xapi_http.rbac_audit_params_of req)
  91. let check_vm_is_running_here __context console =
  92. let vm = Db.Console.get_VM ~__context ~self:console in
  93. if Db.VM.get_power_state ~__context ~self:vm <> `Running then begin
  94. error "VM %s (Console %s) has power_state <> Running" (Ref.string_of vm) (Ref.string_of console);
  95. raise Failure
  96. end;
  97. let localhost = Helpers.get_localhost ~__context in
  98. let resident_on = Db.VM.get_resident_on ~__context ~self:vm in
  99. if resident_on <> localhost then begin
  100. error "VM %s (Console %s) has resident_on = %s <> localhost" (Ref.string_of vm) (Ref.string_of console) (Ref.string_of resident_on);
  101. raise Failure
  102. end
  103. (* GET /console_uri?ref=.....
  104. Cookie: <session id> *)
  105. let handler proxy_fn (req: request) s =
  106. req.close <- true;
  107. Xapi_http.with_context "Connection to VM console" req s
  108. (fun __context ->
  109. let console = console_of_request __context req in
  110. (* only sessions with 'http/connect_console/host_console' permission *)
  111. (* can access dom0 host consoles *)
  112. rbac_check_for_control_domain __context req console
  113. Rbac_static.permission_http_connect_console_host_console.Db_actions.role_name_label;
  114. (* Check VM is actually running locally *)
  115. check_vm_is_running_here __context console;
  116. Http_svr.headers s (Http.http_200_ok ());
  117. proxy_fn __context console s)