Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2022 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #include "SessionServerManager.h"
21 : #include "IApplicationManagementServer.h"
22 : #include "IIpcFactory.h"
23 : #include "ISessionManagementServer.h"
24 : #include "RialtoServerLogging.h"
25 :
26 : #include <algorithm>
27 : #include <stdexcept>
28 : #include <utility>
29 :
30 : namespace
31 : {
32 5 : inline bool isNumber(const std::string &text)
33 : {
34 10 : return std::find_if(text.begin(), text.end(), [](const auto &letter) { return !std::isdigit(letter); }) == text.end();
35 : }
36 : } // namespace
37 :
38 : namespace firebolt::rialto::server::service
39 : {
40 28 : SessionServerManager::SessionServerManager(const ipc::IIpcFactory &ipcFactory, IPlaybackService &playbackService,
41 : ICdmService &cdmService, IControlService &controlService,
42 28 : std::unique_ptr<IHeartbeatProcedureFactory> &&heartbeatProcedureFactory)
43 28 : : m_playbackService{playbackService}, m_cdmService{cdmService}, m_controlService{controlService},
44 28 : m_heartbeatProcedureFactory{std::move(heartbeatProcedureFactory)},
45 28 : m_applicationManagementServer{ipcFactory.createApplicationManagementServer(*this)},
46 28 : m_sessionManagementServer{ipcFactory.createSessionManagementServer(playbackService, cdmService, controlService)},
47 56 : m_isServiceRunning{true}, m_currentState{common::SessionServerState::UNINITIALIZED}
48 : {
49 28 : RIALTO_SERVER_LOG_INFO("Starting Rialto Server Service");
50 : }
51 :
52 56 : SessionServerManager::~SessionServerManager()
53 : {
54 28 : RIALTO_SERVER_LOG_INFO("Stopping Rialto Server Service");
55 :
56 : // The following reset() will ensure that the thread ApplicationManagementServer::m_ipcServerThread
57 : // isn't currently calling any methods within this class (while it is being destructed)
58 : // Particularly, the mentioned thread is responsible for calling the method
59 : // SessionServerManager::teardownService() which then triggers a call to this destuctor
60 : // after it calls stopService()
61 28 : m_applicationManagementServer.reset();
62 :
63 28 : stopService();
64 56 : }
65 :
66 7 : bool SessionServerManager::initialize(int argc, char *argv[])
67 : try
68 : {
69 7 : if (argc != 2)
70 : {
71 2 : RIALTO_SERVER_LOG_ERROR("Wrong number of arguments. Rialto Server Service will close now.");
72 2 : return false;
73 : }
74 10 : std::string socketStr{argv[1]};
75 5 : if (!isNumber(socketStr))
76 : {
77 1 : RIALTO_SERVER_LOG_ERROR("Rialto App Management socket is not a number.");
78 1 : return false;
79 : }
80 4 : if (!m_applicationManagementServer->initialize(std::stoi(socketStr)))
81 : {
82 1 : RIALTO_SERVER_LOG_ERROR("Initialization of Application Management server failed.");
83 1 : return false;
84 : }
85 2 : m_applicationManagementServer->start();
86 2 : return m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::UNINITIALIZED);
87 5 : }
88 1 : catch (const std::exception &e)
89 : {
90 1 : RIALTO_SERVER_LOG_ERROR("Exception caught during service initialization: %s", e.what());
91 1 : return false;
92 : }
93 :
94 1 : void SessionServerManager::startService()
95 : {
96 1 : std::unique_lock<std::mutex> lock{m_serviceMutex};
97 3 : m_serviceCv.wait(lock, [this]() { return !m_isServiceRunning; });
98 1 : }
99 :
100 32 : void SessionServerManager::stopService()
101 : {
102 32 : std::unique_lock<std::mutex> lock{m_serviceMutex};
103 32 : if (m_isServiceRunning)
104 : {
105 28 : m_isServiceRunning = false;
106 28 : m_serviceCv.notify_one();
107 : }
108 32 : }
109 :
110 3 : bool SessionServerManager::configureIpc(const std::string &socketName, unsigned int socketPermissions,
111 : const std::string &socketOwner, const std::string &socketGroup)
112 : {
113 3 : if (!m_sessionManagementServer->initialize(socketName, socketPermissions, socketOwner, socketGroup))
114 : {
115 1 : RIALTO_SERVER_LOG_ERROR("configureIpc failed - SessionManagementServer failed to initialize");
116 1 : return false;
117 : }
118 2 : return true;
119 : }
120 :
121 2 : bool SessionServerManager::configureIpc(int32_t socketFd)
122 : {
123 2 : if (!m_sessionManagementServer->initialize(socketFd))
124 : {
125 1 : RIALTO_SERVER_LOG_ERROR("configureIpc failed - SessionManagementServer failed to initialize");
126 1 : return false;
127 : }
128 1 : return true;
129 : }
130 :
131 3 : bool SessionServerManager::configureServices(const common::SessionServerState &state,
132 : const common::MaxResourceCapabilitites &maxResource,
133 : const std::string &clientDisplayName, const std::string &appName)
134 : {
135 3 : m_sessionManagementServer->start();
136 3 : m_playbackService.setMaxPlaybacks(maxResource.maxPlaybacks);
137 3 : m_playbackService.setMaxWebAudioPlayers(maxResource.maxWebAudioPlayers);
138 3 : m_playbackService.setClientDisplayName(clientDisplayName);
139 3 : m_playbackService.setResourceManagerAppName(appName);
140 3 : return setState(state);
141 : }
142 :
143 22 : bool SessionServerManager::setState(const common::SessionServerState &state)
144 : {
145 22 : switch (state)
146 : {
147 7 : case common::SessionServerState::ACTIVE:
148 : {
149 7 : return switchToActive();
150 : }
151 8 : case common::SessionServerState::INACTIVE:
152 : {
153 8 : return switchToInactive();
154 : }
155 5 : case common::SessionServerState::NOT_RUNNING:
156 : case common::SessionServerState::SUSPENDED:
157 : {
158 5 : return teardownService(state);
159 : }
160 2 : default:
161 : {
162 2 : RIALTO_SERVER_LOG_ERROR("SetState failed - unsupported state");
163 2 : m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::ERROR);
164 : }
165 : }
166 2 : return false;
167 : }
168 :
169 1 : void SessionServerManager::setLogLevels(RIALTO_DEBUG_LEVEL defaultLogLevels, RIALTO_DEBUG_LEVEL clientLogLevels,
170 : RIALTO_DEBUG_LEVEL sessionServerLogLevels, RIALTO_DEBUG_LEVEL ipcLogLevels,
171 : RIALTO_DEBUG_LEVEL serverManagerLogLevels, RIALTO_DEBUG_LEVEL commonLogLevels)
172 : {
173 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_DEFAULT, defaultLogLevels);
174 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_CLIENT, clientLogLevels);
175 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_SERVER, sessionServerLogLevels);
176 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_IPC, ipcLogLevels);
177 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_SERVER_MANAGER, serverManagerLogLevels);
178 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_COMMON, commonLogLevels);
179 1 : m_sessionManagementServer->setLogLevels(defaultLogLevels, clientLogLevels, ipcLogLevels, commonLogLevels);
180 : }
181 :
182 2 : bool SessionServerManager::ping(std::int32_t id, const std::shared_ptr<IAckSender> &ackSender)
183 : {
184 2 : auto heartbeatProcedure{m_heartbeatProcedureFactory->createHeartbeatProcedure(ackSender, id)};
185 :
186 : // Check all rialto server internal threads
187 2 : m_cdmService.ping(heartbeatProcedure);
188 2 : m_playbackService.ping(heartbeatProcedure);
189 :
190 : // Check all Rialto Clients
191 4 : return m_controlService.ping(heartbeatProcedure);
192 2 : }
193 :
194 7 : bool SessionServerManager::switchToActive()
195 : {
196 7 : const auto currentState{m_currentState.load()};
197 7 : if (currentState == common::SessionServerState::ACTIVE)
198 : {
199 1 : RIALTO_SERVER_LOG_DEBUG("Session server already in Active state.");
200 1 : return true;
201 : }
202 6 : if (!m_playbackService.switchToActive())
203 : {
204 1 : RIALTO_SERVER_LOG_ERROR("Player service failed to switch to active state");
205 1 : return false;
206 : }
207 5 : if (!m_cdmService.switchToActive())
208 : {
209 1 : RIALTO_SERVER_LOG_ERROR("Cdm service failed to switch to active state");
210 1 : m_playbackService.switchToInactive();
211 1 : return false;
212 : }
213 4 : if (m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::ACTIVE))
214 : {
215 3 : m_controlService.setApplicationState(ApplicationState::RUNNING);
216 3 : m_sessionManagementServer->notifyApplicationStateChanged(ApplicationState::RUNNING);
217 3 : m_currentState.store(common::SessionServerState::ACTIVE);
218 3 : RIALTO_SERVER_LOG_MIL("RialtoServer state is ACTIVE now");
219 3 : return true;
220 : }
221 1 : m_playbackService.switchToInactive();
222 1 : m_cdmService.switchToInactive();
223 1 : return false;
224 : }
225 :
226 8 : bool SessionServerManager::switchToInactive()
227 : {
228 8 : const auto currentState{m_currentState.load()};
229 8 : if (currentState == common::SessionServerState::INACTIVE)
230 : {
231 1 : RIALTO_SERVER_LOG_DEBUG("Session server already in Inactive state.");
232 1 : return true;
233 : }
234 7 : m_playbackService.switchToInactive();
235 7 : m_cdmService.switchToInactive();
236 : // Record INACTIVE memory snapshot immediately after resource teardown,
237 : // before the manager ACK — ensures we capture it even if the socket breaks.
238 7 : m_sessionManagementServer->notifyApplicationStateChanged(ApplicationState::INACTIVE);
239 7 : if (m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::INACTIVE))
240 : {
241 4 : m_controlService.setApplicationState(ApplicationState::INACTIVE);
242 4 : m_currentState.store(common::SessionServerState::INACTIVE);
243 4 : RIALTO_SERVER_LOG_MIL("RialtoServer state is INACTIVE now");
244 4 : return true;
245 : }
246 3 : if (currentState == common::SessionServerState::ACTIVE)
247 : {
248 1 : if (!m_playbackService.switchToActive())
249 : {
250 1 : RIALTO_SERVER_LOG_WARN("Player service failed to switch to active state");
251 : }
252 1 : if (!m_cdmService.switchToActive())
253 : {
254 1 : RIALTO_SERVER_LOG_WARN("Cdm service failed to switch to active state");
255 : }
256 : }
257 3 : return false;
258 : }
259 :
260 5 : bool SessionServerManager::teardownService(const common::SessionServerState &state)
261 : {
262 5 : RIALTO_SERVER_LOG_MIL("RialtoServer tear down requested");
263 9 : if (m_currentState.load() == common::SessionServerState::NOT_RUNNING ||
264 4 : m_currentState.load() == common::SessionServerState::SUSPENDED)
265 : {
266 1 : RIALTO_SERVER_LOG_DEBUG("Session server already in NotRunning or Suspended state.");
267 1 : return true;
268 : }
269 : // Free resources before sending notification to ServerManager
270 4 : m_playbackService.switchToInactive();
271 4 : m_cdmService.switchToInactive();
272 4 : m_controlService.setApplicationState(ApplicationState::UNKNOWN);
273 :
274 4 : bool result{true};
275 4 : if (m_applicationManagementServer->sendStateChangedEvent(state))
276 : {
277 3 : m_currentState.store(state);
278 : }
279 : else
280 : {
281 1 : result = false;
282 : }
283 :
284 : // stopService() needs to be the last command of this method.
285 : // It triggers destruction of SessionServerManager and therefore
286 : // some member variables may not be valid after this command
287 4 : stopService(); // This HAS TO BE LAST (see above)
288 4 : return result;
289 : }
290 : } // namespace firebolt::rialto::server::service
|