@@ -398,19 +398,24 @@ let is_subject_suspended ~__context ~cache subject_identifier =
398398 debug " Subject identifier %s is suspended" subject_identifier ;
399399 (is_suspended, subject_name)
400400
401+ let reusable_pool_session = Atomic. make Ref. null
402+
401403let destroy_db_session ~__context ~self =
402404 Context. with_tracing ~__context __FUNCTION__ @@ fun __context ->
403- Xapi_event. on_session_deleted self ;
404- (* unregister from the event system *)
405- (* This info line is important for tracking, auditability and client accountability purposes on XenServer *)
406- (* Never print the session id nor uuid: they are secret values that should be known only to the user that *)
407- (* logged in. Instead, we print a non-invertible hash as the tracking id for the session id *)
408- (* see also task creation in context.ml *)
409- (* CP-982: create tracking id in log files to link username to actions *)
410- info " Session.destroy %s" (trackid self) ;
411- Rbac_audit. session_destroy ~__context ~session_id: self ;
412- (try Db.Session. destroy ~__context ~self with _ -> () ) ;
413- Rbac. destroy_session_permissions_tbl ~session_id: self
405+ if self <> Atomic. get reusable_pool_session then (
406+ Xapi_event. on_session_deleted self ;
407+ (* unregister from the event system *)
408+ (* This info line is important for tracking, auditability and client accountability purposes on XenServer *)
409+ (* Never print the session id nor uuid: they are secret values that should be known only to the user that *)
410+ (* logged in. Instead, we print a non-invertible hash as the tracking id for the session id *)
411+ (* see also task creation in context.ml *)
412+ (* CP-982: create tracking id in log files to link username to actions *)
413+ info " Session.destroy %s" (trackid self) ;
414+ Rbac_audit. session_destroy ~__context ~session_id: self ;
415+ (try Db.Session. destroy ~__context ~self with _ -> () ) ;
416+ Rbac. destroy_session_permissions_tbl ~session_id: self
417+ ) else
418+ info " Skipping Session.destroy for reusable pool session %s" (trackid self)
414419
415420(* CP-703: ensure that activate sessions are invalidated in a bounded time *)
416421(* in response to external authentication/directory services updates, such as *)
@@ -610,8 +615,8 @@ let revalidate_all_sessions ~__context =
610615 debug " Unexpected exception while revalidating external sessions: %s"
611616 (ExnHelper. string_of_exn e)
612617
613- let login_no_password_common ~__context ~uname ~originator ~host ~ pool
614- ~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
618+ let login_no_password_common_create_session ~__context ~uname ~originator ~host
619+ ~pool ~ is_local_superuser ~subject ~auth_user_sid ~auth_user_name
615620 ~rbac_permissions ~db_ref ~client_certificate =
616621 Context. with_tracing ~originator ~__context __FUNCTION__ @@ fun __context ->
617622 let create_session () =
@@ -661,6 +666,55 @@ let login_no_password_common ~__context ~uname ~originator ~host ~pool
661666 ignore (Client.Pool. get_all ~rpc ~session_id ) ;
662667 session_id
663668
669+ let login_no_password_common ~__context ~uname ~originator ~host ~pool
670+ ~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
671+ ~rbac_permissions ~db_ref ~client_certificate =
672+ Context. with_tracing ~originator ~__context __FUNCTION__ @@ fun __context ->
673+ let is_valid_session session_id =
674+ if session_id = Ref. null then
675+ false
676+ else
677+ try
678+ (* Call an API function to check the session is still valid *)
679+ let rpc = Helpers. make_rpc ~__context in
680+ ignore (Client.Pool. get_all ~rpc ~session_id ) ;
681+ true
682+ with Api_errors. Server_error (err , _ ) ->
683+ debug " %s: Invalid session: %s" __FUNCTION__ err ;
684+ false
685+ in
686+ let create_session () =
687+ login_no_password_common_create_session ~__context ~uname ~originator ~host
688+ ~pool ~is_local_superuser ~subject ~auth_user_sid ~auth_user_name
689+ ~rbac_permissions ~db_ref ~client_certificate
690+ in
691+ let rec get_session () =
692+ let session = Atomic. get reusable_pool_session in
693+ if is_valid_session session then (
694+ (* Check if the session changed during validation.
695+ Use our version regardless to avoid being stuck in a loop of session creation *)
696+ if Atomic. get reusable_pool_session <> session then
697+ debug " reusable_pool_session has changed, using the original anyway" ;
698+ session
699+ ) else
700+ let new_session = create_session () in
701+ if Atomic. compare_and_set reusable_pool_session session new_session then
702+ new_session
703+ else (
704+ (* someone else raced with us and created a session, destroy ours and attempt to use theirs *)
705+ destroy_db_session ~__context ~self: new_session ;
706+ (get_session [@ tailcall]) ()
707+ )
708+ in
709+ if
710+ (originator, pool, is_local_superuser, uname)
711+ = (xapi_internal_originator, true , true , None )
712+ && ! Xapi_globs. reuse_pool_sessions
713+ then
714+ get_session ()
715+ else
716+ create_session ()
717+
664718(* XXX: only used internally by the code which grants the guest access to the API.
665719 Needs to be protected by a proper access control system *)
666720let login_no_password ~__context ~uname ~host ~pool ~is_local_superuser ~subject
0 commit comments