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 "utils.hpp"
17 
18 void checkAndThrowInternalFailure(boost::system::error_code& ec,
19                                   const std::string& msg)
20 {
21     if (ec)
22     {
23         std::string msgToLog = ec.message() + (msg.empty() ? "" : " - " + msg);
24         phosphor::logging::log<phosphor::logging::level::ERR>(msgToLog.c_str());
25         phosphor::logging::elog<
26             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
27     }
28     return;
29 }
30 
31 void systemdDaemonReload(
32     const std::shared_ptr<sdbusplus::asio::connection>& conn,
33     boost::asio::yield_context yield)
34 {
35     boost::system::error_code ec;
36     conn->yield_method_call<>(yield, ec, sysdService, sysdObjPath, sysdMgrIntf,
37                               sysdReloadMethod);
38     checkAndThrowInternalFailure(ec, "daemon-reload operation failed");
39     return;
40 }
41 
42 static inline uint32_t getJobId(const std::string& path)
43 {
44     auto pos = path.rfind("/");
45     if (pos == std::string::npos)
46     {
47         phosphor::logging::log<phosphor::logging::level::ERR>(
48             "Unable to get job id from job path");
49         phosphor::logging::elog<
50             sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
51     }
52     return static_cast<uint32_t>(std::stoul(path.substr(pos + 1)));
53 }
54 
55 void systemdUnitAction(const std::shared_ptr<sdbusplus::asio::connection>& conn,
56                        boost::asio::yield_context yield,
57                        const std::string& unitName,
58                        const std::string& actionMethod)
59 {
60     boost::system::error_code ec;
61     auto jobPath = conn->yield_method_call<sdbusplus::message::object_path>(
62         yield, ec, sysdService, sysdObjPath, sysdMgrIntf, actionMethod,
63         unitName, sysdReplaceMode);
64     checkAndThrowInternalFailure(ec,
65                                  "Systemd operation failed, " + actionMethod);
66     // Query the job till it doesn't exist anymore.
67     // this way we guarantee that queued job id is done.
68     // this is needed to make sure dependency list on units are
69     // properly handled.
70     while (1)
71     {
72         ec.clear();
73         conn->yield_method_call<>(yield, ec, sysdService, sysdObjPath,
74                                   sysdMgrIntf, sysdGetJobMethod,
75                                   getJobId(jobPath.str));
76         if (ec)
77         {
78             if (ec.value() == boost::system::errc::no_such_file_or_directory)
79             {
80                 // Queued job is done, return now
81                 return;
82             }
83             phosphor::logging::log<phosphor::logging::level::ERR>(
84                 "Systemd operation failed for job query");
85             phosphor::logging::elog<sdbusplus::xyz::openbmc_project::Common::
86                                         Error::InternalFailure>();
87         }
88         boost::asio::steady_timer sleepTimer(conn->get_io_context());
89         sleepTimer.expires_after(std::chrono::milliseconds(20));
90         ec.clear();
91         sleepTimer.async_wait(yield[ec]);
92         checkAndThrowInternalFailure(ec, "Systemd operation timer error");
93     }
94     return;
95 }
96 
97 void systemdUnitFilesStateChange(
98     const std::shared_ptr<sdbusplus::asio::connection>& conn,
99     boost::asio::yield_context yield, const std::vector<std::string>& unitFiles,
100     const std::string& unitState, bool maskedState, bool enabledState)
101 {
102     boost::system::error_code ec;
103 
104     if (unitState == stateMasked && !maskedState)
105     {
106         conn->yield_method_call<>(yield, ec, sysdService, sysdObjPath,
107                                   sysdMgrIntf, "UnmaskUnitFiles", unitFiles,
108                                   false);
109         checkAndThrowInternalFailure(ec, "Systemd UnmaskUnitFiles() failed.");
110     }
111     else if (unitState != stateMasked && maskedState)
112     {
113         conn->yield_method_call<>(yield, ec, sysdService, sysdObjPath,
114                                   sysdMgrIntf, "MaskUnitFiles", unitFiles,
115                                   false, false);
116         checkAndThrowInternalFailure(ec, "Systemd MaskUnitFiles() failed.");
117     }
118     ec.clear();
119     if (unitState != stateEnabled && enabledState)
120     {
121         conn->yield_method_call<>(yield, ec, sysdService, sysdObjPath,
122                                   sysdMgrIntf, "EnableUnitFiles", unitFiles,
123                                   false, false);
124         checkAndThrowInternalFailure(ec, "Systemd EnableUnitFiles() failed.");
125     }
126     else if (unitState != stateDisabled && !enabledState)
127     {
128         conn->yield_method_call<>(yield, ec, sysdService, sysdObjPath,
129                                   sysdMgrIntf, "DisableUnitFiles", unitFiles,
130                                   false);
131         checkAndThrowInternalFailure(ec, "Systemd DisableUnitFiles() failed.");
132     }
133     return;
134 }
135