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 = 228 isSocketActivatedService ? socketObjectPath : 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_), server(srv_), objPath(objPath_), baseUnitName(baseUnitName_), 326 instanceName(instanceName_), serviceObjectPath(serviceObjPath_), 327 socketObjectPath(socketObjPath_) 328 { 329 isSocketActivatedService = serviceObjectPath.empty(); 330 instantiatedUnitName = baseUnitName + addInstanceName(instanceName, "@"); 331 updatedFlag = 0; 332 queryAndUpdateProperties(); 333 return; 334 } 335 336 std::string ServiceConfig::getSocketUnitName() 337 { 338 return instantiatedUnitName + ".socket"; 339 } 340 341 std::string ServiceConfig::getServiceUnitName() 342 { 343 return instantiatedUnitName + ".service"; 344 } 345 346 bool ServiceConfig::isMaskedOut() 347 { 348 // return true if state is masked & no request to update the maskedState 349 return ( 350 stateValue == "masked" && 351 !(updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::maskedState)))); 352 } 353 354 void ServiceConfig::stopAndApplyUnitConfig(boost::asio::yield_context yield) 355 { 356 if (!updatedFlag || isMaskedOut()) 357 { 358 // No updates / masked - Just return. 359 return; 360 } 361 lg2::info("Applying new settings: {OBJPATH}", "OBJPATH", objPath); 362 if (subStateValue == subStateRunning || subStateValue == subStateListening) 363 { 364 if (!socketObjectPath.empty()) 365 { 366 systemdUnitAction(conn, yield, getSocketUnitName(), sysdStopUnit); 367 } 368 if (!isSocketActivatedService) 369 { 370 systemdUnitAction(conn, yield, getServiceUnitName(), sysdStopUnit); 371 } 372 else 373 { 374 // For socket-activated service, each connection will spawn a 375 // service instance from template. Need to find all spawned service 376 // `<unitName>@<attribute>.service` and stop them through the 377 // systemdUnitAction method 378 boost::system::error_code ec; 379 auto listUnits = 380 conn->yield_method_call<std::vector<ListUnitsType>>( 381 yield, ec, sysdService, sysdObjPath, sysdMgrIntf, 382 "ListUnits"); 383 384 checkAndThrowInternalFailure( 385 ec, "yield_method_call error: ListUnits failed"); 386 387 for (const auto& unit : listUnits) 388 { 389 const auto& service = 390 std::get<static_cast<int>(ListUnitElements::name)>(unit); 391 const auto& status = 392 std::get<static_cast<int>(ListUnitElements::subState)>( 393 unit); 394 if (service.find(baseUnitName + "@") != std::string::npos && 395 service.find(".service") != std::string::npos && 396 status == subStateRunning) 397 { 398 systemdUnitAction(conn, yield, service, sysdStopUnit); 399 } 400 } 401 } 402 } 403 404 if (updatedFlag & (1 << static_cast<uint8_t>(UpdatedProp::port))) 405 { 406 createSocketOverrideConf(); 407 // Create override config file and write data. 408 std::string ovrCfgFile{overrideConfDir + "/" + overrideConfFileName}; 409 std::string tmpFile{ovrCfgFile + "_tmp"}; 410 std::ofstream cfgFile(tmpFile, std::ios::out); 411 if (!cfgFile.good()) 412 { 413 lg2::error("Failed to open the {TMPFILE} file.", "TMPFILE", 414 tmpFile); 415 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common:: 416 Error::InternalFailure>(); 417 } 418 419 // Write the socket header 420 cfgFile << "[Socket]\n"; 421 // Listen 422 cfgFile << "Listen" << protocol << "=" 423 << "\n"; 424 cfgFile << "Listen" << protocol << "=" << portNum << "\n"; 425 cfgFile.close(); 426 427 if (std::rename(tmpFile.c_str(), ovrCfgFile.c_str()) != 0) 428 { 429 lg2::error("Failed to rename {TMPFILE} file as {OVERCFGFILE} file.", 430 "TMPFILE", tmpFile, "OVERCFGFILE", ovrCfgFile); 431 std::remove(tmpFile.c_str()); 432 phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common:: 433 Error::InternalFailure>(); 434 } 435 } 436 437 if (updatedFlag & ((1 << static_cast<uint8_t>(UpdatedProp::maskedState)) | 438 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)))) 439 { 440 std::vector<std::string> unitFiles; 441 if (socketObjectPath.empty()) 442 { 443 unitFiles = {getServiceUnitName()}; 444 } 445 else if (serviceObjectPath.empty()) 446 { 447 unitFiles = {getSocketUnitName()}; 448 } 449 else 450 { 451 unitFiles = {getSocketUnitName(), getServiceUnitName()}; 452 } 453 systemdUnitFilesStateChange(conn, yield, unitFiles, stateValue, 454 unitMaskedState, unitEnabledState); 455 } 456 return; 457 } 458 void ServiceConfig::restartUnitConfig(boost::asio::yield_context yield) 459 { 460 if (!updatedFlag || isMaskedOut()) 461 { 462 // No updates. Just return. 463 return; 464 } 465 466 if (unitRunningState) 467 { 468 if (!socketObjectPath.empty()) 469 { 470 systemdUnitAction(conn, yield, getSocketUnitName(), 471 sysdRestartUnit); 472 } 473 if (!serviceObjectPath.empty()) 474 { 475 systemdUnitAction(conn, yield, getServiceUnitName(), 476 sysdRestartUnit); 477 } 478 } 479 480 // Reset the flag 481 updatedFlag = 0; 482 483 lg2::info("Applied new settings: {OBJPATH}", "OBJPATH", objPath); 484 485 queryAndUpdateProperties(); 486 return; 487 } 488 489 void ServiceConfig::startServiceRestartTimer() 490 { 491 timer->expires_after(std::chrono::seconds(restartTimeout)); 492 timer->async_wait([this](const boost::system::error_code& ec) { 493 if (ec == boost::asio::error::operation_aborted) 494 { 495 // Timer reset. 496 return; 497 } 498 else if (ec) 499 { 500 lg2::error("async wait error: {EC}", "EC", ec.value()); 501 return; 502 } 503 updateInProgress = true; 504 boost::asio::spawn(conn->get_io_context(), 505 [this](boost::asio::yield_context yield) { 506 // Stop and apply configuration for all objects 507 for (auto& srvMgrObj : srvMgrObjects) 508 { 509 auto& srvObj = srvMgrObj.second; 510 if (srvObj->updatedFlag) 511 { 512 srvObj->stopAndApplyUnitConfig(yield); 513 } 514 } 515 // Do system reload 516 systemdDaemonReload(conn, yield); 517 // restart unit config. 518 for (auto& srvMgrObj : srvMgrObjects) 519 { 520 auto& srvObj = srvMgrObj.second; 521 if (srvObj->updatedFlag) 522 { 523 srvObj->restartUnitConfig(yield); 524 } 525 } 526 updateInProgress = false; 527 }, 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 |= 554 (1 << static_cast<uint8_t>(UpdatedProp::port)); 555 startServiceRestartTimer(); 556 } 557 res = req; 558 return 1; 559 }); 560 } 561 562 srvCfgIface->register_property( 563 srvCfgPropMasked, unitMaskedState, [this](const bool& req, bool& res) { 564 if (!internalSet) 565 { 566 #ifdef USB_CODE_UPDATE 567 if (baseUnitName == usbCodeUpdateUnitName) 568 { 569 unitMaskedState = req; 570 unitEnabledState = !unitMaskedState; 571 unitRunningState = !unitMaskedState; 572 internalSet = true; 573 srvCfgIface->set_property(srvCfgPropEnabled, 574 unitEnabledState); 575 srvCfgIface->set_property(srvCfgPropRunning, 576 unitRunningState); 577 srvCfgIface->set_property(srvCfgPropMasked, 578 unitMaskedState); 579 internalSet = false; 580 setUSBCodeUpdateState(unitEnabledState); 581 saveUSBCodeUpdateStateToFile(unitMaskedState, 582 unitEnabledState); 583 return 1; 584 } 585 #endif 586 if (req == res) 587 { 588 return 1; 589 } 590 if (updateInProgress) 591 { 592 return 0; 593 } 594 unitMaskedState = req; 595 unitEnabledState = !unitMaskedState; 596 unitRunningState = !unitMaskedState; 597 updatedFlag |= 598 (1 << static_cast<uint8_t>(UpdatedProp::maskedState)) | 599 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)) | 600 (1 << static_cast<uint8_t>(UpdatedProp::runningState)); 601 internalSet = true; 602 srvCfgIface->set_property(srvCfgPropEnabled, unitEnabledState); 603 srvCfgIface->set_property(srvCfgPropRunning, unitRunningState); 604 internalSet = false; 605 startServiceRestartTimer(); 606 } 607 res = req; 608 return 1; 609 }); 610 611 srvCfgIface->register_property( 612 srvCfgPropEnabled, unitEnabledState, 613 [this](const bool& req, bool& res) { 614 if (!internalSet) 615 { 616 #ifdef USB_CODE_UPDATE 617 if (baseUnitName == usbCodeUpdateUnitName) 618 { 619 if (unitMaskedState) 620 { // block updating if masked 621 lg2::error("Invalid value specified"); 622 return -EINVAL; 623 } 624 unitEnabledState = req; 625 unitRunningState = req; 626 internalSet = true; 627 srvCfgIface->set_property(srvCfgPropEnabled, 628 unitEnabledState); 629 srvCfgIface->set_property(srvCfgPropRunning, 630 unitRunningState); 631 internalSet = false; 632 setUSBCodeUpdateState(unitEnabledState); 633 saveUSBCodeUpdateStateToFile(unitMaskedState, 634 unitEnabledState); 635 res = req; 636 return 1; 637 } 638 #endif 639 if (req == res) 640 { 641 return 1; 642 } 643 if (updateInProgress) 644 { 645 return 0; 646 } 647 if (unitMaskedState) 648 { // block updating if masked 649 lg2::error("Invalid value specified"); 650 return -EINVAL; 651 } 652 unitEnabledState = req; 653 updatedFlag |= 654 (1 << static_cast<uint8_t>(UpdatedProp::enabledState)); 655 startServiceRestartTimer(); 656 } 657 res = req; 658 return 1; 659 }); 660 661 srvCfgIface->register_property( 662 srvCfgPropRunning, unitRunningState, 663 [this](const bool& req, bool& res) { 664 if (!internalSet) 665 { 666 #ifdef USB_CODE_UPDATE 667 if (baseUnitName == usbCodeUpdateUnitName) 668 { 669 if (unitMaskedState) 670 { // block updating if masked 671 lg2::error("Invalid value specified"); 672 return -EINVAL; 673 } 674 unitEnabledState = req; 675 unitRunningState = req; 676 internalSet = true; 677 srvCfgIface->set_property(srvCfgPropEnabled, 678 unitEnabledState); 679 srvCfgIface->set_property(srvCfgPropRunning, 680 unitRunningState); 681 internalSet = false; 682 setUSBCodeUpdateState(unitEnabledState); 683 saveUSBCodeUpdateStateToFile(unitMaskedState, 684 unitEnabledState); 685 res = req; 686 return 1; 687 } 688 #endif 689 if (req == res) 690 { 691 return 1; 692 } 693 if (updateInProgress) 694 { 695 return 0; 696 } 697 if (unitMaskedState) 698 { // block updating if masked 699 lg2::error("Invalid value specified"); 700 return -EINVAL; 701 } 702 unitRunningState = req; 703 updatedFlag |= 704 (1 << static_cast<uint8_t>(UpdatedProp::runningState)); 705 startServiceRestartTimer(); 706 } 707 res = req; 708 return 1; 709 }); 710 711 srvCfgIface->initialize(); 712 if (!socketObjectPath.empty()) 713 { 714 sockAttrIface->initialize(); 715 } 716 return; 717 } 718 719 } // namespace service 720 } // namespace phosphor 721