1 /**
2  * Copyright © 2022 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 "config.h"
17 
18 #ifndef CONTROL_USE_JSON
19 #include "manager.hpp"
20 
21 #include <CLI/CLI.hpp>
22 #else
23 #include "../utils/flight_recorder.hpp"
24 #include "json/manager.hpp"
25 #endif
26 
27 #include "dbus_paths.hpp"
28 #include "sdbusplus.hpp"
29 #include "sdeventplus.hpp"
30 
31 #include <phosphor-logging/log.hpp>
32 #include <sdbusplus/bus.hpp>
33 #include <sdeventplus/event.hpp>
34 #include <sdeventplus/source/signal.hpp>
35 #include <stdplus/signal.hpp>
36 
37 #include <fstream>
38 
39 using namespace phosphor::fan::control;
40 using namespace phosphor::logging;
41 
42 #ifdef CONTROL_USE_JSON
dumpFlightRecorder()43 void dumpFlightRecorder()
44 {
45     nlohmann::json data;
46     phosphor::fan::control::json::FlightRecorder::instance().dump(data);
47     std::ofstream file{json::Manager::dumpFile};
48     file << std::setw(4) << data;
49 }
50 #endif
51 
main(int argc,char * argv[])52 int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
53 {
54     auto event = phosphor::fan::util::SDEventPlus::getEvent();
55 
56 #ifndef CONTROL_USE_JSON
57     CLI::App app{"Phosphor Fan Control"};
58 
59     bool init = false;
60     bool control = false;
61     app.add_flag("-i,--init", init, "Sets fans to full speed, delays, exits");
62     app.add_flag("-c,--control", control, "Start fan control algorithm");
63     app.require_option();
64 
65     try
66     {
67         app.parse(argc, argv);
68     }
69     catch (const CLI::Error& e)
70     {
71         return app.exit(e);
72     }
73 
74     Mode mode;
75     if (init)
76     {
77         mode = Mode::init;
78     }
79     else if (control)
80     {
81         mode = Mode::control;
82     }
83 #endif
84 
85     // Attach the event object to the bus object so we can
86     // handle both sd_events (for the timers) and dbus signals.
87     phosphor::fan::util::SDBusPlus::getBus().attach_event(
88         event.get(), SD_EVENT_PRIORITY_NORMAL);
89 
90     try
91     {
92 #ifdef CONTROL_USE_JSON
93         phosphor::fan::control::json::FlightRecorder::instance().log(
94             "main", "Startup");
95         json::Manager manager(event);
96 
97         // Handle loading fan control's config file(s)
98         phosphor::fan::JsonConfig config(
99             std::bind(&json::Manager::load, &manager));
100 
101         // Enable SIGHUP handling to reload JSON configs
102         stdplus::signal::block(SIGHUP);
103         sdeventplus::source::Signal signal(
104             event, SIGHUP,
105             std::bind(&json::Manager::sighupHandler, &manager,
106                       std::placeholders::_1, std::placeholders::_2));
107 
108         // Enable SIGUSR1 handling to dump the flight recorder
109         stdplus::signal::block(SIGUSR1);
110         sdeventplus::source::Signal sigUsr1(
111             event, SIGUSR1,
112             std::bind(&json::Manager::dumpDebugData, &manager,
113                       std::placeholders::_1, std::placeholders::_2));
114 
115         phosphor::fan::util::SDBusPlus::getBus().request_name(CONTROL_BUSNAME);
116 #else
117         Manager manager(phosphor::fan::util::SDBusPlus::getBus(), event, mode);
118 
119         // Init mode will just set fans to max and delay
120         if (mode == Mode::init)
121         {
122             manager.doInit(event);
123             return 0;
124         }
125 #endif
126         return event.loop();
127     }
128     // Log the useful metadata on these exceptions and let the app
129     // return 1 so it is restarted without a core dump.
130     catch (const phosphor::fan::util::DBusServiceError& e)
131     {
132         log<level::ERR>("Uncaught DBus service lookup failure exception",
133                         entry("PATH=%s", e.path.c_str()),
134                         entry("INTERFACE=%s", e.interface.c_str()));
135     }
136     catch (const phosphor::fan::util::DBusMethodError& e)
137     {
138         log<level::ERR>("Uncaught DBus method failure exception",
139                         entry("BUSNAME=%s", e.busName.c_str()),
140                         entry("PATH=%s", e.path.c_str()),
141                         entry("INTERFACE=%s", e.interface.c_str()),
142                         entry("METHOD=%s", e.method.c_str()));
143     }
144     catch (const phosphor::fan::util::DBusPropertyError& e)
145     {
146         log<level::ERR>("Uncaught DBus property access failure exception",
147                         entry("BUSNAME=%s", e.busName.c_str()),
148                         entry("PATH=%s", e.path.c_str()),
149                         entry("INTERFACE=%s", e.interface.c_str()),
150                         entry("PROPERTY=%s", e.property.c_str()));
151     }
152     catch (std::exception& e)
153     {
154 #ifdef CONTROL_USE_JSON
155         phosphor::fan::control::json::FlightRecorder::instance().log(
156             "main", "Unexpected exception exit");
157         dumpFlightRecorder();
158 #endif
159         throw;
160     }
161 
162 #ifdef CONTROL_USE_JSON
163     phosphor::fan::control::json::FlightRecorder::instance().log(
164         "main", "Abnormal exit");
165     dumpFlightRecorder();
166 #endif
167 
168     return 1;
169 }
170