blob: 909ea0ed71e63e612a776b76589b2bf4701457c9 [file] [log] [blame]
Mike Capps385b1982021-06-28 10:40:42 -04001/**
2 * Copyright © 2021 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
Mike Cappsff968232021-06-30 18:24:39 -040017#include "config.h"
18
Mike Capps385b1982021-06-28 10:40:42 -040019#include "sdbusplus.hpp"
20
21#include <CLI/CLI.hpp>
Matt Spinlerb564e152021-10-29 13:10:18 -050022#include <nlohmann/json.hpp>
Mike Capps385b1982021-06-28 10:40:42 -040023#include <sdbusplus/bus.hpp>
24
Matt Spinlerb564e152021-10-29 13:10:18 -050025#include <chrono>
26#include <filesystem>
Mike Capps385b1982021-06-28 10:40:42 -040027#include <iomanip>
28#include <iostream>
29
30using SDBusPlus = phosphor::fan::util::SDBusPlus;
31
Mike Capps16aab352021-06-30 10:17:21 -040032constexpr auto systemdMgrIface = "org.freedesktop.systemd1.Manager";
33constexpr auto systemdPath = "/org/freedesktop/systemd1";
34constexpr auto systemdService = "org.freedesktop.systemd1";
Mike Cappsff968232021-06-30 18:24:39 -040035constexpr auto phosphorServiceName = "phosphor-fan-control@0.service";
Matt Spinlerb564e152021-10-29 13:10:18 -050036constexpr auto dumpFile = "/tmp/fan_control_dump.json";
Mike Capps16aab352021-06-30 10:17:21 -040037
38enum
39{
40 FAN_NAMES = 0,
41 PATH_MAP = 1,
42 IFACES = 2,
43 METHOD = 3
44};
45
Matt Spinlerb564e152021-10-29 13:10:18 -050046struct DumpQuery
47{
48 std::string section;
49 std::string name;
50 std::vector<std::string> properties;
51};
52
Mike Capps385b1982021-06-28 10:40:42 -040053/**
54 * @function extracts fan name from dbus path string (last token where
55 * delimiter is the / character), with proper bounds checking.
56 * @param[in] path - D-Bus path
57 * @return just the fan name.
58 */
59
60std::string justFanName(std::string const& path)
61{
62 std::string fanName;
63
64 auto itr = path.rfind("/");
65 if (itr != std::string::npos && itr < path.size())
66 {
67 fanName = path.substr(1 + itr);
68 }
69
70 return fanName;
71}
72
73/**
74 * @function produces subtree paths whose names match fan token names.
75 * @param[in] path - D-Bus path to obtain subtree from
76 * @param[in] iface - interface to obtain subTreePaths from
77 * @param[in] fans - label matching tokens to filter by
78 * @param[in] shortPath - flag to shorten fan token
79 * @return map of paths by fan name
80 */
81
82std::map<std::string, std::vector<std::string>>
83 getPathsFromIface(const std::string& path, const std::string& iface,
84 const std::vector<std::string>& fans,
85 bool shortPath = false)
86{
87 std::map<std::string, std::vector<std::string>> dest;
88
89 for (auto& path :
90 SDBusPlus::getSubTreePathsRaw(SDBusPlus::getBus(), path, iface, 0))
91 {
92 for (auto& fan : fans)
93 {
94 if (shortPath)
95 {
96 if (fan == justFanName(path))
97 {
98 dest[fan].push_back(path);
99 }
100 }
101 else if (std::string::npos != path.find(fan + "_"))
102 {
103 dest[fan].push_back(path);
104 }
105 }
106 }
107
108 return dest;
109}
110
111/**
Mike Capps530c6552021-06-29 09:50:38 -0400112 * @function consolidated function to load dbus paths and fan names
113 */
114auto loadDBusData()
115{
116 auto& bus{SDBusPlus::getBus()};
117
118 std::vector<std::string> fanNames;
119
120 // paths by D-bus interface,fan name
121 std::map<std::string, std::map<std::string, std::vector<std::string>>>
122 pathMap;
123
124 std::string method("RPM");
125
126 std::map<const std::string, const std::string> interfaces{
127 {"FanSpeed", "xyz.openbmc_project.Control.FanSpeed"},
128 {"FanPwm", "xyz.openbmc_project.Control.FanPwm"},
129 {"SensorValue", "xyz.openbmc_project.Sensor.Value"},
130 {"Item", "xyz.openbmc_project.Inventory.Item"},
131 {"OpStatus", "xyz.openbmc_project.State.Decorator.OperationalStatus"}};
132
133 std::map<const std::string, const std::string> paths{
134 {"motherboard",
135 "/xyz/openbmc_project/inventory/system/chassis/motherboard"},
136 {"tach", "/xyz/openbmc_project/sensors/fan_tach"}};
137
138 // build a list of all fans
139 for (auto& path : SDBusPlus::getSubTreePathsRaw(bus, paths["tach"],
140 interfaces["FanSpeed"], 0))
141 {
142 // special case where we build the list of fans
143 auto fan = justFanName(path);
144 fan = fan.substr(0, fan.rfind("_"));
145 fanNames.push_back(fan);
146 }
147
148 // retry with PWM mode if none found
149 if (0 == fanNames.size())
150 {
151 method = "PWM";
152
153 for (auto& path : SDBusPlus::getSubTreePathsRaw(
154 bus, paths["tach"], interfaces["FanPwm"], 0))
155 {
156 // special case where we build the list of fans
157 auto fan = justFanName(path);
158 fan = fan.substr(0, fan.rfind("_"));
159 fanNames.push_back(fan);
160 }
161 }
162
163 // load tach sensor paths for each fan
164 pathMap["tach"] =
165 getPathsFromIface(paths["tach"], interfaces["SensorValue"], fanNames);
166
167 // load inventory Item data for each fan
168 pathMap["inventory"] = getPathsFromIface(
169 paths["motherboard"], interfaces["Item"], fanNames, true);
170
171 // load operational status data for each fan
172 pathMap["opstatus"] = getPathsFromIface(
173 paths["motherboard"], interfaces["OpStatus"], fanNames, true);
174
175 return std::make_tuple(fanNames, pathMap, interfaces, method);
176}
177
178/**
Mike Capps385b1982021-06-28 10:40:42 -0400179 * @function gets the states of phosphor-fanctl. equivalent to
180 * "systemctl status phosphor-fan-control@0"
181 * @return a list of several (sub)states of fanctl (loaded,
182 * active, running) as well as D-Bus properties representing
183 * BMC states (bmc state,chassis power state, host state)
184 */
185
186std::array<std::string, 6> getStates()
187{
188 using DBusTuple =
189 std::tuple<std::string, std::string, std::string, std::string,
190 std::string, std::string, sdbusplus::message::object_path,
191 uint32_t, std::string, sdbusplus::message::object_path>;
192
193 std::array<std::string, 6> ret;
194
Mike Capps16aab352021-06-30 10:17:21 -0400195 std::vector<std::string> services{phosphorServiceName};
Mike Capps385b1982021-06-28 10:40:42 -0400196
197 try
198 {
199 auto fields{SDBusPlus::callMethodAndRead<std::vector<DBusTuple>>(
200 systemdService, systemdPath, systemdMgrIface, "ListUnitsByNames",
201 services)};
202
203 if (fields.size() > 0)
204 {
205 ret[0] = std::get<2>(fields[0]);
206 ret[1] = std::get<3>(fields[0]);
207 ret[2] = std::get<4>(fields[0]);
208 }
209 else
210 {
211 std::cout << "No units found for systemd service: " << services[0]
212 << std::endl;
213 }
214 }
Mike Capps530c6552021-06-29 09:50:38 -0400215 catch (const std::exception& e)
Mike Capps385b1982021-06-28 10:40:42 -0400216 {
217 std::cerr << "Failure retrieving phosphor-fan-control states: "
218 << e.what() << std::endl;
219 }
220
221 std::string path("/xyz/openbmc_project/state/bmc0");
222 std::string iface("xyz.openbmc_project.State.BMC");
223 ret[3] =
224 SDBusPlus::getProperty<std::string>(path, iface, "CurrentBMCState");
225
226 path = "/xyz/openbmc_project/state/chassis0";
227 iface = "xyz.openbmc_project.State.Chassis";
228 ret[4] =
229 SDBusPlus::getProperty<std::string>(path, iface, "CurrentPowerState");
230
231 path = "/xyz/openbmc_project/state/host0";
232 iface = "xyz.openbmc_project.State.Host";
233 ret[5] =
234 SDBusPlus::getProperty<std::string>(path, iface, "CurrentHostState");
235
236 return ret;
237}
238
239/**
Mike Capps530c6552021-06-29 09:50:38 -0400240 * @function helper to determine interface type from a given control method
241 */
242std::string ifaceTypeFromMethod(const std::string& method)
243{
244 return (method == "RPM" ? "FanSpeed" : "FanPwm");
245}
246
247/**
Mike Capps385b1982021-06-28 10:40:42 -0400248 * @function performs the "status" command from the cmdline.
249 * get states and sensor data and output to the console
250 */
251void status()
252{
253 using std::cout;
254 using std::endl;
255 using std::setw;
256
Mike Capps530c6552021-06-29 09:50:38 -0400257 auto busData = loadDBusData();
Mike Capps16aab352021-06-30 10:17:21 -0400258 auto& method = std::get<METHOD>(busData);
Mike Capps385b1982021-06-28 10:40:42 -0400259
Mike Capps530c6552021-06-29 09:50:38 -0400260 std::string property;
Mike Capps385b1982021-06-28 10:40:42 -0400261
262 // get the state,substate of fan-control and obmc
263 auto states = getStates();
264
265 // print the header
266 cout << "Fan Control Service State : " << states[0] << ", " << states[1]
267 << "(" << states[2] << ")" << endl;
268 cout << endl;
269 cout << "CurrentBMCState : " << states[3] << endl;
270 cout << "CurrentPowerState : " << states[4] << endl;
271 cout << "CurrentHostState : " << states[5] << endl;
272 cout << endl;
273 cout << " FAN "
Mike Cappsff968232021-06-30 18:24:39 -0400274 << "TARGET(" << method << ") FEEDBACK(RPM) PRESENT"
275 << " FUNCTIONAL" << endl;
Mike Capps385b1982021-06-28 10:40:42 -0400276 cout << "==============================================================="
277 << endl;
278
Mike Capps16aab352021-06-30 10:17:21 -0400279 auto& fanNames{std::get<FAN_NAMES>(busData)};
280 auto& pathMap{std::get<PATH_MAP>(busData)};
281 auto& interfaces{std::get<IFACES>(busData)};
Mike Capps530c6552021-06-29 09:50:38 -0400282
Mike Capps385b1982021-06-28 10:40:42 -0400283 for (auto& fan : fanNames)
284 {
Mike Cappsff968232021-06-30 18:24:39 -0400285 cout << " " << fan << setw(14);
Mike Capps385b1982021-06-28 10:40:42 -0400286
287 // get the target RPM
288 property = "Target";
289 cout << SDBusPlus::getProperty<uint64_t>(
Mike Capps530c6552021-06-29 09:50:38 -0400290 pathMap["tach"][fan][0],
291 interfaces[ifaceTypeFromMethod(method)], property)
Mike Cappsff968232021-06-30 18:24:39 -0400292 << setw(12);
Mike Capps385b1982021-06-28 10:40:42 -0400293
294 // get the sensor RPM
295 property = "Value";
Mike Capps530c6552021-06-29 09:50:38 -0400296
Mike Cappsff968232021-06-30 18:24:39 -0400297 std::ostringstream output;
Mike Capps385b1982021-06-28 10:40:42 -0400298 int numRotors = pathMap["tach"][fan].size();
299 // print tach readings for each rotor
300 for (auto& path : pathMap["tach"][fan])
301 {
Mike Cappsff968232021-06-30 18:24:39 -0400302 output << SDBusPlus::getProperty<double>(
Mike Capps385b1982021-06-28 10:40:42 -0400303 path, interfaces["SensorValue"], property);
304
305 // dont print slash on last rotor
306 if (--numRotors)
Mike Cappsff968232021-06-30 18:24:39 -0400307 output << "/";
Mike Capps385b1982021-06-28 10:40:42 -0400308 }
Mike Cappsff968232021-06-30 18:24:39 -0400309 cout << setw(18) << output.str() << setw(10);
Mike Capps385b1982021-06-28 10:40:42 -0400310
Mike Capps530c6552021-06-29 09:50:38 -0400311 // print the Present property
Mike Capps385b1982021-06-28 10:40:42 -0400312 property = "Present";
313 std::string val;
314 for (auto& path : pathMap["inventory"][fan])
315 {
316 try
317 {
318 if (SDBusPlus::getProperty<bool>(path, interfaces["Item"],
319 property))
320 {
321 val = "true";
322 }
323 else
324 {
325 val = "false";
326 }
327 }
Patrick Williamsddb773b2021-10-06 11:24:49 -0500328 catch (const phosphor::fan::util::DBusPropertyError&)
Mike Capps385b1982021-06-28 10:40:42 -0400329 {
330 val = "Unknown";
331 }
332 cout << val;
333 }
334
335 cout << setw(13);
336
Mike Capps530c6552021-06-29 09:50:38 -0400337 // and the functional property
Mike Capps385b1982021-06-28 10:40:42 -0400338 property = "Functional";
339 for (auto& path : pathMap["opstatus"][fan])
340 {
341 try
342 {
343 if (SDBusPlus::getProperty<bool>(path, interfaces["OpStatus"],
344 property))
345 {
346 val = "true";
347 }
348 else
349 {
350 val = "false";
351 }
352 }
Patrick Williamsddb773b2021-10-06 11:24:49 -0500353 catch (const phosphor::fan::util::DBusPropertyError&)
Mike Capps385b1982021-06-28 10:40:42 -0400354 {
355 val = "Unknown";
356 }
357 cout << val;
358 }
359
360 cout << endl;
361 }
362}
363
364/**
Mike Capps530c6552021-06-29 09:50:38 -0400365 * @function print target RPM/PWM and tach readings from each fan
366 */
367void get()
368{
369 using std::cout;
370 using std::endl;
371 using std::setw;
372
373 auto busData = loadDBusData();
374
Mike Capps16aab352021-06-30 10:17:21 -0400375 auto& fanNames{std::get<FAN_NAMES>(busData)};
376 auto& pathMap{std::get<PATH_MAP>(busData)};
377 auto& interfaces{std::get<IFACES>(busData)};
378 auto& method = std::get<METHOD>(busData);
Mike Capps530c6552021-06-29 09:50:38 -0400379
380 std::string property;
381
382 // print the header
383 cout << "TARGET SENSOR" << setw(11) << "TARGET(" << method
Mike Cappsff968232021-06-30 18:24:39 -0400384 << ") FEEDBACK SENSOR FEEDBACK(RPM)" << endl;
Mike Capps530c6552021-06-29 09:50:38 -0400385 cout << "==============================================================="
386 << endl;
387
388 for (auto& fan : fanNames)
389 {
390 if (pathMap["tach"][fan].size() == 0)
391 continue;
392 // print just the sensor name
393 auto shortPath = pathMap["tach"][fan][0];
394 shortPath = justFanName(shortPath);
Mike Cappsff968232021-06-30 18:24:39 -0400395 cout << shortPath << setw(18);
Mike Capps530c6552021-06-29 09:50:38 -0400396
397 // print its target RPM/PWM
398 property = "Target";
399 cout << SDBusPlus::getProperty<uint64_t>(
400 pathMap["tach"][fan][0],
401 interfaces[ifaceTypeFromMethod(method)], property)
402 << setw(12) << " ";
403
404 // print readings for each rotor
405 property = "Value";
406
407 auto indent = 0U;
408 for (auto& path : pathMap["tach"][fan])
409 {
410 cout << setw(indent);
Mike Cappsff968232021-06-30 18:24:39 -0400411 cout << justFanName(path) << setw(16)
Mike Capps530c6552021-06-29 09:50:38 -0400412 << SDBusPlus::getProperty<double>(
413 path, interfaces["SensorValue"], property)
414 << endl;
415
416 if (0 == indent)
Mike Cappsff968232021-06-30 18:24:39 -0400417 indent = 42;
Mike Capps530c6552021-06-29 09:50:38 -0400418 }
419 }
420}
421
422/**
Mike Capps16aab352021-06-30 10:17:21 -0400423 * @function set fan[s] to a target RPM
424 */
Mike Cappsff968232021-06-30 18:24:39 -0400425void set(uint64_t target, std::vector<std::string>& fanList)
Mike Capps16aab352021-06-30 10:17:21 -0400426{
427 auto busData = loadDBusData();
Mike Capps16aab352021-06-30 10:17:21 -0400428 auto& bus{SDBusPlus::getBus()};
429 auto& pathMap{std::get<PATH_MAP>(busData)};
430 auto& interfaces{std::get<IFACES>(busData)};
431 auto& method = std::get<METHOD>(busData);
432
433 std::string ifaceType(method == "RPM" ? "FanSpeed" : "FanPwm");
434
435 // stop the fan-control service
Mike Cappsff968232021-06-30 18:24:39 -0400436 SDBusPlus::callMethodAndRead<sdbusplus::message::object_path>(
Mike Capps16aab352021-06-30 10:17:21 -0400437 systemdService, systemdPath, systemdMgrIface, "StopUnit",
438 phosphorServiceName, "replace");
439
440 if (fanList.size() == 0)
441 {
442 fanList = std::get<FAN_NAMES>(busData);
443 }
444
445 for (auto& fan : fanList)
446 {
447 try
448 {
449 auto paths(pathMap["tach"].find(fan));
450
451 if (pathMap["tach"].end() == paths)
452 {
453 // try again, maybe it was a sensor name instead of a fan name
454 for (const auto& [fanName, sensors] : pathMap["tach"])
455 {
456 for (const auto& path : sensors)
457 {
458 std::string sensor(path.substr(path.rfind("/")));
459
460 if (sensor.size() > 0)
461 {
462 sensor = sensor.substr(1);
463
464 if (sensor == fan)
465 {
466 paths = pathMap["tach"].find(fanName);
467
468 break;
469 }
470 }
471 }
472 }
473 }
474
475 if (pathMap["tach"].end() == paths)
476 {
477 std::cout << "Could not find tach path for fan: " << fan
478 << std::endl;
479 continue;
480 }
481
482 // set the target RPM
483 SDBusPlus::setProperty<uint64_t>(bus, paths->second[0],
484 interfaces[ifaceType], "Target",
485 std::move(target));
486 }
487 catch (const phosphor::fan::util::DBusPropertyError& e)
488 {
489 std::cerr << "Cannot set target rpm for " << fan
490 << " caught D-Bus exception: " << e.what() << std::endl;
491 }
492 }
493}
494
495/**
496 * @function restart fan-control to allow it to manage fan speeds
497 */
498void resume()
499{
500 try
501 {
502 auto retval =
503 SDBusPlus::callMethodAndRead<sdbusplus::message::object_path>(
504 systemdService, systemdPath, systemdMgrIface, "StartUnit",
505 phosphorServiceName, "replace");
506 }
507 catch (const phosphor::fan::util::DBusMethodError& e)
508 {
509 std::cerr << "Unable to start fan control: " << e.what() << std::endl;
510 }
511}
512
513/**
Mike Cappsff968232021-06-30 18:24:39 -0400514 * @function force reload of control files by sending HUP signal
515 */
516void reload()
517{
518#ifdef CONTROL_USE_JSON
519 try
520 {
521 SDBusPlus::callMethod(systemdService, systemdPath, systemdMgrIface,
522 "KillUnit", phosphorServiceName, "main", SIGHUP);
523 }
524 catch (const phosphor::fan::util::DBusPropertyError& e)
525 {
526 std::cerr << "Unable to reload configuration files: " << e.what()
527 << std::endl;
528 }
529#else
530 // YAML config doesn't support SIGHUP-based reloads
531 std::cerr << "Error: reload function unavailable for YAML-configuration"
532 << std::endl;
533#endif
534}
535
536/**
Mike Capps7c8b97f2021-10-06 14:04:18 -0400537 * @function dump the FlightRecorder log data
538 */
Matt Spinlerb5c21a22021-10-14 16:52:12 -0500539void dumpFanControl()
Mike Capps7c8b97f2021-10-06 14:04:18 -0400540{
541 try
542 {
543 SDBusPlus::callMethod(systemdService, systemdPath, systemdMgrIface,
544 "KillUnit", phosphorServiceName, "main", SIGUSR1);
Matt Spinlerb564e152021-10-29 13:10:18 -0500545 std::cout << "Fan control dump written to: " << dumpFile << std::endl;
Mike Capps7c8b97f2021-10-06 14:04:18 -0400546 }
547 catch (const phosphor::fan::util::DBusPropertyError& e)
548 {
Matt Spinlerb5c21a22021-10-14 16:52:12 -0500549 std::cerr << "Unable to dump fan control: " << e.what() << std::endl;
Mike Capps7c8b97f2021-10-06 14:04:18 -0400550 }
551}
552
553/**
Matt Spinlerb564e152021-10-29 13:10:18 -0500554 * @function Query items in the dump file
555 */
556void queryDumpFile(const DumpQuery& dq)
557{
558 nlohmann::json output;
559 std::ifstream file{dumpFile};
560
561 if (!file.good())
562 {
563 std::cerr << "Unable to open dump file, please run 'fanctl dump'.\n";
564 return;
565 }
566
567 auto dumpData = nlohmann::json::parse(file);
568
569 if (!dumpData.contains(dq.section))
570 {
571 std::cerr << "Error: Dump file does not contain " << dq.section
572 << " section"
573 << "\n";
574 return;
575 }
576
577 const auto& section = dumpData.at(dq.section);
578
579 for (const auto& [key1, values1] : section.items())
580 {
581 if (dq.name.empty() || (key1.find(dq.name) != std::string::npos))
582 {
583 // If no properties specified, print the whole JSON value
584 if (dq.properties.empty())
585 {
586 output[key1] = values1;
587 continue;
588 }
589
590 // Look for properties both one and two levels down.
591 // Future improvement: Use recursion.
592 for (const auto& [key2, values2] : values1.items())
593 {
594 for (const auto& prop : dq.properties)
595 {
596 if (prop == key2)
597 {
598 output[key1][prop] = values2;
599 }
600 }
601
602 for (const auto& [key3, values3] : values2.items())
603 {
604 for (const auto& prop : dq.properties)
605 {
606 if (prop == key3)
607 {
608 output[key1][prop] = values3;
609 }
610 }
611 }
612 }
613 }
614 }
615
616 if (!output.empty())
617 {
618 std::cout << std::setw(4) << output << "\n";
619 }
620}
621
622/**
Mike Cappsff968232021-06-30 18:24:39 -0400623 * @function setup the CLI object to accept all options
624 */
Matt Spinlerb564e152021-10-29 13:10:18 -0500625void initCLI(CLI::App& app, uint64_t& target, std::vector<std::string>& fanList,
626 DumpQuery& dq)
Mike Cappsff968232021-06-30 18:24:39 -0400627{
628 app.set_help_flag("-h,--help", "Print this help page and exit.");
629
630 // App requires only 1 subcommand to be given
631 app.require_subcommand(1);
632
633 // This represents the command given
634 auto commands = app.add_option_group("Commands");
635
636 // status method
637 std::string strHelp("Prints fan target/tach readings, present/functional "
638 "states, and fan-monitor/BMC/Power service status");
Mike Capps49766182021-09-27 22:32:46 -0400639
Mike Cappsff968232021-06-30 18:24:39 -0400640 auto cmdStatus = commands->add_subcommand("status", strHelp);
641 cmdStatus->set_help_flag("-h, --help", strHelp);
642 cmdStatus->require_option(0);
643
644 // get method
645 strHelp = "Get the current fan target and feedback speeds for all rotors";
646 auto cmdGet = commands->add_subcommand("get", strHelp);
647 cmdGet->set_help_flag("-h, --help", strHelp);
648 cmdGet->require_option(0);
649
650 // set method
651 strHelp = "Set target (all rotors) for one-or-more fans";
652 auto cmdSet = commands->add_subcommand("set", strHelp);
653 strHelp = R"(set <TARGET> [TARGET SENSOR(S)]
654 <TARGET>
655 - RPM/PWM target to set the fans
656[TARGET SENSOR LIST]
657- list of target sensors to set)";
658 cmdSet->set_help_flag("-h, --help", strHelp);
659 cmdSet->add_option("target", target, "RPM/PWM target to set the fans");
660 cmdSet->add_option(
661 "fan list", fanList,
662 "[optional] list of 1+ fans to set target RPM/PWM (default: all)");
663 cmdSet->require_option();
664
665 strHelp = "Reload phosphor-fan configuration files";
666 auto cmdReload = commands->add_subcommand("reload", strHelp);
667 cmdReload->set_help_flag("-h, --help", strHelp);
668 cmdReload->require_option(0);
669
670 strHelp = "Resume running phosphor-fan-control";
671 auto cmdResume = commands->add_subcommand("resume", strHelp);
672 cmdResume->set_help_flag("-h, --help", strHelp);
673 cmdResume->require_option(0);
Mike Capps7c8b97f2021-10-06 14:04:18 -0400674
675 // Dump method
676 auto cmdDump = commands->add_subcommand(
677 "dump", "Dump the FlightRecorder diagnostic log");
678 cmdDump->set_help_flag("-h, --help",
679 "Dump the FlightRecorder diagnostic log");
680 cmdDump->require_option(0);
Matt Spinlerb564e152021-10-29 13:10:18 -0500681
682#ifdef CONTROL_USE_JSON
683 auto cmdDumpQuery =
684 commands->add_subcommand("query_dump", "Query the dump file");
685
686 cmdDumpQuery->set_help_flag("-h, --help", "Query the dump file");
687 cmdDumpQuery
688 ->add_option("-s, --section", dq.section, "Dump file section name")
689 ->required();
690 cmdDumpQuery->add_option("-n, --name", dq.name,
691 "Optional dump file entry name (or substring)");
692 cmdDumpQuery->add_option("-p, --properties", dq.properties,
693 "Optional list of dump file property names");
694#endif
Mike Cappsff968232021-06-30 18:24:39 -0400695}
696
697/**
Mike Capps385b1982021-06-28 10:40:42 -0400698 * @function main entry point for the application
699 */
700int main(int argc, char* argv[])
701{
702 auto rc = 0;
Mike Capps16aab352021-06-30 10:17:21 -0400703 uint64_t target{0U};
704 std::vector<std::string> fanList;
Matt Spinlerb564e152021-10-29 13:10:18 -0500705 DumpQuery dq;
Mike Capps385b1982021-06-28 10:40:42 -0400706
707 try
708 {
Mike Capps49766182021-09-27 22:32:46 -0400709 CLI::App app{"Manually control, get fan tachs, view status, and resume "
710 "automatic control of all fans within a chassis. Full "
711 "documentation can be found at the readme:\n"
712 "https://github.com/openbmc/phosphor-fan-presence/tree/"
713 "master/docs/control/fanctl"};
Mike Capps385b1982021-06-28 10:40:42 -0400714
Matt Spinlerb564e152021-10-29 13:10:18 -0500715 initCLI(app, target, fanList, dq);
Mike Capps16aab352021-06-30 10:17:21 -0400716
Mike Capps385b1982021-06-28 10:40:42 -0400717 CLI11_PARSE(app, argc, argv);
718
Mike Cappsff968232021-06-30 18:24:39 -0400719 if (app.got_subcommand("get"))
Mike Capps385b1982021-06-28 10:40:42 -0400720 {
Mike Cappsff968232021-06-30 18:24:39 -0400721 get();
Mike Capps385b1982021-06-28 10:40:42 -0400722 }
Mike Capps16aab352021-06-30 10:17:21 -0400723 else if (app.got_subcommand("set"))
724 {
725 set(target, fanList);
726 }
Mike Cappsff968232021-06-30 18:24:39 -0400727 else if (app.got_subcommand("reload"))
Mike Capps530c6552021-06-29 09:50:38 -0400728 {
Mike Cappsff968232021-06-30 18:24:39 -0400729 reload();
Mike Capps530c6552021-06-29 09:50:38 -0400730 }
Mike Capps16aab352021-06-30 10:17:21 -0400731 else if (app.got_subcommand("resume"))
732 {
733 resume();
734 }
Mike Cappsff968232021-06-30 18:24:39 -0400735 else if (app.got_subcommand("status"))
736 {
737 status();
738 }
Mike Capps7c8b97f2021-10-06 14:04:18 -0400739 else if (app.got_subcommand("dump"))
740 {
Matt Spinlerb5c21a22021-10-14 16:52:12 -0500741 dumpFanControl();
Mike Capps7c8b97f2021-10-06 14:04:18 -0400742 }
Matt Spinlerb564e152021-10-29 13:10:18 -0500743#ifdef CONTROL_USE_JSON
744 else if (app.got_subcommand("query_dump"))
745 {
746 queryDumpFile(dq);
747 }
748#endif
Mike Capps385b1982021-06-28 10:40:42 -0400749 }
750 catch (const std::exception& e)
751 {
752 rc = -1;
753 std::cerr << argv[0] << " failed: " << e.what() << std::endl;
754 }
755
756 return rc;
757}