blob: 72277d1d04d4bd7945df1f07b2167c4f2a62848d [file] [log] [blame]
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +05301/**
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
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053017#include "argument.hpp"
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053018#include "watchdog.hpp"
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053019
Patrick Venture8f6c5152018-09-11 17:45:33 -070020#include <iostream>
William A. Kennington III73c2cfb2018-09-12 18:01:37 -070021#include <optional>
Patrick Venture8f6c5152018-09-11 17:45:33 -070022#include <phosphor-logging/elog-errors.hpp>
23#include <phosphor-logging/elog.hpp>
24#include <phosphor-logging/log.hpp>
25#include <string>
26#include <xyz/openbmc_project/Common/error.hpp>
27
William A. Kennington III1232a152018-02-02 15:57:34 -080028using phosphor::watchdog::ArgumentParser;
29using phosphor::watchdog::Watchdog;
William A. Kennington III93975262018-02-02 16:00:50 -080030using sdbusplus::xyz::openbmc_project::State::server::convertForMessage;
William A. Kennington III1232a152018-02-02 15:57:34 -080031
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053032static void exitWithError(const char* err, char** argv)
33{
William A. Kennington III1232a152018-02-02 15:57:34 -080034 ArgumentParser::usage(argv);
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053035 std::cerr << "ERROR: " << err << "\n";
36 exit(EXIT_FAILURE);
37}
38
William A. Kennington III93975262018-02-02 16:00:50 -080039void printActionTargets(
Patrick Venture8f6c5152018-09-11 17:45:33 -070040 const std::map<Watchdog::Action, std::string>& actionTargets)
William A. Kennington III93975262018-02-02 16:00:50 -080041{
42 std::cerr << "Action Targets:\n";
43 for (const auto& actionTarget : actionTargets)
44 {
Patrick Venture8f6c5152018-09-11 17:45:33 -070045 std::cerr << " " << convertForMessage(actionTarget.first) << " -> "
46 << actionTarget.second << "\n";
William A. Kennington III93975262018-02-02 16:00:50 -080047 }
48 std::cerr << std::flush;
49}
50
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053051int main(int argc, char** argv)
52{
Vishwanatha Subbanna7e146552017-05-29 17:03:33 +053053 using namespace phosphor::logging;
Patrick Venture8f6c5152018-09-11 17:45:33 -070054 using InternalFailure =
55 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053056 // Read arguments.
William A. Kennington III1232a152018-02-02 15:57:34 -080057 auto options = ArgumentParser(argc, argv);
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053058
Patrick Venture09eebe32017-08-11 15:23:17 -070059 // Parse out continue argument.
60 auto continueParam = (options)["continue"];
61 // Default it to exit on watchdog timeout
62 auto continueAfterTimeout = false;
William A. Kennington III5d307182018-01-23 22:00:55 -080063 if (!continueParam.empty())
Patrick Venture09eebe32017-08-11 15:23:17 -070064 {
65 continueAfterTimeout = true;
66 }
67
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053068 // Parse out path argument.
William A. Kennington III5d307182018-01-23 22:00:55 -080069 auto pathParam = (options)["path"];
70 if (pathParam.empty())
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053071 {
72 exitWithError("Path not specified.", argv);
73 }
William A. Kennington III5d307182018-01-23 22:00:55 -080074 if (pathParam.size() > 1)
75 {
76 exitWithError("Multiple paths specified.", argv);
77 }
78 auto path = pathParam.back();
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053079
80 // Parse out service name argument
William A. Kennington III5d307182018-01-23 22:00:55 -080081 auto serviceParam = (options)["service"];
82 if (serviceParam.empty())
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053083 {
84 exitWithError("Service not specified.", argv);
85 }
William A. Kennington III5d307182018-01-23 22:00:55 -080086 if (serviceParam.size() > 1)
87 {
88 exitWithError("Multiple services specified.", argv);
89 }
90 auto service = serviceParam.back();
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +053091
92 // Parse out target argument. It is fine if the caller does not
93 // pass this if they are not interested in calling into any target
94 // on meeting a condition.
William A. Kennington III5d307182018-01-23 22:00:55 -080095 auto targetParam = (options)["target"];
96 if (targetParam.size() > 1)
97 {
98 exitWithError("Multiple targets specified.", argv);
99 }
William A. Kennington III1232a152018-02-02 15:57:34 -0800100 std::map<Watchdog::Action, Watchdog::TargetName> actionTargets;
Patrick Venture8f6c5152018-09-11 17:45:33 -0700101 if (!targetParam.empty())
102 {
William A. Kennington III1232a152018-02-02 15:57:34 -0800103 auto target = targetParam.back();
104 actionTargets[Watchdog::Action::HardReset] = target;
105 actionTargets[Watchdog::Action::PowerOff] = target;
106 actionTargets[Watchdog::Action::PowerCycle] = target;
107 }
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +0530108
William A. Kennington III27df4b52018-02-02 16:02:05 -0800109 // Parse out the action_target arguments. We allow one target to map
110 // to an action. These targets can replace the target specified above.
111 for (const auto& actionTarget : (options)["action_target"])
112 {
113 size_t keyValueSplit = actionTarget.find("=");
114 if (keyValueSplit == std::string::npos)
115 {
116 exitWithError(
Patrick Venture8f6c5152018-09-11 17:45:33 -0700117 "Invalid action_target format, expect <action>=<target>.",
118 argv);
William A. Kennington III27df4b52018-02-02 16:02:05 -0800119 }
120
121 std::string key = actionTarget.substr(0, keyValueSplit);
Patrick Venture8f6c5152018-09-11 17:45:33 -0700122 std::string value = actionTarget.substr(keyValueSplit + 1);
William A. Kennington III27df4b52018-02-02 16:02:05 -0800123
124 // Convert an action from a fully namespaced value
125 Watchdog::Action action;
126 try
127 {
128 action = Watchdog::convertActionFromString(key);
129 }
Patrick Venture8f6c5152018-09-11 17:45:33 -0700130 catch (const sdbusplus::exception::InvalidEnumString&)
William A. Kennington III27df4b52018-02-02 16:02:05 -0800131 {
132 exitWithError("Bad action specified.", argv);
133 }
134
135 actionTargets[action] = std::move(value);
136 }
William A. Kennington III93975262018-02-02 16:00:50 -0800137 printActionTargets(actionTargets);
138
William A. Kennington IIId1331082018-02-27 18:47:05 -0800139 // Parse out the fallback settings for the watchdog. Note that we require
140 // both of the fallback arguments to do anything here, but having a fallback
141 // is entirely optional.
142 auto fallbackActionParam = (options)["fallback_action"];
143 auto fallbackIntervalParam = (options)["fallback_interval"];
144 if (fallbackActionParam.empty() ^ fallbackIntervalParam.empty())
145 {
146 exitWithError("Only one of the fallback options was specified.", argv);
147 }
148 if (fallbackActionParam.size() > 1 || fallbackIntervalParam.size() > 1)
149 {
150 exitWithError("Multiple fallbacks specified.", argv);
151 }
William A. Kennington III73c2cfb2018-09-12 18:01:37 -0700152 std::optional<Watchdog::Fallback> fallback;
William A. Kennington IIId1331082018-02-27 18:47:05 -0800153 if (!fallbackActionParam.empty())
154 {
155 Watchdog::Action action;
156 try
157 {
Patrick Venture8f6c5152018-09-11 17:45:33 -0700158 action =
159 Watchdog::convertActionFromString(fallbackActionParam.back());
William A. Kennington IIId1331082018-02-27 18:47:05 -0800160 }
Patrick Venture8f6c5152018-09-11 17:45:33 -0700161 catch (const sdbusplus::exception::InvalidEnumString&)
William A. Kennington IIId1331082018-02-27 18:47:05 -0800162 {
163 exitWithError("Bad action specified.", argv);
164 }
165 uint64_t interval;
166 try
167 {
168 interval = std::stoull(fallbackIntervalParam.back());
169 }
Patrick Venture8f6c5152018-09-11 17:45:33 -0700170 catch (const std::logic_error&)
William A. Kennington IIId1331082018-02-27 18:47:05 -0800171 {
Patrick Venture8f6c5152018-09-11 17:45:33 -0700172 exitWithError("Failed to convert fallback interval to integer.",
173 argv);
William A. Kennington IIId1331082018-02-27 18:47:05 -0800174 }
175 fallback = Watchdog::Fallback{
176 .action = action,
177 .interval = interval,
William A. Kennington III22352192018-02-27 18:51:44 -0800178 .always = false,
William A. Kennington IIId1331082018-02-27 18:47:05 -0800179 };
180 }
181
William A. Kennington III22352192018-02-27 18:51:44 -0800182 auto fallbackAlwaysParam = (options)["fallback_always"];
183 if (!fallbackAlwaysParam.empty())
184 {
185 if (!fallback)
186 {
187 exitWithError("Specified the fallback should always be enabled but "
Patrick Venture8f6c5152018-09-11 17:45:33 -0700188 "no fallback provided.",
189 argv);
William A. Kennington III22352192018-02-27 18:51:44 -0800190 }
191 fallback->always = true;
192 }
193
Vishwanatha Subbanna7e146552017-05-29 17:03:33 +0530194 sd_event* event = nullptr;
195 auto r = sd_event_default(&event);
196 if (r < 0)
197 {
198 log<level::ERR>("Error creating a default sd_event handler");
199 return r;
200 }
201 phosphor::watchdog::EventPtr eventP{event};
202 event = nullptr;
203
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +0530204 // Get a handle to system dbus.
205 auto bus = sdbusplus::bus::new_default();
Vishwanatha Subbanna7e146552017-05-29 17:03:33 +0530206
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +0530207 // Add systemd object manager.
208 sdbusplus::server::manager::manager(bus, path.c_str());
209
210 // Attach the bus to sd_event to service user requests
211 bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
212
Vishwanatha Subbanna4d5ef3f2017-05-31 18:54:22 +0530213 try
Vishwanatha Subbanna7e146552017-05-29 17:03:33 +0530214 {
Vishwanatha Subbanna4d5ef3f2017-05-31 18:54:22 +0530215 // Create a watchdog object
William A. Kennington IIId1331082018-02-27 18:47:05 -0800216 Watchdog watchdog(bus, path.c_str(), eventP, std::move(actionTargets),
Patrick Venture8f6c5152018-09-11 17:45:33 -0700217 std::move(fallback));
William A. Kennington IIId1331082018-02-27 18:47:05 -0800218
Vishwanatha Subbanna4d5ef3f2017-05-31 18:54:22 +0530219 // Claim the bus
220 bus.request_name(service.c_str());
221
William A. Kennington III825f4982018-02-27 19:10:56 -0800222 // Loop until our timer expires and we don't want to continue
223 while (continueAfterTimeout || !watchdog.timerExpired())
Vishwanatha Subbanna7e146552017-05-29 17:03:33 +0530224 {
Vishwanatha Subbanna4d5ef3f2017-05-31 18:54:22 +0530225 // -1 denotes wait for ever
226 r = sd_event_run(eventP.get(), (uint64_t)-1);
227 if (r < 0)
228 {
229 log<level::ERR>("Error waiting for events");
230 elog<InternalFailure>();
231 }
Vishwanatha Subbanna7e146552017-05-29 17:03:33 +0530232 }
233 }
Patrick Venture8f6c5152018-09-11 17:45:33 -0700234 catch (InternalFailure& e)
Vishwanatha Subbanna4d5ef3f2017-05-31 18:54:22 +0530235 {
236 phosphor::logging::commit<InternalFailure>();
237
238 // Need a coredump in the error cases.
239 std::terminate();
240 }
Vishwanatha Subbanna15b1dc12017-05-23 15:16:13 +0530241 return 0;
242}