blob: 1818bd7c73a9de3d9f204ff013ab82d659790bed [file] [log] [blame]
AppaRao Puli00840472018-10-03 19:37:46 +05301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
AppaRao Puliee853eb2020-05-29 00:53:20 +053016#include "srvcfg_manager.hpp"
17
18#include <boost/asio/spawn.hpp>
19
AppaRao Puli00840472018-10-03 19:37:46 +053020#include <fstream>
21#include <regex>
AppaRao Puli00840472018-10-03 19:37:46 +053022
Richard Marian Thomaiyar33816cf2019-06-14 13:54:50 +053023extern std::unique_ptr<boost::asio::steady_timer> timer;
AppaRao Puli00840472018-10-03 19:37:46 +053024extern std::map<std::string, std::shared_ptr<phosphor::service::ServiceConfig>>
25 srvMgrObjects;
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053026static bool updateInProgress = false;
AppaRao Puli00840472018-10-03 19:37:46 +053027
28namespace phosphor
29{
30namespace service
31{
32
AppaRao Puliee853eb2020-05-29 00:53:20 +053033static constexpr const char* overrideConfFileName = "override.conf";
AppaRao Puli00840472018-10-03 19:37:46 +053034static constexpr const size_t restartTimeout = 15; // seconds
35
AppaRao Puliee853eb2020-05-29 00:53:20 +053036static constexpr const char* systemd1UnitBasePath =
AppaRao Puli00840472018-10-03 19:37:46 +053037 "/org/freedesktop/systemd1/unit/";
AppaRao Puliee853eb2020-05-29 00:53:20 +053038static constexpr const char* systemdOverrideUnitBasePath =
AppaRao Puli00840472018-10-03 19:37:46 +053039 "/etc/systemd/system/";
40
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053041void ServiceConfig::updateSocketProperties(
AppaRao Puliee853eb2020-05-29 00:53:20 +053042 const boost::container::flat_map<std::string, VariantType>& propertyMap)
AppaRao Puli00840472018-10-03 19:37:46 +053043{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053044 auto listenIt = propertyMap.find("Listen");
45 if (listenIt != propertyMap.end())
46 {
47 auto listenVal =
48 std::get<std::vector<std::tuple<std::string, std::string>>>(
49 listenIt->second);
50 if (listenVal.size())
51 {
52 protocol = std::get<0>(listenVal[0]);
53 std::string port = std::get<1>(listenVal[0]);
54 auto tmp = std::stoul(port.substr(port.find_last_of(":") + 1),
55 nullptr, 10);
56 if (tmp > std::numeric_limits<uint16_t>::max())
57 {
58 throw std::out_of_range("Out of range");
59 }
60 portNum = tmp;
61 if (iface && iface->is_initialized())
62 {
63 internalSet = true;
64 iface->set_property(srvCfgPropPort, portNum);
65 internalSet = false;
66 }
67 }
68 }
69}
70
71void ServiceConfig::updateServiceProperties(
AppaRao Puliee853eb2020-05-29 00:53:20 +053072 const boost::container::flat_map<std::string, VariantType>& propertyMap)
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053073{
74 auto stateIt = propertyMap.find("UnitFileState");
75 if (stateIt != propertyMap.end())
76 {
77 stateValue = std::get<std::string>(stateIt->second);
78 unitEnabledState = unitMaskedState = false;
79 if (stateValue == stateMasked)
80 {
81 unitMaskedState = true;
82 }
83 else if (stateValue == stateEnabled)
84 {
85 unitEnabledState = true;
86 }
87 if (iface && iface->is_initialized())
88 {
89 internalSet = true;
90 iface->set_property(srvCfgPropMasked, unitMaskedState);
91 iface->set_property(srvCfgPropEnabled, unitEnabledState);
92 internalSet = false;
93 }
94 }
95 auto subStateIt = propertyMap.find("SubState");
96 if (subStateIt != propertyMap.end())
97 {
98 subStateValue = std::get<std::string>(subStateIt->second);
99 if (subStateValue == subStateRunning)
100 {
101 unitRunningState = true;
102 }
103 if (iface && iface->is_initialized())
104 {
105 internalSet = true;
106 iface->set_property(srvCfgPropRunning, unitRunningState);
107 internalSet = false;
108 }
109 }
110}
111
112void ServiceConfig::queryAndUpdateProperties()
113{
AppaRao Puli00840472018-10-03 19:37:46 +0530114 conn->async_method_call(
115 [this](boost::system::error_code ec,
AppaRao Puliee853eb2020-05-29 00:53:20 +0530116 const boost::container::flat_map<std::string, VariantType>&
117 propertyMap) {
AppaRao Puli00840472018-10-03 19:37:46 +0530118 if (ec)
119 {
120 phosphor::logging::log<phosphor::logging::level::ERR>(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530121 "async_method_call error: Failed to service unit "
122 "properties");
AppaRao Puli00840472018-10-03 19:37:46 +0530123 return;
124 }
AppaRao Puli00840472018-10-03 19:37:46 +0530125 try
126 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530127 updateServiceProperties(propertyMap);
128 if (!socketObjectPath.empty())
AppaRao Puli00840472018-10-03 19:37:46 +0530129 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530130 conn->async_method_call(
131 [this](boost::system::error_code ec,
132 const boost::container::flat_map<
AppaRao Puliee853eb2020-05-29 00:53:20 +0530133 std::string, VariantType>& propertyMap) {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530134 if (ec)
135 {
136 phosphor::logging::log<
137 phosphor::logging::level::ERR>(
138 "async_method_call error: Failed to get "
139 "all property");
140 return;
141 }
142 try
143 {
144 updateSocketProperties(propertyMap);
145 if (!iface)
146 {
147 registerProperties();
148 }
149 }
AppaRao Puliee853eb2020-05-29 00:53:20 +0530150 catch (const std::exception& e)
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530151 {
152 phosphor::logging::log<
153 phosphor::logging::level::ERR>(
154 "Exception in getting socket properties",
155 phosphor::logging::entry("WHAT=%s",
156 e.what()));
157 return;
158 }
159 },
160 sysdService, socketObjectPath, dBusPropIntf,
161 dBusGetAllMethod, sysdSocketIntf);
AppaRao Puli00840472018-10-03 19:37:46 +0530162 }
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530163 else if (!iface)
164 {
165 registerProperties();
166 }
AppaRao Puli00840472018-10-03 19:37:46 +0530167 }
AppaRao Puliee853eb2020-05-29 00:53:20 +0530168 catch (const std::exception& e)
AppaRao Puli00840472018-10-03 19:37:46 +0530169 {
170 phosphor::logging::log<phosphor::logging::level::ERR>(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530171 "Exception in getting socket properties",
AppaRao Puli00840472018-10-03 19:37:46 +0530172 phosphor::logging::entry("WHAT=%s", e.what()));
173 return;
174 }
AppaRao Puli00840472018-10-03 19:37:46 +0530175 },
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530176 sysdService, serviceObjectPath, dBusPropIntf, dBusGetAllMethod,
177 sysdUnitIntf);
AppaRao Puli00840472018-10-03 19:37:46 +0530178 return;
179}
180
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530181void ServiceConfig::createSocketOverrideConf()
182{
183 if (!socketObjectPath.empty())
184 {
185 std::string socketUnitName(instantiatedUnitName + ".socket");
186 /// Check override socket directory exist, if not create it.
187 std::experimental::filesystem::path ovrUnitFileDir(
188 systemdOverrideUnitBasePath);
189 ovrUnitFileDir += socketUnitName;
190 ovrUnitFileDir += ".d";
191 if (!std::experimental::filesystem::exists(ovrUnitFileDir))
192 {
193 if (!std::experimental::filesystem::create_directories(
194 ovrUnitFileDir))
195 {
196 phosphor::logging::log<phosphor::logging::level::ERR>(
197 "Unable to create the directory.",
198 phosphor::logging::entry("DIR=%s", ovrUnitFileDir.c_str()));
199 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::
200 Common::Error::InternalFailure>();
201 }
202 }
203 overrideConfDir = std::string(ovrUnitFileDir);
204 }
205}
206
AppaRao Puli00840472018-10-03 19:37:46 +0530207ServiceConfig::ServiceConfig(
AppaRao Puliee853eb2020-05-29 00:53:20 +0530208 sdbusplus::asio::object_server& srv_,
209 std::shared_ptr<sdbusplus::asio::connection>& conn_,
210 const std::string& objPath_, const std::string& baseUnitName_,
211 const std::string& instanceName_, const std::string& serviceObjPath_,
212 const std::string& socketObjPath_) :
AppaRao Puli00840472018-10-03 19:37:46 +0530213 server(srv_),
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530214 conn(conn_), objPath(objPath_), baseUnitName(baseUnitName_),
215 instanceName(instanceName_), serviceObjectPath(serviceObjPath_),
216 socketObjectPath(socketObjPath_)
AppaRao Puli00840472018-10-03 19:37:46 +0530217{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530218 instantiatedUnitName = baseUnitName + addInstanceName(instanceName, "@");
AppaRao Puli00840472018-10-03 19:37:46 +0530219 updatedFlag = 0;
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530220 queryAndUpdateProperties();
AppaRao Puli00840472018-10-03 19:37:46 +0530221 return;
222}
223
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530224std::string ServiceConfig::getSocketUnitName()
AppaRao Puli00840472018-10-03 19:37:46 +0530225{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530226 return instantiatedUnitName + ".socket";
227}
228
229std::string ServiceConfig::getServiceUnitName()
230{
231 return instantiatedUnitName + ".service";
232}
233
234bool ServiceConfig::isMaskedOut()
235{
236 // return true if state is masked & no request to update the maskedState
237 return (
238 stateValue == "masked" &&
239 !(updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::maskedState))));
240}
241
242void ServiceConfig::stopAndApplyUnitConfig(boost::asio::yield_context yield)
243{
244 if (!updatedFlag || isMaskedOut())
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530245 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530246 // No updates / masked - Just return.
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530247 return;
248 }
AppaRao Puli00840472018-10-03 19:37:46 +0530249 phosphor::logging::log<phosphor::logging::level::INFO>(
250 "Applying new settings.",
251 phosphor::logging::entry("OBJPATH=%s", objPath.c_str()));
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530252 if (subStateValue == "running")
AppaRao Puli00840472018-10-03 19:37:46 +0530253 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530254 if (!socketObjectPath.empty())
255 {
256 systemdUnitAction(conn, yield, getSocketUnitName(), sysdStopUnit);
257 }
258 systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit);
259 }
260
261 if (updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::port)))
262 {
263 createSocketOverrideConf();
AppaRao Puli00840472018-10-03 19:37:46 +0530264 // Create override config file and write data.
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530265 std::string ovrCfgFile{overrideConfDir + "/" + overrideConfFileName};
AppaRao Puli00840472018-10-03 19:37:46 +0530266 std::string tmpFile{ovrCfgFile + "_tmp"};
267 std::ofstream cfgFile(tmpFile, std::ios::out);
268 if (!cfgFile.good())
269 {
270 phosphor::logging::log<phosphor::logging::level::ERR>(
271 "Failed to open override.conf_tmp file");
272 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
273 Error::InternalFailure>();
274 }
275
276 // Write the socket header
277 cfgFile << "[Socket]\n";
278 // Listen
279 cfgFile << "Listen" << protocol << "="
280 << "\n";
281 cfgFile << "Listen" << protocol << "=" << portNum << "\n";
AppaRao Puli00840472018-10-03 19:37:46 +0530282 cfgFile.close();
283
284 if (std::rename(tmpFile.c_str(), ovrCfgFile.c_str()) != 0)
285 {
286 phosphor::logging::log<phosphor::logging::level::ERR>(
287 "Failed to rename tmp file as override.conf");
288 std::remove(tmpFile.c_str());
289 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
290 Error::InternalFailure>();
291 }
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530292 }
AppaRao Puli00840472018-10-03 19:37:46 +0530293
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530294 if (updatedFlag & ((1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
295 (1 << static_cast<uint8_t>(UpdatedProp::enabledState))))
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530296 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530297 std::vector<std::string> unitFiles;
298 if (socketObjectPath.empty())
299 {
300 unitFiles = {getServiceUnitName()};
301 }
302 else
303 {
304 unitFiles = {getSocketUnitName(), getServiceUnitName()};
305 }
306 systemdUnitFilesStateChange(conn, yield, unitFiles, stateValue,
307 unitMaskedState, unitEnabledState);
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530308 }
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530309 return;
310}
311void ServiceConfig::restartUnitConfig(boost::asio::yield_context yield)
312{
313 if (!updatedFlag || isMaskedOut())
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530314 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530315 // No updates. Just return.
316 return;
AppaRao Puli00840472018-10-03 19:37:46 +0530317 }
318
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530319 if (unitRunningState)
AppaRao Puli00840472018-10-03 19:37:46 +0530320 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530321 if (!socketObjectPath.empty())
322 {
323 systemdUnitAction(conn, yield, getSocketUnitName(),
324 sysdRestartUnit);
325 }
326 systemdUnitAction(conn, yield, getServiceUnitName(), sysdRestartUnit);
AppaRao Puli00840472018-10-03 19:37:46 +0530327 }
328
329 // Reset the flag
330 updatedFlag = 0;
331
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530332 phosphor::logging::log<phosphor::logging::level::INFO>(
333 "Applied new settings",
334 phosphor::logging::entry("OBJPATH=%s", objPath.c_str()));
AppaRao Puli00840472018-10-03 19:37:46 +0530335
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530336 queryAndUpdateProperties();
AppaRao Puli00840472018-10-03 19:37:46 +0530337 return;
338}
339
340void ServiceConfig::startServiceRestartTimer()
341{
Richard Marian Thomaiyar33816cf2019-06-14 13:54:50 +0530342 timer->expires_after(std::chrono::seconds(restartTimeout));
AppaRao Puliee853eb2020-05-29 00:53:20 +0530343 timer->async_wait([this](const boost::system::error_code& ec) {
AppaRao Puli00840472018-10-03 19:37:46 +0530344 if (ec == boost::asio::error::operation_aborted)
345 {
346 // Timer reset.
347 return;
348 }
349 else if (ec)
350 {
351 phosphor::logging::log<phosphor::logging::level::ERR>(
352 "async wait error.");
353 return;
354 }
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530355 updateInProgress = true;
356 boost::asio::spawn(conn->get_io_context(),
357 [this](boost::asio::yield_context yield) {
358 // Stop and apply configuration for all objects
AppaRao Puliee853eb2020-05-29 00:53:20 +0530359 for (auto& srvMgrObj : srvMgrObjects)
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530360 {
AppaRao Puliee853eb2020-05-29 00:53:20 +0530361 auto& srvObj = srvMgrObj.second;
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530362 if (srvObj->updatedFlag)
363 {
364 srvObj->stopAndApplyUnitConfig(yield);
365 }
366 }
367 // Do system reload
368 systemdDaemonReload(conn, yield);
369 // restart unit config.
AppaRao Puliee853eb2020-05-29 00:53:20 +0530370 for (auto& srvMgrObj : srvMgrObjects)
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530371 {
AppaRao Puliee853eb2020-05-29 00:53:20 +0530372 auto& srvObj = srvMgrObj.second;
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530373 if (srvObj->updatedFlag)
374 {
375 srvObj->restartUnitConfig(yield);
376 }
377 }
378 updateInProgress = false;
379 });
AppaRao Puli00840472018-10-03 19:37:46 +0530380 });
381}
382
383void ServiceConfig::registerProperties()
384{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530385 iface = server.add_interface(objPath, serviceConfigIntfName);
386
387 if (!socketObjectPath.empty())
388 {
389 iface->register_property(
390 srvCfgPropPort, portNum,
AppaRao Puliee853eb2020-05-29 00:53:20 +0530391 [this](const uint16_t& req, uint16_t& res) {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530392 if (!internalSet)
393 {
394 if (req == res)
395 {
396 return 1;
397 }
398 if (updateInProgress)
399 {
400 return 0;
401 }
402 portNum = req;
403 updatedFlag |=
404 (1 << static_cast<uint8_t>(UpdatedProp::port));
405 startServiceRestartTimer();
406 }
407 res = req;
408 return 1;
409 });
410 }
AppaRao Puli00840472018-10-03 19:37:46 +0530411
412 iface->register_property(
AppaRao Puliee853eb2020-05-29 00:53:20 +0530413 srvCfgPropMasked, unitMaskedState, [this](const bool& req, bool& res) {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530414 if (!internalSet)
AppaRao Puli00840472018-10-03 19:37:46 +0530415 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530416 if (req == res)
417 {
418 return 1;
419 }
420 if (updateInProgress)
421 {
422 return 0;
423 }
424 unitMaskedState = req;
425 unitEnabledState = !unitMaskedState;
426 unitRunningState = !unitMaskedState;
427 updatedFlag |=
428 (1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
429 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)) |
430 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
431 internalSet = true;
432 iface->set_property(srvCfgPropEnabled, unitEnabledState);
433 iface->set_property(srvCfgPropRunning, unitRunningState);
434 internalSet = false;
435 startServiceRestartTimer();
AppaRao Puli00840472018-10-03 19:37:46 +0530436 }
AppaRao Puli00840472018-10-03 19:37:46 +0530437 res = req;
438 return 1;
439 });
440
441 iface->register_property(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530442 srvCfgPropEnabled, unitEnabledState,
AppaRao Puliee853eb2020-05-29 00:53:20 +0530443 [this](const bool& req, bool& res) {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530444 if (!internalSet)
AppaRao Puli00840472018-10-03 19:37:46 +0530445 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530446 if (req == res)
447 {
448 return 1;
449 }
450 if (updateInProgress)
451 {
452 return 0;
453 }
454 if (unitMaskedState)
455 { // block updating if masked
456 phosphor::logging::log<phosphor::logging::level::ERR>(
457 "Invalid value specified");
458 return -EINVAL;
459 }
460 unitEnabledState = req;
461 updatedFlag |=
462 (1 << static_cast<uint8_t>(UpdatedProp::enabledState));
463 startServiceRestartTimer();
AppaRao Puli00840472018-10-03 19:37:46 +0530464 }
AppaRao Puli00840472018-10-03 19:37:46 +0530465 res = req;
466 return 1;
467 });
468
469 iface->register_property(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530470 srvCfgPropRunning, unitRunningState,
AppaRao Puliee853eb2020-05-29 00:53:20 +0530471 [this](const bool& req, bool& res) {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530472 if (!internalSet)
AppaRao Puli00840472018-10-03 19:37:46 +0530473 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530474 if (req == res)
475 {
476 return 1;
477 }
478 if (updateInProgress)
479 {
480 return 0;
481 }
482 if (unitMaskedState)
483 { // block updating if masked
484 phosphor::logging::log<phosphor::logging::level::ERR>(
485 "Invalid value specified");
486 return -EINVAL;
487 }
488 unitRunningState = req;
489 updatedFlag |=
490 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
491 startServiceRestartTimer();
AppaRao Puli00840472018-10-03 19:37:46 +0530492 }
AppaRao Puli00840472018-10-03 19:37:46 +0530493 res = req;
494 return 1;
495 });
496
497 iface->initialize();
498 return;
499}
500
501} // namespace service
502} // namespace phosphor