1 /**
2  * Copyright © 2017 IBM 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 "monitor.hpp"
17 
18 #include <phosphor-logging/log.hpp>
19 
20 namespace phosphor
21 {
22 namespace unit
23 {
24 namespace failure
25 {
26 
27 using namespace phosphor::logging;
28 
29 constexpr auto FAILED_STATE = "failed";
30 constexpr auto START_METHOD = "StartUnit";
31 constexpr auto STOP_METHOD = "StopUnit";
32 
33 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
34 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
35 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
36 constexpr auto SYSTEMD_PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
37 constexpr auto SYSTEMD_UNIT_INTERFACE = "org.freedesktop.systemd1.Unit";
38 
39 void Monitor::analyze()
40 {
41     if (inFailedState(std::move(getSourceUnitPath())))
42     {
43         runTargetAction();
44     }
45 }
46 
47 bool Monitor::inFailedState(const std::string&& path)
48 {
49     std::variant<std::string> property;
50 
51     auto method = bus.new_method_call(SYSTEMD_SERVICE, path.c_str(),
52                                       SYSTEMD_PROPERTY_INTERFACE, "Get");
53 
54     method.append(SYSTEMD_UNIT_INTERFACE, "ActiveState");
55 
56     auto reply = bus.call(method);
57     if (reply.is_method_error())
58     {
59         log<level::ERR>("Failed reading ActiveState DBus property",
60                         entry("UNIT=%s", source.c_str()));
61         // TODO openbmc/openbmc#851 - Once available, throw returned error
62         throw std::runtime_error("Failed reading ActiveState DBus property");
63     }
64 
65     reply.read(property);
66 
67     auto value = std::get<std::string>(property);
68     return (value == FAILED_STATE);
69 }
70 
71 std::string Monitor::getSourceUnitPath()
72 {
73     sdbusplus::message::object_path path;
74 
75     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
76                                       SYSTEMD_INTERFACE, "GetUnit");
77     method.append(source);
78     auto reply = bus.call(method);
79 
80     if (reply.is_method_error())
81     {
82         log<level::ERR>("Failed GetUnit DBus method call",
83                         entry("UNIT=%s", source.c_str()));
84         // TODO openbmc/openbmc#851 - Once available, throw returned error
85         throw std::runtime_error("Failed GetUnit DBus method call");
86     }
87 
88     reply.read(path);
89 
90     return static_cast<std::string>(path);
91 }
92 
93 void Monitor::runTargetAction()
94 {
95     // Start or stop the target unit
96     auto methodCall = (action == Action::start) ? START_METHOD : STOP_METHOD;
97 
98     log<level::INFO>("The source unit is in failed state, "
99                      "running target action",
100                      entry("SOURCE=%s", source.c_str()),
101                      entry("TARGET=%s", target.c_str()),
102                      entry("ACTION=%s", methodCall));
103 
104     auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
105                                             SYSTEMD_INTERFACE, methodCall);
106     method.append(target);
107     method.append("replace");
108 
109     auto reply = bus.call(method);
110 
111     if (reply.is_method_error())
112     {
113         log<level::ERR>("Failed to run action on the target unit",
114                         entry("UNIT=%s", target.c_str()));
115         // TODO openbmc/openbmc#851 - Once available, throw returned error
116         throw std::runtime_error("Failed to run action on the target unit");
117     }
118 }
119 } // namespace failure
120 } // namespace unit
121 } // namespace phosphor
122