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