blob: 7f397d06a4a80356b8b0d9920925a4dd3b6a36fe [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*/
16#include <fstream>
17#include <regex>
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053018#include <boost/asio/spawn.hpp>
AppaRao Puli00840472018-10-03 19:37:46 +053019#include "srvcfg_manager.hpp"
20
Richard Marian Thomaiyar33816cf2019-06-14 13:54:50 +053021extern std::unique_ptr<boost::asio::steady_timer> timer;
AppaRao Puli00840472018-10-03 19:37:46 +053022extern std::map<std::string, std::shared_ptr<phosphor::service::ServiceConfig>>
23 srvMgrObjects;
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053024static bool updateInProgress = false;
AppaRao Puli00840472018-10-03 19:37:46 +053025
26namespace phosphor
27{
28namespace service
29{
30
31static constexpr const char *overrideConfFileName = "override.conf";
32static constexpr const size_t restartTimeout = 15; // seconds
33
34static constexpr const char *systemd1UnitBasePath =
35 "/org/freedesktop/systemd1/unit/";
36static constexpr const char *systemdOverrideUnitBasePath =
37 "/etc/systemd/system/";
38
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053039void ServiceConfig::updateSocketProperties(
40 const boost::container::flat_map<std::string, VariantType> &propertyMap)
AppaRao Puli00840472018-10-03 19:37:46 +053041{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053042 auto listenIt = propertyMap.find("Listen");
43 if (listenIt != propertyMap.end())
44 {
45 auto listenVal =
46 std::get<std::vector<std::tuple<std::string, std::string>>>(
47 listenIt->second);
48 if (listenVal.size())
49 {
50 protocol = std::get<0>(listenVal[0]);
51 std::string port = std::get<1>(listenVal[0]);
52 auto tmp = std::stoul(port.substr(port.find_last_of(":") + 1),
53 nullptr, 10);
54 if (tmp > std::numeric_limits<uint16_t>::max())
55 {
56 throw std::out_of_range("Out of range");
57 }
58 portNum = tmp;
59 if (iface && iface->is_initialized())
60 {
61 internalSet = true;
62 iface->set_property(srvCfgPropPort, portNum);
63 internalSet = false;
64 }
65 }
66 }
67}
68
69void ServiceConfig::updateServiceProperties(
70 const boost::container::flat_map<std::string, VariantType> &propertyMap)
71{
72 auto stateIt = propertyMap.find("UnitFileState");
73 if (stateIt != propertyMap.end())
74 {
75 stateValue = std::get<std::string>(stateIt->second);
76 unitEnabledState = unitMaskedState = false;
77 if (stateValue == stateMasked)
78 {
79 unitMaskedState = true;
80 }
81 else if (stateValue == stateEnabled)
82 {
83 unitEnabledState = true;
84 }
85 if (iface && iface->is_initialized())
86 {
87 internalSet = true;
88 iface->set_property(srvCfgPropMasked, unitMaskedState);
89 iface->set_property(srvCfgPropEnabled, unitEnabledState);
90 internalSet = false;
91 }
92 }
93 auto subStateIt = propertyMap.find("SubState");
94 if (subStateIt != propertyMap.end())
95 {
96 subStateValue = std::get<std::string>(subStateIt->second);
97 if (subStateValue == subStateRunning)
98 {
99 unitRunningState = true;
100 }
101 if (iface && iface->is_initialized())
102 {
103 internalSet = true;
104 iface->set_property(srvCfgPropRunning, unitRunningState);
105 internalSet = false;
106 }
107 }
108}
109
110void ServiceConfig::queryAndUpdateProperties()
111{
AppaRao Puli00840472018-10-03 19:37:46 +0530112 conn->async_method_call(
113 [this](boost::system::error_code ec,
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530114 const boost::container::flat_map<std::string, VariantType>
115 &propertyMap) {
AppaRao Puli00840472018-10-03 19:37:46 +0530116 if (ec)
117 {
118 phosphor::logging::log<phosphor::logging::level::ERR>(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530119 "async_method_call error: Failed to service unit "
120 "properties");
AppaRao Puli00840472018-10-03 19:37:46 +0530121 return;
122 }
AppaRao Puli00840472018-10-03 19:37:46 +0530123 try
124 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530125 updateServiceProperties(propertyMap);
126 if (!socketObjectPath.empty())
AppaRao Puli00840472018-10-03 19:37:46 +0530127 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530128 conn->async_method_call(
129 [this](boost::system::error_code ec,
130 const boost::container::flat_map<
131 std::string, VariantType> &propertyMap) {
132 if (ec)
133 {
134 phosphor::logging::log<
135 phosphor::logging::level::ERR>(
136 "async_method_call error: Failed to get "
137 "all property");
138 return;
139 }
140 try
141 {
142 updateSocketProperties(propertyMap);
143 if (!iface)
144 {
145 registerProperties();
146 }
147 }
148 catch (const std::exception &e)
149 {
150 phosphor::logging::log<
151 phosphor::logging::level::ERR>(
152 "Exception in getting socket properties",
153 phosphor::logging::entry("WHAT=%s",
154 e.what()));
155 return;
156 }
157 },
158 sysdService, socketObjectPath, dBusPropIntf,
159 dBusGetAllMethod, sysdSocketIntf);
AppaRao Puli00840472018-10-03 19:37:46 +0530160 }
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530161 else if (!iface)
162 {
163 registerProperties();
164 }
AppaRao Puli00840472018-10-03 19:37:46 +0530165 }
166 catch (const std::exception &e)
167 {
168 phosphor::logging::log<phosphor::logging::level::ERR>(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530169 "Exception in getting socket properties",
AppaRao Puli00840472018-10-03 19:37:46 +0530170 phosphor::logging::entry("WHAT=%s", e.what()));
171 return;
172 }
AppaRao Puli00840472018-10-03 19:37:46 +0530173 },
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530174 sysdService, serviceObjectPath, dBusPropIntf, dBusGetAllMethod,
175 sysdUnitIntf);
AppaRao Puli00840472018-10-03 19:37:46 +0530176 return;
177}
178
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530179void ServiceConfig::createSocketOverrideConf()
180{
181 if (!socketObjectPath.empty())
182 {
183 std::string socketUnitName(instantiatedUnitName + ".socket");
184 /// Check override socket directory exist, if not create it.
185 std::experimental::filesystem::path ovrUnitFileDir(
186 systemdOverrideUnitBasePath);
187 ovrUnitFileDir += socketUnitName;
188 ovrUnitFileDir += ".d";
189 if (!std::experimental::filesystem::exists(ovrUnitFileDir))
190 {
191 if (!std::experimental::filesystem::create_directories(
192 ovrUnitFileDir))
193 {
194 phosphor::logging::log<phosphor::logging::level::ERR>(
195 "Unable to create the directory.",
196 phosphor::logging::entry("DIR=%s", ovrUnitFileDir.c_str()));
197 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::
198 Common::Error::InternalFailure>();
199 }
200 }
201 overrideConfDir = std::string(ovrUnitFileDir);
202 }
203}
204
AppaRao Puli00840472018-10-03 19:37:46 +0530205ServiceConfig::ServiceConfig(
206 sdbusplus::asio::object_server &srv_,
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530207 std::shared_ptr<sdbusplus::asio::connection> &conn_,
208 const std::string &objPath_, const std::string &baseUnitName_,
209 const std::string &instanceName_, const std::string &serviceObjPath_,
210 const std::string &socketObjPath_) :
AppaRao Puli00840472018-10-03 19:37:46 +0530211 server(srv_),
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530212 conn(conn_), objPath(objPath_), baseUnitName(baseUnitName_),
213 instanceName(instanceName_), serviceObjectPath(serviceObjPath_),
214 socketObjectPath(socketObjPath_)
AppaRao Puli00840472018-10-03 19:37:46 +0530215{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530216 instantiatedUnitName = baseUnitName + addInstanceName(instanceName, "@");
AppaRao Puli00840472018-10-03 19:37:46 +0530217 updatedFlag = 0;
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530218 queryAndUpdateProperties();
AppaRao Puli00840472018-10-03 19:37:46 +0530219 return;
220}
221
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530222std::string ServiceConfig::getSocketUnitName()
AppaRao Puli00840472018-10-03 19:37:46 +0530223{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530224 return instantiatedUnitName + ".socket";
225}
226
227std::string ServiceConfig::getServiceUnitName()
228{
229 return instantiatedUnitName + ".service";
230}
231
232bool ServiceConfig::isMaskedOut()
233{
234 // return true if state is masked & no request to update the maskedState
235 return (
236 stateValue == "masked" &&
237 !(updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::maskedState))));
238}
239
240void ServiceConfig::stopAndApplyUnitConfig(boost::asio::yield_context yield)
241{
242 if (!updatedFlag || isMaskedOut())
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530243 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530244 // No updates / masked - Just return.
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530245 return;
246 }
AppaRao Puli00840472018-10-03 19:37:46 +0530247 phosphor::logging::log<phosphor::logging::level::INFO>(
248 "Applying new settings.",
249 phosphor::logging::entry("OBJPATH=%s", objPath.c_str()));
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530250 if (subStateValue == "running")
AppaRao Puli00840472018-10-03 19:37:46 +0530251 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530252 if (!socketObjectPath.empty())
253 {
254 systemdUnitAction(conn, yield, getSocketUnitName(), sysdStopUnit);
255 }
256 systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit);
257 }
258
259 if (updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::port)))
260 {
261 createSocketOverrideConf();
AppaRao Puli00840472018-10-03 19:37:46 +0530262 // Create override config file and write data.
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530263 std::string ovrCfgFile{overrideConfDir + "/" + overrideConfFileName};
AppaRao Puli00840472018-10-03 19:37:46 +0530264 std::string tmpFile{ovrCfgFile + "_tmp"};
265 std::ofstream cfgFile(tmpFile, std::ios::out);
266 if (!cfgFile.good())
267 {
268 phosphor::logging::log<phosphor::logging::level::ERR>(
269 "Failed to open override.conf_tmp file");
270 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
271 Error::InternalFailure>();
272 }
273
274 // Write the socket header
275 cfgFile << "[Socket]\n";
276 // Listen
277 cfgFile << "Listen" << protocol << "="
278 << "\n";
279 cfgFile << "Listen" << protocol << "=" << portNum << "\n";
AppaRao Puli00840472018-10-03 19:37:46 +0530280 cfgFile.close();
281
282 if (std::rename(tmpFile.c_str(), ovrCfgFile.c_str()) != 0)
283 {
284 phosphor::logging::log<phosphor::logging::level::ERR>(
285 "Failed to rename tmp file as override.conf");
286 std::remove(tmpFile.c_str());
287 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
288 Error::InternalFailure>();
289 }
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530290 }
AppaRao Puli00840472018-10-03 19:37:46 +0530291
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530292 if (updatedFlag & ((1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
293 (1 << static_cast<uint8_t>(UpdatedProp::enabledState))))
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530294 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530295 std::vector<std::string> unitFiles;
296 if (socketObjectPath.empty())
297 {
298 unitFiles = {getServiceUnitName()};
299 }
300 else
301 {
302 unitFiles = {getSocketUnitName(), getServiceUnitName()};
303 }
304 systemdUnitFilesStateChange(conn, yield, unitFiles, stateValue,
305 unitMaskedState, unitEnabledState);
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530306 }
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530307 return;
308}
309void ServiceConfig::restartUnitConfig(boost::asio::yield_context yield)
310{
311 if (!updatedFlag || isMaskedOut())
AppaRao Pulie55cfd62019-02-15 15:35:29 +0530312 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530313 // No updates. Just return.
314 return;
AppaRao Puli00840472018-10-03 19:37:46 +0530315 }
316
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530317 if (unitRunningState)
AppaRao Puli00840472018-10-03 19:37:46 +0530318 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530319 if (!socketObjectPath.empty())
320 {
321 systemdUnitAction(conn, yield, getSocketUnitName(),
322 sysdRestartUnit);
323 }
324 systemdUnitAction(conn, yield, getServiceUnitName(), sysdRestartUnit);
AppaRao Puli00840472018-10-03 19:37:46 +0530325 }
326
327 // Reset the flag
328 updatedFlag = 0;
329
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530330 phosphor::logging::log<phosphor::logging::level::INFO>(
331 "Applied new settings",
332 phosphor::logging::entry("OBJPATH=%s", objPath.c_str()));
AppaRao Puli00840472018-10-03 19:37:46 +0530333
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530334 queryAndUpdateProperties();
AppaRao Puli00840472018-10-03 19:37:46 +0530335 return;
336}
337
338void ServiceConfig::startServiceRestartTimer()
339{
Richard Marian Thomaiyar33816cf2019-06-14 13:54:50 +0530340 timer->expires_after(std::chrono::seconds(restartTimeout));
AppaRao Puli00840472018-10-03 19:37:46 +0530341 timer->async_wait([this](const boost::system::error_code &ec) {
342 if (ec == boost::asio::error::operation_aborted)
343 {
344 // Timer reset.
345 return;
346 }
347 else if (ec)
348 {
349 phosphor::logging::log<phosphor::logging::level::ERR>(
350 "async wait error.");
351 return;
352 }
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530353 updateInProgress = true;
354 boost::asio::spawn(conn->get_io_context(),
355 [this](boost::asio::yield_context yield) {
356 // Stop and apply configuration for all objects
357 for (auto &srvMgrObj : srvMgrObjects)
358 {
359 auto &srvObj = srvMgrObj.second;
360 if (srvObj->updatedFlag)
361 {
362 srvObj->stopAndApplyUnitConfig(yield);
363 }
364 }
365 // Do system reload
366 systemdDaemonReload(conn, yield);
367 // restart unit config.
368 for (auto &srvMgrObj : srvMgrObjects)
369 {
370 auto &srvObj = srvMgrObj.second;
371 if (srvObj->updatedFlag)
372 {
373 srvObj->restartUnitConfig(yield);
374 }
375 }
376 updateInProgress = false;
377 });
AppaRao Puli00840472018-10-03 19:37:46 +0530378 });
379}
380
381void ServiceConfig::registerProperties()
382{
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530383 iface = server.add_interface(objPath, serviceConfigIntfName);
384
385 if (!socketObjectPath.empty())
386 {
387 iface->register_property(
388 srvCfgPropPort, portNum,
389 [this](const uint16_t &req, uint16_t &res) {
390 if (!internalSet)
391 {
392 if (req == res)
393 {
394 return 1;
395 }
396 if (updateInProgress)
397 {
398 return 0;
399 }
400 portNum = req;
401 updatedFlag |=
402 (1 << static_cast<uint8_t>(UpdatedProp::port));
403 startServiceRestartTimer();
404 }
405 res = req;
406 return 1;
407 });
408 }
AppaRao Puli00840472018-10-03 19:37:46 +0530409
410 iface->register_property(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530411 srvCfgPropMasked, unitMaskedState, [this](const bool &req, bool &res) {
412 if (!internalSet)
AppaRao Puli00840472018-10-03 19:37:46 +0530413 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530414 if (req == res)
415 {
416 return 1;
417 }
418 if (updateInProgress)
419 {
420 return 0;
421 }
422 unitMaskedState = req;
423 unitEnabledState = !unitMaskedState;
424 unitRunningState = !unitMaskedState;
425 updatedFlag |=
426 (1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
427 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)) |
428 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
429 internalSet = true;
430 iface->set_property(srvCfgPropEnabled, unitEnabledState);
431 iface->set_property(srvCfgPropRunning, unitRunningState);
432 internalSet = false;
433 startServiceRestartTimer();
AppaRao Puli00840472018-10-03 19:37:46 +0530434 }
AppaRao Puli00840472018-10-03 19:37:46 +0530435 res = req;
436 return 1;
437 });
438
439 iface->register_property(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530440 srvCfgPropEnabled, unitEnabledState,
441 [this](const bool &req, bool &res) {
442 if (!internalSet)
AppaRao Puli00840472018-10-03 19:37:46 +0530443 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530444 if (req == res)
445 {
446 return 1;
447 }
448 if (updateInProgress)
449 {
450 return 0;
451 }
452 if (unitMaskedState)
453 { // block updating if masked
454 phosphor::logging::log<phosphor::logging::level::ERR>(
455 "Invalid value specified");
456 return -EINVAL;
457 }
458 unitEnabledState = req;
459 updatedFlag |=
460 (1 << static_cast<uint8_t>(UpdatedProp::enabledState));
461 startServiceRestartTimer();
AppaRao Puli00840472018-10-03 19:37:46 +0530462 }
AppaRao Puli00840472018-10-03 19:37:46 +0530463 res = req;
464 return 1;
465 });
466
467 iface->register_property(
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530468 srvCfgPropRunning, unitRunningState,
469 [this](const bool &req, bool &res) {
470 if (!internalSet)
AppaRao Puli00840472018-10-03 19:37:46 +0530471 {
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530472 if (req == res)
473 {
474 return 1;
475 }
476 if (updateInProgress)
477 {
478 return 0;
479 }
480 if (unitMaskedState)
481 { // block updating if masked
482 phosphor::logging::log<phosphor::logging::level::ERR>(
483 "Invalid value specified");
484 return -EINVAL;
485 }
486 unitRunningState = req;
487 updatedFlag |=
488 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
489 startServiceRestartTimer();
AppaRao Puli00840472018-10-03 19:37:46 +0530490 }
AppaRao Puli00840472018-10-03 19:37:46 +0530491 res = req;
492 return 1;
493 });
494
495 iface->initialize();
496 return;
497}
498
499} // namespace service
500} // namespace phosphor