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