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