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_), 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 |= 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