1 /*
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 "srvcfg_manager.hpp"
17
18 #include <boost/asio/detached.hpp>
19 #include <boost/asio/spawn.hpp>
20 #ifdef USB_CODE_UPDATE
21 #include <cereal/archives/json.hpp>
22 #include <cereal/types/tuple.hpp>
23 #include <cereal/types/unordered_map.hpp>
24
25 #include <cstdio>
26 #endif
27 #include <fstream>
28 #ifdef PERSIST_SETTINGS
29 #include <nlohmann/json.hpp>
30 #endif
31 #include <regex>
32
33 extern std::unique_ptr<boost::asio::steady_timer> timer;
34 extern std::map<std::string, std::shared_ptr<phosphor::service::ServiceConfig>>
35 srvMgrObjects;
36 static bool updateInProgress = false;
37
38 namespace phosphor
39 {
40 namespace service
41 {
42
43 static constexpr const char* overrideConfFileName = "override.conf";
44 static constexpr const size_t restartTimeout = 15; // seconds
45
46 static constexpr const char* systemd1UnitBasePath =
47 "/org/freedesktop/systemd1/unit/";
48 static constexpr const char* systemdOverrideUnitBasePath =
49 "/etc/systemd/system/";
50
51 #ifdef PERSIST_SETTINGS
52 static constexpr const char* persistDataFileVersionStr = "Version";
53 static constexpr const size_t persistDataFileVersion = 1;
54 #endif
55
56 #ifdef USB_CODE_UPDATE
57 static constexpr const char* usbCodeUpdateStateFilePath =
58 "/var/lib/srvcfg_manager";
59 static constexpr const char* usbCodeUpdateStateFile =
60 "/var/lib/srvcfg_manager/usb-code-update-state";
61 static constexpr const char* emptyUsbCodeUpdateRulesFile =
62 "/etc/udev/rules.d/70-bmc-usb.rules";
63
64 using UsbCodeUpdateStateMap = std::unordered_map<std::string, bool>;
65
setUSBCodeUpdateState(const bool & state)66 void ServiceConfig::setUSBCodeUpdateState(const bool& state)
67 {
68 // Enable usb code update
69 if (state)
70 {
71 if (std::filesystem::exists(emptyUsbCodeUpdateRulesFile))
72 {
73 lg2::info("Enable usb code update");
74 std::filesystem::remove(emptyUsbCodeUpdateRulesFile);
75 }
76 return;
77 }
78
79 // Disable usb code update
80 if (std::filesystem::exists(emptyUsbCodeUpdateRulesFile))
81 {
82 std::filesystem::remove(emptyUsbCodeUpdateRulesFile);
83 }
84 std::error_code ec;
85 std::filesystem::create_symlink("/dev/null", emptyUsbCodeUpdateRulesFile,
86 ec);
87 if (ec)
88 {
89 lg2::error("Disable usb code update failed");
90 return;
91 }
92 lg2::info("Disable usb code update");
93 }
94
saveUSBCodeUpdateStateToFile(const bool & maskedState,const bool & enabledState)95 void ServiceConfig::saveUSBCodeUpdateStateToFile(const bool& maskedState,
96 const bool& enabledState)
97 {
98 if (!std::filesystem::exists(usbCodeUpdateStateFilePath))
99 {
100 std::filesystem::create_directories(usbCodeUpdateStateFilePath);
101 }
102
103 UsbCodeUpdateStateMap usbCodeUpdateState;
104 usbCodeUpdateState[srvCfgPropMasked] = maskedState;
105 usbCodeUpdateState[srvCfgPropEnabled] = enabledState;
106
107 std::ofstream file(usbCodeUpdateStateFile, std::ios::out);
108 cereal::JSONOutputArchive archive(file);
109 archive(CEREAL_NVP(usbCodeUpdateState));
110 }
111
getUSBCodeUpdateStateFromFile()112 void ServiceConfig::getUSBCodeUpdateStateFromFile()
113 {
114 if (!std::filesystem::exists(usbCodeUpdateStateFile))
115 {
116 lg2::info("usb-code-update-state file does not exist");
117
118 unitMaskedState = false;
119 unitEnabledState = true;
120 unitRunningState = true;
121 setUSBCodeUpdateState(unitEnabledState);
122 return;
123 }
124
125 std::ifstream file(usbCodeUpdateStateFile);
126 cereal::JSONInputArchive archive(file);
127 UsbCodeUpdateStateMap usbCodeUpdateState;
128 archive(usbCodeUpdateState);
129
130 auto iterMask = usbCodeUpdateState.find(srvCfgPropMasked);
131 if (iterMask != usbCodeUpdateState.end())
132 {
133 unitMaskedState = iterMask->second;
134 if (unitMaskedState)
135 {
136 unitEnabledState = !unitMaskedState;
137 unitRunningState = !unitMaskedState;
138 setUSBCodeUpdateState(unitEnabledState);
139 return;
140 }
141
142 auto iterEnable = usbCodeUpdateState.find(srvCfgPropEnabled);
143 if (iterEnable != usbCodeUpdateState.end())
144 {
145 unitEnabledState = iterEnable->second;
146 unitRunningState = iterEnable->second;
147 setUSBCodeUpdateState(unitEnabledState);
148 }
149 }
150 }
151 #endif
152
updateSocketProperties(const boost::container::flat_map<std::string,VariantType> & propertyMap)153 void ServiceConfig::updateSocketProperties(
154 const boost::container::flat_map<std::string, VariantType>& propertyMap)
155 {
156 auto listenIt = propertyMap.find("Listen");
157 if (listenIt != propertyMap.end())
158 {
159 auto listenVal =
160 std::get<std::vector<std::tuple<std::string, std::string>>>(
161 listenIt->second);
162 if (listenVal.size())
163 {
164 protocol = std::get<0>(listenVal[0]);
165 std::string port = std::get<1>(listenVal[0]);
166 auto tmp = std::stoul(port.substr(port.find_last_of(":") + 1),
167 nullptr, 10);
168 if (tmp > std::numeric_limits<uint16_t>::max())
169 {
170 throw std::out_of_range("Out of range");
171 }
172 portNum = tmp;
173 if (sockAttrIface && sockAttrIface->is_initialized())
174 {
175 internalSet = true;
176 sockAttrIface->set_property(sockAttrPropPort, portNum);
177 internalSet = false;
178 }
179 }
180 }
181 }
182
updateServiceProperties(const boost::container::flat_map<std::string,VariantType> & propertyMap)183 void ServiceConfig::updateServiceProperties(
184 const boost::container::flat_map<std::string, VariantType>& propertyMap)
185 {
186 auto stateIt = propertyMap.find("UnitFileState");
187 if (stateIt != propertyMap.end())
188 {
189 stateValue = std::get<std::string>(stateIt->second);
190 unitEnabledState = unitMaskedState = false;
191 if (stateValue == stateMasked)
192 {
193 unitMaskedState = true;
194 }
195 else if (stateValue == stateEnabled)
196 {
197 unitEnabledState = true;
198 }
199 if (srvCfgIface && srvCfgIface->is_initialized())
200 {
201 internalSet = true;
202 srvCfgIface->set_property(srvCfgPropMasked, unitMaskedState);
203 srvCfgIface->set_property(srvCfgPropEnabled, unitEnabledState);
204 internalSet = false;
205 }
206 }
207 auto subStateIt = propertyMap.find("SubState");
208 if (subStateIt != propertyMap.end())
209 {
210 subStateValue = std::get<std::string>(subStateIt->second);
211 if (subStateValue == subStateRunning ||
212 subStateValue == subStateListening)
213 {
214 unitRunningState = true;
215 }
216 if (srvCfgIface && srvCfgIface->is_initialized())
217 {
218 internalSet = true;
219 srvCfgIface->set_property(srvCfgPropRunning, unitRunningState);
220 internalSet = false;
221 }
222 }
223
224 #ifdef USB_CODE_UPDATE
225 if (baseUnitName == usbCodeUpdateUnitName)
226 {
227 getUSBCodeUpdateStateFromFile();
228 }
229 #endif
230 }
231
queryAndUpdateProperties()232 void ServiceConfig::queryAndUpdateProperties()
233 {
234 std::string objectPath =
235 isSocketActivatedService ? socketObjectPath : serviceObjectPath;
236 if (objectPath.empty())
237 {
238 return;
239 }
240
241 conn->async_method_call(
242 [this](boost::system::error_code ec,
243 const boost::container::flat_map<std::string, VariantType>&
244 propertyMap) {
245 if (ec)
246 {
247 lg2::error(
248 "async_method_call error: Failed to service unit properties: {EC}",
249 "EC", ec.value());
250 return;
251 }
252 try
253 {
254 updateServiceProperties(propertyMap);
255 if (!socketObjectPath.empty())
256 {
257 conn->async_method_call(
258 [this](boost::system::error_code ec,
259 const boost::container::flat_map<
260 std::string, VariantType>& propertyMap) {
261 if (ec)
262 {
263 lg2::error(
264 "async_method_call error: Failed to get all property: {EC}",
265 "EC", ec.value());
266 return;
267 }
268 try
269 {
270 updateSocketProperties(propertyMap);
271 if (!srvCfgIface)
272 {
273 registerProperties();
274 }
275 }
276 catch (const std::exception& e)
277 {
278 lg2::error(
279 "Exception in getting socket properties: {ERROR}",
280 "ERROR", e);
281 return;
282 }
283 },
284 sysdService, socketObjectPath, dBusPropIntf,
285 dBusGetAllMethod, sysdSocketIntf);
286 }
287 else if (!srvCfgIface)
288 {
289 registerProperties();
290 }
291 loadStateFile();
292 }
293 catch (const std::exception& e)
294 {
295 lg2::error("Exception in getting socket properties: {ERROR}",
296 "ERROR", e);
297 return;
298 }
299 },
300 sysdService, objectPath, dBusPropIntf, dBusGetAllMethod, sysdUnitIntf);
301 return;
302 }
303
createSocketOverrideConf()304 void ServiceConfig::createSocketOverrideConf()
305 {
306 if (!socketObjectPath.empty())
307 {
308 std::string socketUnitName(instantiatedUnitName + ".socket");
309 /// Check override socket directory exist, if not create it.
310 std::filesystem::path ovrUnitFileDir(systemdOverrideUnitBasePath);
311 ovrUnitFileDir += socketUnitName;
312 ovrUnitFileDir += ".d";
313 if (!std::filesystem::exists(ovrUnitFileDir))
314 {
315 if (!std::filesystem::create_directories(ovrUnitFileDir))
316 {
317 lg2::error("Unable to create the {DIR} directory.", "DIR",
318 ovrUnitFileDir);
319 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::
320 Common::Error::InternalFailure>();
321 }
322 }
323 overrideConfDir = std::string(ovrUnitFileDir);
324 }
325 }
326
writeStateFile()327 void ServiceConfig::writeStateFile()
328 {
329 #ifdef PERSIST_SETTINGS
330 nlohmann::json stateMap;
331 stateMap[persistDataFileVersionStr] = persistDataFileVersion;
332 stateMap[srvCfgPropMasked] = unitMaskedState;
333 stateMap[srvCfgPropEnabled] = unitEnabledState;
334 stateMap[srvCfgPropRunning] = unitRunningState;
335
336 std::ofstream file(stateFile);
337 file << stateMap;
338 file.close();
339 #endif
340 }
341
loadStateFile()342 void ServiceConfig::loadStateFile()
343 {
344 #ifdef PERSIST_SETTINGS
345 if (std::filesystem::exists(stateFile))
346 {
347 std::ifstream file(stateFile);
348 if (!file.good())
349 {
350 lg2::error("Error reading {FILEPATH}; delete it and continue",
351 "FILEPATH", stateFile);
352 std::filesystem::remove(stateFile);
353 // rewrite file with what was ready from systemd
354 writeStateFile();
355 return;
356 }
357
358 nlohmann::json stateMap =
359 nlohmann::json::parse(file, nullptr, false, true);
360 if (stateMap.is_discarded())
361 {
362 lg2::error("Error loading {FILEPATH}; delete it and continue",
363 "FILEPATH", stateFile);
364 std::filesystem::remove(stateFile);
365 // rewrite file with what was ready from systemd
366 writeStateFile();
367 return;
368 }
369 else if (stateMap[persistDataFileVersionStr] != persistDataFileVersion)
370 {
371 lg2::error(
372 "Error version:{VERSION} read from {FILEPATH} does not match expected {VERSION_EXP}; delete it and continue",
373 "VERSION", stateMap[persistDataFileVersionStr], "FILEPATH",
374 stateFile, "VERSION_EXP", persistDataFileVersion);
375 std::filesystem::remove(stateFile);
376 // rewrite file with what was ready from systemd
377 writeStateFile();
378 return;
379 }
380
381 // If there are any differences, the persistent config file wins so
382 // update the dbus properties and trigger a reload to apply the changes
383 if (stateMap[srvCfgPropMasked] != unitMaskedState)
384 {
385 lg2::info(
386 "Masked property for {FILEPATH} not equal. Setting to {SETTING}",
387 "FILEPATH", stateFile, "SETTING", stateMap[srvCfgPropMasked]);
388 unitMaskedState = stateMap[srvCfgPropMasked];
389 updatedFlag |=
390 (1 << static_cast<uint8_t>(UpdatedProp::maskedState));
391 startServiceRestartTimer();
392 }
393 if (stateMap[srvCfgPropEnabled] != unitEnabledState)
394 {
395 lg2::info(
396 "Enabled property for {FILEPATH} not equal. Setting to {SETTING}",
397 "FILEPATH", stateFile, "SETTING", stateMap[srvCfgPropEnabled]);
398 unitEnabledState = stateMap[srvCfgPropEnabled];
399 updatedFlag |=
400 (1 << static_cast<uint8_t>(UpdatedProp::enabledState));
401 startServiceRestartTimer();
402 }
403 if (stateMap[srvCfgPropRunning] != unitRunningState)
404 {
405 lg2::info(
406 "Running property for {FILEPATH} not equal. Setting to {SETTING}",
407 "FILEPATH", stateFile, "SETTING", stateMap[srvCfgPropRunning]);
408 unitRunningState = stateMap[srvCfgPropRunning];
409 updatedFlag |=
410 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
411 startServiceRestartTimer();
412 }
413 }
414 else
415 {
416 // Just write out what we got from systemd if no existing config file
417 writeStateFile();
418 }
419 #endif
420 }
421
ServiceConfig(sdbusplus::asio::object_server & srv_,std::shared_ptr<sdbusplus::asio::connection> & conn_,const std::string & objPath_,const std::string & baseUnitName_,const std::string & instanceName_,const std::string & serviceObjPath_,const std::string & socketObjPath_)422 ServiceConfig::ServiceConfig(
423 sdbusplus::asio::object_server& srv_,
424 std::shared_ptr<sdbusplus::asio::connection>& conn_,
425 const std::string& objPath_, const std::string& baseUnitName_,
426 const std::string& instanceName_, const std::string& serviceObjPath_,
427 const std::string& socketObjPath_) :
428 conn(conn_), server(srv_), objPath(objPath_), baseUnitName(baseUnitName_),
429 instanceName(instanceName_), serviceObjectPath(serviceObjPath_),
430 socketObjectPath(socketObjPath_)
431 {
432 isSocketActivatedService = serviceObjectPath.empty();
433 instantiatedUnitName = baseUnitName + addInstanceName(instanceName, "@");
434 updatedFlag = 0;
435 stateFile = srvDataBaseDir + instantiatedUnitName;
436 queryAndUpdateProperties();
437 return;
438 }
439
getSocketUnitName()440 std::string ServiceConfig::getSocketUnitName()
441 {
442 return instantiatedUnitName + ".socket";
443 }
444
getServiceUnitName()445 std::string ServiceConfig::getServiceUnitName()
446 {
447 return instantiatedUnitName + ".service";
448 }
449
isMaskedOut()450 bool ServiceConfig::isMaskedOut()
451 {
452 // return true if state is masked & no request to update the maskedState
453 return (
454 stateValue == "masked" &&
455 !(updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::maskedState))));
456 }
457
stopAndApplyUnitConfig(boost::asio::yield_context yield)458 void ServiceConfig::stopAndApplyUnitConfig(boost::asio::yield_context yield)
459 {
460 if (!updatedFlag || isMaskedOut())
461 {
462 // No updates / masked - Just return.
463 return;
464 }
465 lg2::info("Applying new settings: {OBJPATH}", "OBJPATH", objPath);
466 if (subStateValue == subStateRunning || subStateValue == subStateListening)
467 {
468 if (!socketObjectPath.empty())
469 {
470 systemdUnitAction(conn, yield, getSocketUnitName(), sysdStopUnit);
471 }
472 if (!isSocketActivatedService)
473 {
474 systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit);
475 }
476 else
477 {
478 // For socket-activated service, each connection will spawn a
479 // service instance from template. Need to find all spawned service
480 // `<unitName>@<attribute>.service` and stop them through the
481 // systemdUnitAction method
482 boost::system::error_code ec;
483 auto listUnits =
484 conn->yield_method_call<std::vector<ListUnitsType>>(
485 yield, ec, sysdService, sysdObjPath, sysdMgrIntf,
486 "ListUnits");
487
488 checkAndThrowInternalFailure(
489 ec, "yield_method_call error: ListUnits failed");
490
491 for (const auto& unit : listUnits)
492 {
493 const auto& service =
494 std::get<static_cast<int>(ListUnitElements::name)>(unit);
495 const auto& status =
496 std::get<static_cast<int>(ListUnitElements::subState)>(
497 unit);
498 if (service.find(baseUnitName + "@") != std::string::npos &&
499 service.find(".service") != std::string::npos &&
500 status == subStateRunning)
501 {
502 systemdUnitAction(conn, yield, service, sysdStopUnit);
503 }
504 }
505 }
506 }
507
508 if (updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::port)))
509 {
510 createSocketOverrideConf();
511 // Create override config file and write data.
512 std::string ovrCfgFile{overrideConfDir + "/" + overrideConfFileName};
513 std::string tmpFile{ovrCfgFile + "_tmp"};
514 std::ofstream cfgFile(tmpFile, std::ios::out);
515 if (!cfgFile.good())
516 {
517 lg2::error("Failed to open the {TMPFILE} file.", "TMPFILE",
518 tmpFile);
519 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
520 Error::InternalFailure>();
521 }
522
523 // Write the socket header
524 cfgFile << "[Socket]\n";
525 // Listen
526 cfgFile << "Listen" << protocol << "="
527 << "\n";
528 cfgFile << "Listen" << protocol << "=" << portNum << "\n";
529 cfgFile.close();
530
531 if (std::rename(tmpFile.c_str(), ovrCfgFile.c_str()) != 0)
532 {
533 lg2::error("Failed to rename {TMPFILE} file as {OVERCFGFILE} file.",
534 "TMPFILE", tmpFile, "OVERCFGFILE", ovrCfgFile);
535 std::remove(tmpFile.c_str());
536 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
537 Error::InternalFailure>();
538 }
539 }
540
541 if (updatedFlag & ((1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
542 (1 << static_cast<uint8_t>(UpdatedProp::enabledState))))
543 {
544 std::vector<std::string> unitFiles;
545 if (socketObjectPath.empty())
546 {
547 unitFiles = {getServiceUnitName()};
548 }
549 else if (serviceObjectPath.empty())
550 {
551 unitFiles = {getSocketUnitName()};
552 }
553 else
554 {
555 unitFiles = {getSocketUnitName(), getServiceUnitName()};
556 }
557 systemdUnitFilesStateChange(conn, yield, unitFiles, stateValue,
558 unitMaskedState, unitEnabledState);
559 }
560 return;
561 }
restartUnitConfig(boost::asio::yield_context yield)562 void ServiceConfig::restartUnitConfig(boost::asio::yield_context yield)
563 {
564 if (!updatedFlag || isMaskedOut())
565 {
566 // No updates. Just return.
567 return;
568 }
569
570 if (unitRunningState)
571 {
572 if (!socketObjectPath.empty())
573 {
574 systemdUnitAction(conn, yield, getSocketUnitName(),
575 sysdRestartUnit);
576 }
577 if (!serviceObjectPath.empty())
578 {
579 systemdUnitAction(conn, yield, getServiceUnitName(),
580 sysdRestartUnit);
581 }
582 }
583
584 // Reset the flag
585 updatedFlag = 0;
586
587 lg2::info("Applied new settings: {OBJPATH}", "OBJPATH", objPath);
588
589 queryAndUpdateProperties();
590 return;
591 }
592
startServiceRestartTimer()593 void ServiceConfig::startServiceRestartTimer()
594 {
595 timer->expires_after(std::chrono::seconds(restartTimeout));
596 timer->async_wait([this](const boost::system::error_code& ec) {
597 if (ec == boost::asio::error::operation_aborted)
598 {
599 // Timer reset.
600 return;
601 }
602 else if (ec)
603 {
604 lg2::error("async wait error: {EC}", "EC", ec.value());
605 return;
606 }
607 updateInProgress = true;
608 // Ensure our persistent files are updated with changes
609 writeStateFile();
610 boost::asio::spawn(
611 conn->get_io_context(),
612 [this](boost::asio::yield_context yield) {
613 // Stop and apply configuration for all objects
614 for (auto& srvMgrObj : srvMgrObjects)
615 {
616 auto& srvObj = srvMgrObj.second;
617 if (srvObj->updatedFlag)
618 {
619 srvObj->stopAndApplyUnitConfig(yield);
620 }
621 }
622 // Do system reload
623 systemdDaemonReload(conn, yield);
624 // restart unit config.
625 for (auto& srvMgrObj : srvMgrObjects)
626 {
627 auto& srvObj = srvMgrObj.second;
628 if (srvObj->updatedFlag)
629 {
630 srvObj->restartUnitConfig(yield);
631 }
632 }
633 updateInProgress = false;
634 },
635 boost::asio::detached);
636 });
637 }
638
registerProperties()639 void ServiceConfig::registerProperties()
640 {
641 srvCfgIface = server.add_interface(objPath, serviceConfigIntfName);
642
643 if (!socketObjectPath.empty())
644 {
645 sockAttrIface = server.add_interface(objPath, sockAttrIntfName);
646 sockAttrIface->register_property(
647 sockAttrPropPort, portNum,
648 [this](const uint16_t& req, uint16_t& res) {
649 if (!internalSet)
650 {
651 if (req == res)
652 {
653 return 1;
654 }
655 if (updateInProgress)
656 {
657 return 0;
658 }
659 portNum = req;
660 updatedFlag |=
661 (1 << static_cast<uint8_t>(UpdatedProp::port));
662 startServiceRestartTimer();
663 }
664 res = req;
665 return 1;
666 });
667 }
668
669 srvCfgIface->register_property(
670 srvCfgPropMasked, unitMaskedState, [this](const bool& req, bool& res) {
671 if (!internalSet)
672 {
673 #ifdef USB_CODE_UPDATE
674 if (baseUnitName == usbCodeUpdateUnitName)
675 {
676 unitMaskedState = req;
677 unitEnabledState = !unitMaskedState;
678 unitRunningState = !unitMaskedState;
679 internalSet = true;
680 srvCfgIface->set_property(srvCfgPropEnabled,
681 unitEnabledState);
682 srvCfgIface->set_property(srvCfgPropRunning,
683 unitRunningState);
684 srvCfgIface->set_property(srvCfgPropMasked,
685 unitMaskedState);
686 internalSet = false;
687 setUSBCodeUpdateState(unitEnabledState);
688 saveUSBCodeUpdateStateToFile(unitMaskedState,
689 unitEnabledState);
690 return 1;
691 }
692 #endif
693 if (req == res)
694 {
695 return 1;
696 }
697 if (updateInProgress)
698 {
699 return 0;
700 }
701 unitMaskedState = req;
702 unitEnabledState = !unitMaskedState;
703 unitRunningState = !unitMaskedState;
704 updatedFlag |=
705 (1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
706 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)) |
707 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
708 internalSet = true;
709 srvCfgIface->set_property(srvCfgPropEnabled, unitEnabledState);
710 srvCfgIface->set_property(srvCfgPropRunning, unitRunningState);
711 internalSet = false;
712 startServiceRestartTimer();
713 }
714 res = req;
715 return 1;
716 });
717
718 srvCfgIface->register_property(
719 srvCfgPropEnabled, unitEnabledState,
720 [this](const bool& req, bool& res) {
721 if (!internalSet)
722 {
723 #ifdef USB_CODE_UPDATE
724 if (baseUnitName == usbCodeUpdateUnitName)
725 {
726 if (unitMaskedState)
727 { // block updating if masked
728 lg2::error("Invalid value specified");
729 return -EINVAL;
730 }
731 unitEnabledState = req;
732 unitRunningState = req;
733 internalSet = true;
734 srvCfgIface->set_property(srvCfgPropEnabled,
735 unitEnabledState);
736 srvCfgIface->set_property(srvCfgPropRunning,
737 unitRunningState);
738 internalSet = false;
739 setUSBCodeUpdateState(unitEnabledState);
740 saveUSBCodeUpdateStateToFile(unitMaskedState,
741 unitEnabledState);
742 res = req;
743 return 1;
744 }
745 #endif
746 if (req == res)
747 {
748 return 1;
749 }
750 if (updateInProgress)
751 {
752 return 0;
753 }
754 if (unitMaskedState)
755 { // block updating if masked
756 lg2::error("Invalid value specified");
757 return -EINVAL;
758 }
759 unitEnabledState = req;
760 updatedFlag |=
761 (1 << static_cast<uint8_t>(UpdatedProp::enabledState));
762 startServiceRestartTimer();
763 }
764 res = req;
765 return 1;
766 });
767
768 srvCfgIface->register_property(
769 srvCfgPropRunning, unitRunningState,
770 [this](const bool& req, bool& res) {
771 if (!internalSet)
772 {
773 #ifdef USB_CODE_UPDATE
774 if (baseUnitName == usbCodeUpdateUnitName)
775 {
776 if (unitMaskedState)
777 { // block updating if masked
778 lg2::error("Invalid value specified");
779 return -EINVAL;
780 }
781 unitEnabledState = req;
782 unitRunningState = req;
783 internalSet = true;
784 srvCfgIface->set_property(srvCfgPropEnabled,
785 unitEnabledState);
786 srvCfgIface->set_property(srvCfgPropRunning,
787 unitRunningState);
788 internalSet = false;
789 setUSBCodeUpdateState(unitEnabledState);
790 saveUSBCodeUpdateStateToFile(unitMaskedState,
791 unitEnabledState);
792 res = req;
793 return 1;
794 }
795 #endif
796 if (req == res)
797 {
798 return 1;
799 }
800 if (updateInProgress)
801 {
802 return 0;
803 }
804 if (unitMaskedState)
805 { // block updating if masked
806 lg2::error("Invalid value specified");
807 return -EINVAL;
808 }
809 unitRunningState = req;
810 updatedFlag |=
811 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
812 startServiceRestartTimer();
813 }
814 res = req;
815 return 1;
816 });
817
818 srvCfgIface->initialize();
819 if (!socketObjectPath.empty())
820 {
821 sockAttrIface->initialize();
822 }
823 return;
824 }
825
826 } // namespace service
827 } // namespace phosphor
828