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