blob: 24aac072cd2e8d4c9cc2a2b8d981ef5ae8cc5af3 [file] [log] [blame]
Vernon Maueryba2c0832020-07-15 10:02:38 -07001/*
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
Ed Tanousc828db72025-04-01 16:42:24 -070018#include <boost/asio/detached.hpp>
Vernon Maueryba2c0832020-07-15 10:02:38 -070019#include <boost/asio/spawn.hpp>
Chicago Duan25a0f632021-11-11 16:32:07 +080020#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
Vernon Maueryba2c0832020-07-15 10:02:38 -070027#include <fstream>
Andrew Geisslercb0f1342025-04-22 16:08:27 -050028#ifdef PERSIST_SETTINGS
29#include <nlohmann/json.hpp>
30#endif
Vernon Maueryba2c0832020-07-15 10:02:38 -070031#include <regex>
32
33extern std::unique_ptr<boost::asio::steady_timer> timer;
34extern std::map<std::string, std::shared_ptr<phosphor::service::ServiceConfig>>
35 srvMgrObjects;
36static bool updateInProgress = false;
37
38namespace phosphor
39{
40namespace service
41{
42
43static constexpr const char* overrideConfFileName = "override.conf";
44static constexpr const size_t restartTimeout = 15; // seconds
45
46static constexpr const char* systemd1UnitBasePath =
47 "/org/freedesktop/systemd1/unit/";
48static constexpr const char* systemdOverrideUnitBasePath =
49 "/etc/systemd/system/";
50
Andrew Geisslercb0f1342025-04-22 16:08:27 -050051#ifdef PERSIST_SETTINGS
52static constexpr const char* persistDataFileVersionStr = "Version";
53static constexpr const size_t persistDataFileVersion = 1;
54#endif
55
Chicago Duan25a0f632021-11-11 16:32:07 +080056#ifdef USB_CODE_UPDATE
57static constexpr const char* usbCodeUpdateStateFilePath =
58 "/var/lib/srvcfg_manager";
59static constexpr const char* usbCodeUpdateStateFile =
60 "/var/lib/srvcfg_manager/usb-code-update-state";
61static constexpr const char* emptyUsbCodeUpdateRulesFile =
62 "/etc/udev/rules.d/70-bmc-usb.rules";
Chicago Duan25a0f632021-11-11 16:32:07 +080063
64using UsbCodeUpdateStateMap = std::unordered_map<std::string, bool>;
65
66void ServiceConfig::setUSBCodeUpdateState(const bool& state)
67{
68 // Enable usb code update
69 if (state)
70 {
71 if (std::filesystem::exists(emptyUsbCodeUpdateRulesFile))
72 {
Jiaqing Zhaof27f4312022-04-02 17:42:46 +080073 lg2::info("Enable usb code update");
Chicago Duan25a0f632021-11-11 16:32:07 +080074 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 {
Jiaqing Zhaof27f4312022-04-02 17:42:46 +080089 lg2::error("Disable usb code update failed");
Chicago Duan25a0f632021-11-11 16:32:07 +080090 return;
91 }
Jiaqing Zhaof27f4312022-04-02 17:42:46 +080092 lg2::info("Disable usb code update");
Chicago Duan25a0f632021-11-11 16:32:07 +080093}
94
95void 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
112void ServiceConfig::getUSBCodeUpdateStateFromFile()
113{
114 if (!std::filesystem::exists(usbCodeUpdateStateFile))
115 {
Jiaqing Zhaof27f4312022-04-02 17:42:46 +0800116 lg2::info("usb-code-update-state file does not exist");
Chicago Duan25a0f632021-11-11 16:32:07 +0800117
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
Vernon Maueryba2c0832020-07-15 10:02:38 -0700153void 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
183void 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);
George Liua19b5092021-05-24 15:54:02 +0800211 if (subStateValue == subStateRunning ||
212 subStateValue == subStateListening)
Vernon Maueryba2c0832020-07-15 10:02:38 -0700213 {
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 }
Chicago Duan25a0f632021-11-11 16:32:07 +0800223
224#ifdef USB_CODE_UPDATE
Jiaqing Zhao430d7ea2022-04-01 23:23:25 +0800225 if (baseUnitName == usbCodeUpdateUnitName)
Chicago Duan25a0f632021-11-11 16:32:07 +0800226 {
227 getUSBCodeUpdateStateFromFile();
228 }
229#endif
Vernon Maueryba2c0832020-07-15 10:02:38 -0700230}
231
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500232void ServiceConfig::queryAndUpdateProperties(bool isStartup = false)
Vernon Maueryba2c0832020-07-15 10:02:38 -0700233{
Patrick Williamsde879722024-08-16 15:21:46 -0400234 std::string objectPath =
235 isSocketActivatedService ? socketObjectPath : serviceObjectPath;
George Liua19b5092021-05-24 15:54:02 +0800236 if (objectPath.empty())
237 {
238 return;
239 }
240
Vernon Maueryba2c0832020-07-15 10:02:38 -0700241 conn->async_method_call(
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500242 [this,
243 isStartup](boost::system::error_code ec,
244 const boost::container::flat_map<std::string, VariantType>&
245 propertyMap) {
Patrick Williamsde879722024-08-16 15:21:46 -0400246 if (ec)
Vernon Maueryba2c0832020-07-15 10:02:38 -0700247 {
Patrick Williamsde879722024-08-16 15:21:46 -0400248 lg2::error(
249 "async_method_call error: Failed to service unit properties: {EC}",
250 "EC", ec.value());
251 return;
Vernon Maueryba2c0832020-07-15 10:02:38 -0700252 }
Patrick Williamsde879722024-08-16 15:21:46 -0400253 try
Vernon Maueryba2c0832020-07-15 10:02:38 -0700254 {
Patrick Williamsde879722024-08-16 15:21:46 -0400255 updateServiceProperties(propertyMap);
256 if (!socketObjectPath.empty())
257 {
258 conn->async_method_call(
259 [this](boost::system::error_code ec,
260 const boost::container::flat_map<
261 std::string, VariantType>& propertyMap) {
262 if (ec)
263 {
264 lg2::error(
265 "async_method_call error: Failed to get all property: {EC}",
266 "EC", ec.value());
267 return;
268 }
269 try
270 {
271 updateSocketProperties(propertyMap);
272 if (!srvCfgIface)
273 {
274 registerProperties();
275 }
276 }
277 catch (const std::exception& e)
278 {
279 lg2::error(
280 "Exception in getting socket properties: {ERROR}",
281 "ERROR", e);
282 return;
283 }
284 },
285 sysdService, socketObjectPath, dBusPropIntf,
286 dBusGetAllMethod, sysdSocketIntf);
287 }
288 else if (!srvCfgIface)
289 {
290 registerProperties();
291 }
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500292 if (isStartup)
293 {
294 // On startup, load our persistent settings and compare to
295 // what was read from systemd. If they are different, use
296 // the persistent settings
297 loadStateFile();
298 }
299 else
300 {
301 // This is just an update once we're already running so
302 // write the values out to our persistent settings
303 writeStateFile();
304 }
Vernon Maueryba2c0832020-07-15 10:02:38 -0700305 }
Patrick Williamsde879722024-08-16 15:21:46 -0400306 catch (const std::exception& e)
307 {
308 lg2::error("Exception in getting socket properties: {ERROR}",
309 "ERROR", e);
310 return;
311 }
312 },
George Liua19b5092021-05-24 15:54:02 +0800313 sysdService, objectPath, dBusPropIntf, dBusGetAllMethod, sysdUnitIntf);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700314 return;
315}
316
317void ServiceConfig::createSocketOverrideConf()
318{
319 if (!socketObjectPath.empty())
320 {
321 std::string socketUnitName(instantiatedUnitName + ".socket");
322 /// Check override socket directory exist, if not create it.
323 std::filesystem::path ovrUnitFileDir(systemdOverrideUnitBasePath);
324 ovrUnitFileDir += socketUnitName;
325 ovrUnitFileDir += ".d";
326 if (!std::filesystem::exists(ovrUnitFileDir))
327 {
328 if (!std::filesystem::create_directories(ovrUnitFileDir))
329 {
George Liucb267c82022-01-05 17:53:28 +0800330 lg2::error("Unable to create the {DIR} directory.", "DIR",
331 ovrUnitFileDir);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700332 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::
333 Common::Error::InternalFailure>();
334 }
335 }
336 overrideConfDir = std::string(ovrUnitFileDir);
337 }
338}
339
Andrew Geisslercb0f1342025-04-22 16:08:27 -0500340void ServiceConfig::writeStateFile()
341{
342#ifdef PERSIST_SETTINGS
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500343 lg2::debug("Writing Persistent State File Information to {STATE_FILE}",
344 "STATE_FILE", stateFile);
Andrew Geisslercb0f1342025-04-22 16:08:27 -0500345 nlohmann::json stateMap;
346 stateMap[persistDataFileVersionStr] = persistDataFileVersion;
347 stateMap[srvCfgPropMasked] = unitMaskedState;
348 stateMap[srvCfgPropEnabled] = unitEnabledState;
349 stateMap[srvCfgPropRunning] = unitRunningState;
350
351 std::ofstream file(stateFile);
352 file << stateMap;
353 file.close();
354#endif
355}
356
Andrew Geissler51ce6e32025-04-28 15:53:30 -0500357void ServiceConfig::loadStateFile()
358{
359#ifdef PERSIST_SETTINGS
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500360 lg2::debug("Loading Persistent State File Information from {STATE_FILE}",
361 "STATE_FILE", stateFile);
Andrew Geissler51ce6e32025-04-28 15:53:30 -0500362 if (std::filesystem::exists(stateFile))
363 {
364 std::ifstream file(stateFile);
365 if (!file.good())
366 {
367 lg2::error("Error reading {FILEPATH}; delete it and continue",
368 "FILEPATH", stateFile);
369 std::filesystem::remove(stateFile);
370 // rewrite file with what was ready from systemd
371 writeStateFile();
372 return;
373 }
374
375 nlohmann::json stateMap =
376 nlohmann::json::parse(file, nullptr, false, true);
377 if (stateMap.is_discarded())
378 {
379 lg2::error("Error loading {FILEPATH}; delete it and continue",
380 "FILEPATH", stateFile);
381 std::filesystem::remove(stateFile);
382 // rewrite file with what was ready from systemd
383 writeStateFile();
384 return;
385 }
386 else if (stateMap[persistDataFileVersionStr] != persistDataFileVersion)
387 {
388 lg2::error(
389 "Error version:{VERSION} read from {FILEPATH} does not match expected {VERSION_EXP}; delete it and continue",
390 "VERSION", stateMap[persistDataFileVersionStr], "FILEPATH",
391 stateFile, "VERSION_EXP", persistDataFileVersion);
392 std::filesystem::remove(stateFile);
393 // rewrite file with what was ready from systemd
394 writeStateFile();
395 return;
396 }
397
398 // If there are any differences, the persistent config file wins so
399 // update the dbus properties and trigger a reload to apply the changes
400 if (stateMap[srvCfgPropMasked] != unitMaskedState)
401 {
402 lg2::info(
403 "Masked property for {FILEPATH} not equal. Setting to {SETTING}",
404 "FILEPATH", stateFile, "SETTING", stateMap[srvCfgPropMasked]);
405 unitMaskedState = stateMap[srvCfgPropMasked];
406 updatedFlag |=
407 (1 << static_cast<uint8_t>(UpdatedProp::maskedState));
408 startServiceRestartTimer();
409 }
410 if (stateMap[srvCfgPropEnabled] != unitEnabledState)
411 {
412 lg2::info(
413 "Enabled property for {FILEPATH} not equal. Setting to {SETTING}",
414 "FILEPATH", stateFile, "SETTING", stateMap[srvCfgPropEnabled]);
415 unitEnabledState = stateMap[srvCfgPropEnabled];
416 updatedFlag |=
417 (1 << static_cast<uint8_t>(UpdatedProp::enabledState));
418 startServiceRestartTimer();
419 }
420 if (stateMap[srvCfgPropRunning] != unitRunningState)
421 {
422 lg2::info(
423 "Running property for {FILEPATH} not equal. Setting to {SETTING}",
424 "FILEPATH", stateFile, "SETTING", stateMap[srvCfgPropRunning]);
425 unitRunningState = stateMap[srvCfgPropRunning];
426 updatedFlag |=
427 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
428 startServiceRestartTimer();
429 }
430 }
431 else
432 {
433 // Just write out what we got from systemd if no existing config file
434 writeStateFile();
435 }
436#endif
437}
438
Vernon Maueryba2c0832020-07-15 10:02:38 -0700439ServiceConfig::ServiceConfig(
440 sdbusplus::asio::object_server& srv_,
441 std::shared_ptr<sdbusplus::asio::connection>& conn_,
442 const std::string& objPath_, const std::string& baseUnitName_,
443 const std::string& instanceName_, const std::string& serviceObjPath_,
444 const std::string& socketObjPath_) :
Patrick Williamsde879722024-08-16 15:21:46 -0400445 conn(conn_), server(srv_), objPath(objPath_), baseUnitName(baseUnitName_),
Vernon Maueryba2c0832020-07-15 10:02:38 -0700446 instanceName(instanceName_), serviceObjectPath(serviceObjPath_),
447 socketObjectPath(socketObjPath_)
448{
Jiaqing Zhaof4766832022-02-28 14:28:18 +0800449 isSocketActivatedService = serviceObjectPath.empty();
Vernon Maueryba2c0832020-07-15 10:02:38 -0700450 instantiatedUnitName = baseUnitName + addInstanceName(instanceName, "@");
451 updatedFlag = 0;
Andrew Geisslercb0f1342025-04-22 16:08:27 -0500452 stateFile = srvDataBaseDir + instantiatedUnitName;
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500453 queryAndUpdateProperties(true);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700454 return;
455}
456
457std::string ServiceConfig::getSocketUnitName()
458{
459 return instantiatedUnitName + ".socket";
460}
461
462std::string ServiceConfig::getServiceUnitName()
463{
464 return instantiatedUnitName + ".service";
465}
466
467bool ServiceConfig::isMaskedOut()
468{
469 // return true if state is masked & no request to update the maskedState
470 return (
471 stateValue == "masked" &&
472 !(updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::maskedState))));
473}
474
475void ServiceConfig::stopAndApplyUnitConfig(boost::asio::yield_context yield)
476{
477 if (!updatedFlag || isMaskedOut())
478 {
479 // No updates / masked - Just return.
480 return;
481 }
George Liucb267c82022-01-05 17:53:28 +0800482 lg2::info("Applying new settings: {OBJPATH}", "OBJPATH", objPath);
George Liua19b5092021-05-24 15:54:02 +0800483 if (subStateValue == subStateRunning || subStateValue == subStateListening)
Vernon Maueryba2c0832020-07-15 10:02:38 -0700484 {
485 if (!socketObjectPath.empty())
486 {
487 systemdUnitAction(conn, yield, getSocketUnitName(), sysdStopUnit);
488 }
Jiaqing Zhaof4766832022-02-28 14:28:18 +0800489 if (!isSocketActivatedService)
George Liua19b5092021-05-24 15:54:02 +0800490 {
491 systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit);
492 }
493 else
494 {
Jiaqing Zhaof4766832022-02-28 14:28:18 +0800495 // For socket-activated service, each connection will spawn a
496 // service instance from template. Need to find all spawned service
497 // `<unitName>@<attribute>.service` and stop them through the
498 // systemdUnitAction method
George Liua19b5092021-05-24 15:54:02 +0800499 boost::system::error_code ec;
500 auto listUnits =
501 conn->yield_method_call<std::vector<ListUnitsType>>(
502 yield, ec, sysdService, sysdObjPath, sysdMgrIntf,
503 "ListUnits");
504
505 checkAndThrowInternalFailure(
506 ec, "yield_method_call error: ListUnits failed");
507
508 for (const auto& unit : listUnits)
509 {
510 const auto& service =
511 std::get<static_cast<int>(ListUnitElements::name)>(unit);
512 const auto& status =
513 std::get<static_cast<int>(ListUnitElements::subState)>(
514 unit);
Jiaqing Zhaof4766832022-02-28 14:28:18 +0800515 if (service.find(baseUnitName + "@") != std::string::npos &&
George Liua19b5092021-05-24 15:54:02 +0800516 service.find(".service") != std::string::npos &&
517 status == subStateRunning)
518 {
519 systemdUnitAction(conn, yield, service, sysdStopUnit);
520 }
521 }
522 }
Vernon Maueryba2c0832020-07-15 10:02:38 -0700523 }
524
525 if (updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::port)))
526 {
527 createSocketOverrideConf();
528 // Create override config file and write data.
529 std::string ovrCfgFile{overrideConfDir + "/" + overrideConfFileName};
530 std::string tmpFile{ovrCfgFile + "_tmp"};
531 std::ofstream cfgFile(tmpFile, std::ios::out);
532 if (!cfgFile.good())
533 {
George Liucb267c82022-01-05 17:53:28 +0800534 lg2::error("Failed to open the {TMPFILE} file.", "TMPFILE",
535 tmpFile);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700536 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
537 Error::InternalFailure>();
538 }
539
540 // Write the socket header
541 cfgFile << "[Socket]\n";
542 // Listen
543 cfgFile << "Listen" << protocol << "="
544 << "\n";
545 cfgFile << "Listen" << protocol << "=" << portNum << "\n";
546 cfgFile.close();
547
548 if (std::rename(tmpFile.c_str(), ovrCfgFile.c_str()) != 0)
549 {
George Liucb267c82022-01-05 17:53:28 +0800550 lg2::error("Failed to rename {TMPFILE} file as {OVERCFGFILE} file.",
551 "TMPFILE", tmpFile, "OVERCFGFILE", ovrCfgFile);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700552 std::remove(tmpFile.c_str());
553 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
554 Error::InternalFailure>();
555 }
556 }
557
558 if (updatedFlag & ((1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
559 (1 << static_cast<uint8_t>(UpdatedProp::enabledState))))
560 {
561 std::vector<std::string> unitFiles;
562 if (socketObjectPath.empty())
563 {
564 unitFiles = {getServiceUnitName()};
565 }
Jiaqing Zhaof4766832022-02-28 14:28:18 +0800566 else if (serviceObjectPath.empty())
George Liua19b5092021-05-24 15:54:02 +0800567 {
568 unitFiles = {getSocketUnitName()};
569 }
Vernon Maueryba2c0832020-07-15 10:02:38 -0700570 else
571 {
572 unitFiles = {getSocketUnitName(), getServiceUnitName()};
573 }
574 systemdUnitFilesStateChange(conn, yield, unitFiles, stateValue,
575 unitMaskedState, unitEnabledState);
576 }
577 return;
578}
579void ServiceConfig::restartUnitConfig(boost::asio::yield_context yield)
580{
581 if (!updatedFlag || isMaskedOut())
582 {
583 // No updates. Just return.
584 return;
585 }
586
587 if (unitRunningState)
588 {
589 if (!socketObjectPath.empty())
590 {
591 systemdUnitAction(conn, yield, getSocketUnitName(),
592 sysdRestartUnit);
593 }
Jiaqing Zhaof4766832022-02-28 14:28:18 +0800594 if (!serviceObjectPath.empty())
George Liua19b5092021-05-24 15:54:02 +0800595 {
596 systemdUnitAction(conn, yield, getServiceUnitName(),
597 sysdRestartUnit);
598 }
Vernon Maueryba2c0832020-07-15 10:02:38 -0700599 }
600
601 // Reset the flag
602 updatedFlag = 0;
603
Andrew Geissler4d4afc32025-09-16 11:04:47 -0500604 lg2::info("Applied new settings: {OBJPATH} {UNIT_RUNNING_STATE}", "OBJPATH",
605 objPath, "UNIT_RUNNING_STATE", unitRunningState);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700606
607 queryAndUpdateProperties();
608 return;
609}
610
611void ServiceConfig::startServiceRestartTimer()
612{
613 timer->expires_after(std::chrono::seconds(restartTimeout));
614 timer->async_wait([this](const boost::system::error_code& ec) {
615 if (ec == boost::asio::error::operation_aborted)
616 {
617 // Timer reset.
618 return;
619 }
620 else if (ec)
621 {
George Liucb267c82022-01-05 17:53:28 +0800622 lg2::error("async wait error: {EC}", "EC", ec.value());
Vernon Maueryba2c0832020-07-15 10:02:38 -0700623 return;
624 }
625 updateInProgress = true;
Ed Tanousc828db72025-04-01 16:42:24 -0700626 boost::asio::spawn(
Jason M. Bills543e0722025-02-26 10:33:58 -0800627 conn->get_io_context(),
628 [this](boost::asio::yield_context yield) {
629 // Stop and apply configuration for all objects
630 for (auto& srvMgrObj : srvMgrObjects)
631 {
632 auto& srvObj = srvMgrObj.second;
633 if (srvObj->updatedFlag)
634 {
635 srvObj->stopAndApplyUnitConfig(yield);
636 }
637 }
638 // Do system reload
639 systemdDaemonReload(conn, yield);
640 // restart unit config.
641 for (auto& srvMgrObj : srvMgrObjects)
642 {
643 auto& srvObj = srvMgrObj.second;
644 if (srvObj->updatedFlag)
645 {
646 srvObj->restartUnitConfig(yield);
647 }
648 }
649 updateInProgress = false;
650 },
Ed Tanousc828db72025-04-01 16:42:24 -0700651 boost::asio::detached);
Vernon Maueryba2c0832020-07-15 10:02:38 -0700652 });
653}
654
655void ServiceConfig::registerProperties()
656{
657 srvCfgIface = server.add_interface(objPath, serviceConfigIntfName);
658
659 if (!socketObjectPath.empty())
660 {
661 sockAttrIface = server.add_interface(objPath, sockAttrIntfName);
662 sockAttrIface->register_property(
663 sockAttrPropPort, portNum,
664 [this](const uint16_t& req, uint16_t& res) {
Patrick Williamsde879722024-08-16 15:21:46 -0400665 if (!internalSet)
666 {
667 if (req == res)
668 {
669 return 1;
670 }
671 if (updateInProgress)
672 {
673 return 0;
674 }
675 portNum = req;
676 updatedFlag |=
677 (1 << static_cast<uint8_t>(UpdatedProp::port));
678 startServiceRestartTimer();
679 }
680 res = req;
681 return 1;
682 });
683 }
684
685 srvCfgIface->register_property(
686 srvCfgPropMasked, unitMaskedState, [this](const bool& req, bool& res) {
Vernon Maueryba2c0832020-07-15 10:02:38 -0700687 if (!internalSet)
688 {
Patrick Williamsde879722024-08-16 15:21:46 -0400689#ifdef USB_CODE_UPDATE
690 if (baseUnitName == usbCodeUpdateUnitName)
691 {
692 unitMaskedState = req;
693 unitEnabledState = !unitMaskedState;
694 unitRunningState = !unitMaskedState;
695 internalSet = true;
696 srvCfgIface->set_property(srvCfgPropEnabled,
697 unitEnabledState);
698 srvCfgIface->set_property(srvCfgPropRunning,
699 unitRunningState);
700 srvCfgIface->set_property(srvCfgPropMasked,
701 unitMaskedState);
702 internalSet = false;
703 setUSBCodeUpdateState(unitEnabledState);
704 saveUSBCodeUpdateStateToFile(unitMaskedState,
705 unitEnabledState);
706 return 1;
707 }
708#endif
Vernon Maueryba2c0832020-07-15 10:02:38 -0700709 if (req == res)
710 {
711 return 1;
712 }
713 if (updateInProgress)
714 {
715 return 0;
716 }
Patrick Williamsde879722024-08-16 15:21:46 -0400717 unitMaskedState = req;
718 unitEnabledState = !unitMaskedState;
719 unitRunningState = !unitMaskedState;
720 updatedFlag |=
721 (1 << static_cast<uint8_t>(UpdatedProp::maskedState)) |
722 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)) |
723 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
724 internalSet = true;
725 srvCfgIface->set_property(srvCfgPropEnabled, unitEnabledState);
726 srvCfgIface->set_property(srvCfgPropRunning, unitRunningState);
727 internalSet = false;
Vernon Maueryba2c0832020-07-15 10:02:38 -0700728 startServiceRestartTimer();
729 }
730 res = req;
731 return 1;
Patrick Williamsd8effd62023-10-20 11:19:46 -0500732 });
Vernon Maueryba2c0832020-07-15 10:02:38 -0700733
Patrick Williamsde879722024-08-16 15:21:46 -0400734 srvCfgIface->register_property(
735 srvCfgPropEnabled, unitEnabledState,
736 [this](const bool& req, bool& res) {
737 if (!internalSet)
Patrick Williamsdfc72702023-05-10 07:51:18 -0500738 {
Patrick Williamsde879722024-08-16 15:21:46 -0400739#ifdef USB_CODE_UPDATE
740 if (baseUnitName == usbCodeUpdateUnitName)
741 {
742 if (unitMaskedState)
743 { // block updating if masked
744 lg2::error("Invalid value specified");
745 return -EINVAL;
746 }
747 unitEnabledState = req;
748 unitRunningState = req;
749 internalSet = true;
750 srvCfgIface->set_property(srvCfgPropEnabled,
751 unitEnabledState);
752 srvCfgIface->set_property(srvCfgPropRunning,
753 unitRunningState);
754 internalSet = false;
755 setUSBCodeUpdateState(unitEnabledState);
756 saveUSBCodeUpdateStateToFile(unitMaskedState,
757 unitEnabledState);
758 res = req;
759 return 1;
760 }
Chicago Duan25a0f632021-11-11 16:32:07 +0800761#endif
Patrick Williamsde879722024-08-16 15:21:46 -0400762 if (req == res)
763 {
764 return 1;
765 }
766 if (updateInProgress)
767 {
768 return 0;
769 }
Vernon Maueryba2c0832020-07-15 10:02:38 -0700770 if (unitMaskedState)
771 { // block updating if masked
George Liucb267c82022-01-05 17:53:28 +0800772 lg2::error("Invalid value specified");
Vernon Maueryba2c0832020-07-15 10:02:38 -0700773 return -EINVAL;
774 }
775 unitEnabledState = req;
Patrick Williamsde879722024-08-16 15:21:46 -0400776 updatedFlag |=
777 (1 << static_cast<uint8_t>(UpdatedProp::enabledState));
778 startServiceRestartTimer();
Vernon Maueryba2c0832020-07-15 10:02:38 -0700779 }
Patrick Williamsde879722024-08-16 15:21:46 -0400780 res = req;
781 return 1;
782 });
Patrick Williamsdfc72702023-05-10 07:51:18 -0500783
Patrick Williamsde879722024-08-16 15:21:46 -0400784 srvCfgIface->register_property(
785 srvCfgPropRunning, unitRunningState,
786 [this](const bool& req, bool& res) {
787 if (!internalSet)
Patrick Williamsdfc72702023-05-10 07:51:18 -0500788 {
Patrick Williamsde879722024-08-16 15:21:46 -0400789#ifdef USB_CODE_UPDATE
790 if (baseUnitName == usbCodeUpdateUnitName)
791 {
792 if (unitMaskedState)
793 { // block updating if masked
794 lg2::error("Invalid value specified");
795 return -EINVAL;
796 }
797 unitEnabledState = req;
798 unitRunningState = req;
799 internalSet = true;
800 srvCfgIface->set_property(srvCfgPropEnabled,
801 unitEnabledState);
802 srvCfgIface->set_property(srvCfgPropRunning,
803 unitRunningState);
804 internalSet = false;
805 setUSBCodeUpdateState(unitEnabledState);
806 saveUSBCodeUpdateStateToFile(unitMaskedState,
807 unitEnabledState);
808 res = req;
809 return 1;
810 }
811#endif
812 if (req == res)
813 {
814 return 1;
815 }
816 if (updateInProgress)
817 {
818 return 0;
819 }
Vernon Maueryba2c0832020-07-15 10:02:38 -0700820 if (unitMaskedState)
821 { // block updating if masked
George Liucb267c82022-01-05 17:53:28 +0800822 lg2::error("Invalid value specified");
Vernon Maueryba2c0832020-07-15 10:02:38 -0700823 return -EINVAL;
824 }
825 unitRunningState = req;
Patrick Williamsde879722024-08-16 15:21:46 -0400826 updatedFlag |=
827 (1 << static_cast<uint8_t>(UpdatedProp::runningState));
828 startServiceRestartTimer();
Vernon Maueryba2c0832020-07-15 10:02:38 -0700829 }
Patrick Williamsde879722024-08-16 15:21:46 -0400830 res = req;
831 return 1;
832 });
Vernon Maueryba2c0832020-07-15 10:02:38 -0700833
834 srvCfgIface->initialize();
835 if (!socketObjectPath.empty())
836 {
837 sockAttrIface->initialize();
838 }
839 return;
840}
841
842} // namespace service
843} // namespace phosphor