1*69ae808eSJason M. Bills /*
2*69ae808eSJason M. Bills // Copyright (c) 2019 Intel Corporation
3*69ae808eSJason M. Bills //
4*69ae808eSJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License");
5*69ae808eSJason M. Bills // you may not use this file except in compliance with the License.
6*69ae808eSJason M. Bills // You may obtain a copy of the License at
7*69ae808eSJason M. Bills //
8*69ae808eSJason M. Bills // http://www.apache.org/licenses/LICENSE-2.0
9*69ae808eSJason M. Bills //
10*69ae808eSJason M. Bills // Unless required by applicable law or agreed to in writing, software
11*69ae808eSJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS,
12*69ae808eSJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*69ae808eSJason M. Bills // See the License for the specific language governing permissions and
14*69ae808eSJason M. Bills // limitations under the License.
15*69ae808eSJason M. Bills */
16*69ae808eSJason M. Bills
17*69ae808eSJason M. Bills #include "callback_manager.hpp"
18*69ae808eSJason M. Bills
19*69ae808eSJason M. Bills #include <boost/asio/io_context.hpp>
20*69ae808eSJason M. Bills #include <boost/asio/steady_timer.hpp>
21*69ae808eSJason M. Bills #include <boost/container/flat_map.hpp>
22*69ae808eSJason M. Bills #include <sdbusplus/asio/connection.hpp>
23*69ae808eSJason M. Bills #include <sdbusplus/asio/object_server.hpp>
24*69ae808eSJason M. Bills
25*69ae808eSJason M. Bills #include <variant>
26*69ae808eSJason M. Bills
27*69ae808eSJason M. Bills constexpr const char* fatalLedPath =
28*69ae808eSJason M. Bills "/xyz/openbmc_project/led/groups/status_critical";
29*69ae808eSJason M. Bills constexpr const char* criticalLedPath =
30*69ae808eSJason M. Bills "/xyz/openbmc_project/led/groups/status_non_critical";
31*69ae808eSJason M. Bills constexpr const char* warningLedPath =
32*69ae808eSJason M. Bills "/xyz/openbmc_project/led/groups/status_degraded";
33*69ae808eSJason M. Bills constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok";
34*69ae808eSJason M. Bills
35*69ae808eSJason M. Bills constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
36*69ae808eSJason M. Bills constexpr const char* ledAssertProp = "Asserted";
37*69ae808eSJason M. Bills constexpr const char* ledManagerBusname =
38*69ae808eSJason M. Bills "xyz.openbmc_project.LED.GroupManager";
39*69ae808eSJason M. Bills
40*69ae808eSJason M. Bills std::unique_ptr<AssociationManager> associationManager;
41*69ae808eSJason M. Bills
42*69ae808eSJason M. Bills enum class StatusSetting
43*69ae808eSJason M. Bills {
44*69ae808eSJason M. Bills none,
45*69ae808eSJason M. Bills ok,
46*69ae808eSJason M. Bills warn,
47*69ae808eSJason M. Bills critical,
48*69ae808eSJason M. Bills fatal
49*69ae808eSJason M. Bills };
50*69ae808eSJason M. Bills
51*69ae808eSJason M. Bills constexpr const bool debug = false;
52*69ae808eSJason M. Bills
53*69ae808eSJason M. Bills // final led state tracking
54*69ae808eSJason M. Bills StatusSetting currentPriority = StatusSetting::none;
55*69ae808eSJason M. Bills
56*69ae808eSJason M. Bills // maps of <object-path, <property, asserted>>
57*69ae808eSJason M. Bills boost::container::flat_map<std::string,
58*69ae808eSJason M. Bills boost::container::flat_map<std::string, bool>>
59*69ae808eSJason M. Bills fatalAssertMap;
60*69ae808eSJason M. Bills boost::container::flat_map<std::string,
61*69ae808eSJason M. Bills boost::container::flat_map<std::string, bool>>
62*69ae808eSJason M. Bills criticalAssertMap;
63*69ae808eSJason M. Bills boost::container::flat_map<std::string,
64*69ae808eSJason M. Bills boost::container::flat_map<std::string, bool>>
65*69ae808eSJason M. Bills warningAssertMap;
66*69ae808eSJason M. Bills
assertedInMap(const boost::container::flat_map<std::string,boost::container::flat_map<std::string,bool>> & map)67*69ae808eSJason M. Bills std::vector<std::string> assertedInMap(
68*69ae808eSJason M. Bills const boost::container::flat_map<
69*69ae808eSJason M. Bills std::string, boost::container::flat_map<std::string, bool>>& map)
70*69ae808eSJason M. Bills {
71*69ae808eSJason M. Bills std::vector<std::string> ret;
72*69ae808eSJason M. Bills // if any of the properties are true, return true
73*69ae808eSJason M. Bills for (const auto& pair : map)
74*69ae808eSJason M. Bills {
75*69ae808eSJason M. Bills for (const auto& item : pair.second)
76*69ae808eSJason M. Bills {
77*69ae808eSJason M. Bills if (item.second)
78*69ae808eSJason M. Bills {
79*69ae808eSJason M. Bills ret.push_back(pair.first);
80*69ae808eSJason M. Bills }
81*69ae808eSJason M. Bills }
82*69ae808eSJason M. Bills }
83*69ae808eSJason M. Bills return ret;
84*69ae808eSJason M. Bills }
85*69ae808eSJason M. Bills
updateLedStatus(std::shared_ptr<sdbusplus::asio::connection> & conn,bool forceRefresh=false)86*69ae808eSJason M. Bills void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn,
87*69ae808eSJason M. Bills bool forceRefresh = false)
88*69ae808eSJason M. Bills {
89*69ae808eSJason M. Bills std::vector<std::string> fatalVector = assertedInMap(fatalAssertMap);
90*69ae808eSJason M. Bills bool fatal = fatalVector.size();
91*69ae808eSJason M. Bills
92*69ae808eSJason M. Bills std::vector<std::string> criticalVector = assertedInMap(criticalAssertMap);
93*69ae808eSJason M. Bills bool critical = criticalVector.size();
94*69ae808eSJason M. Bills
95*69ae808eSJason M. Bills std::vector<std::string> warningVector = assertedInMap(warningAssertMap);
96*69ae808eSJason M. Bills bool warn = warningVector.size();
97*69ae808eSJason M. Bills
98*69ae808eSJason M. Bills associationManager->setLocalAssociations(fatalVector, criticalVector,
99*69ae808eSJason M. Bills warningVector);
100*69ae808eSJason M. Bills
101*69ae808eSJason M. Bills StatusSetting last = currentPriority;
102*69ae808eSJason M. Bills std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
103*69ae808eSJason M. Bills if (forceRefresh)
104*69ae808eSJason M. Bills {
105*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(fatalLedPath, false));
106*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(criticalLedPath, false));
107*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(warningLedPath, false));
108*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(okLedPath, false));
109*69ae808eSJason M. Bills for (const auto& ledPair : ledsToSet)
110*69ae808eSJason M. Bills {
111*69ae808eSJason M. Bills conn->async_method_call(
112*69ae808eSJason M. Bills [ledPair](const boost::system::error_code ec) {
113*69ae808eSJason M. Bills std::ios_base::fmtflags originalFlags = std::cerr.flags();
114*69ae808eSJason M. Bills if (ec)
115*69ae808eSJason M. Bills {
116*69ae808eSJason M. Bills std::cerr << "Cannot set " << ledPair.first << " to "
117*69ae808eSJason M. Bills << std::boolalpha
118*69ae808eSJason M. Bills << std::get<bool>(ledPair.second) << "\n";
119*69ae808eSJason M. Bills std::cerr.flags(originalFlags);
120*69ae808eSJason M. Bills }
121*69ae808eSJason M. Bills if constexpr (debug)
122*69ae808eSJason M. Bills {
123*69ae808eSJason M. Bills std::cerr << "Set " << ledPair.first << " to "
124*69ae808eSJason M. Bills << std::boolalpha
125*69ae808eSJason M. Bills << std::get<bool>(ledPair.second) << "\n";
126*69ae808eSJason M. Bills std::cerr.flags(originalFlags);
127*69ae808eSJason M. Bills }
128*69ae808eSJason M. Bills },
129*69ae808eSJason M. Bills ledManagerBusname, ledPair.first,
130*69ae808eSJason M. Bills "org.freedesktop.DBus.Properties", "Set", ledIface,
131*69ae808eSJason M. Bills ledAssertProp, ledPair.second);
132*69ae808eSJason M. Bills }
133*69ae808eSJason M. Bills }
134*69ae808eSJason M. Bills if (fatal)
135*69ae808eSJason M. Bills {
136*69ae808eSJason M. Bills currentPriority = StatusSetting::fatal;
137*69ae808eSJason M. Bills }
138*69ae808eSJason M. Bills else if (critical)
139*69ae808eSJason M. Bills {
140*69ae808eSJason M. Bills currentPriority = StatusSetting::critical;
141*69ae808eSJason M. Bills }
142*69ae808eSJason M. Bills else if (warn)
143*69ae808eSJason M. Bills {
144*69ae808eSJason M. Bills currentPriority = StatusSetting::warn;
145*69ae808eSJason M. Bills }
146*69ae808eSJason M. Bills else
147*69ae808eSJason M. Bills {
148*69ae808eSJason M. Bills currentPriority = StatusSetting::ok;
149*69ae808eSJason M. Bills }
150*69ae808eSJason M. Bills
151*69ae808eSJason M. Bills if (last != currentPriority || forceRefresh)
152*69ae808eSJason M. Bills {
153*69ae808eSJason M. Bills switch (currentPriority)
154*69ae808eSJason M. Bills {
155*69ae808eSJason M. Bills case (StatusSetting::fatal):
156*69ae808eSJason M. Bills {
157*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(fatalLedPath, true));
158*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(criticalLedPath, false));
159*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(warningLedPath, false));
160*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(okLedPath, false));
161*69ae808eSJason M. Bills break;
162*69ae808eSJason M. Bills }
163*69ae808eSJason M. Bills case (StatusSetting::critical):
164*69ae808eSJason M. Bills {
165*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(fatalLedPath, false));
166*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(criticalLedPath, true));
167*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(warningLedPath, false));
168*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(okLedPath, false));
169*69ae808eSJason M. Bills break;
170*69ae808eSJason M. Bills }
171*69ae808eSJason M. Bills case (StatusSetting::warn):
172*69ae808eSJason M. Bills {
173*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(fatalLedPath, false));
174*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(criticalLedPath, false));
175*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(warningLedPath, true));
176*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(okLedPath, false));
177*69ae808eSJason M. Bills break;
178*69ae808eSJason M. Bills }
179*69ae808eSJason M. Bills case (StatusSetting::ok):
180*69ae808eSJason M. Bills {
181*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(fatalLedPath, false));
182*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(criticalLedPath, false));
183*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(warningLedPath, false));
184*69ae808eSJason M. Bills ledsToSet.push_back(std::make_pair(okLedPath, true));
185*69ae808eSJason M. Bills break;
186*69ae808eSJason M. Bills }
187*69ae808eSJason M. Bills case (StatusSetting::none):
188*69ae808eSJason M. Bills default:
189*69ae808eSJason M. Bills break;
190*69ae808eSJason M. Bills }
191*69ae808eSJason M. Bills }
192*69ae808eSJason M. Bills
193*69ae808eSJason M. Bills for (const auto& ledPair : ledsToSet)
194*69ae808eSJason M. Bills {
195*69ae808eSJason M. Bills conn->async_method_call(
196*69ae808eSJason M. Bills [ledPair](const boost::system::error_code ec) {
197*69ae808eSJason M. Bills if (ec)
198*69ae808eSJason M. Bills {
199*69ae808eSJason M. Bills std::cerr << "Cannot set " << ledPair.first << " to "
200*69ae808eSJason M. Bills << std::boolalpha
201*69ae808eSJason M. Bills << std::get<bool>(ledPair.second) << "\n";
202*69ae808eSJason M. Bills }
203*69ae808eSJason M. Bills if constexpr (debug)
204*69ae808eSJason M. Bills {
205*69ae808eSJason M. Bills std::cerr
206*69ae808eSJason M. Bills << "Set " << ledPair.first << " to " << std::boolalpha
207*69ae808eSJason M. Bills << std::get<bool>(ledPair.second) << "\n";
208*69ae808eSJason M. Bills }
209*69ae808eSJason M. Bills },
210*69ae808eSJason M. Bills ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
211*69ae808eSJason M. Bills "Set", ledIface, ledAssertProp, ledPair.second);
212*69ae808eSJason M. Bills }
213*69ae808eSJason M. Bills }
214*69ae808eSJason M. Bills
createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection> & conn)215*69ae808eSJason M. Bills void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
216*69ae808eSJason M. Bills {
217*69ae808eSJason M. Bills static sdbusplus::bus::match_t match(
218*69ae808eSJason M. Bills static_cast<sdbusplus::bus_t&>(*conn),
219*69ae808eSJason M. Bills "type='signal',member='ThresholdAsserted'",
220*69ae808eSJason M. Bills [&conn](sdbusplus::message_t& message) {
221*69ae808eSJason M. Bills std::string sensorName;
222*69ae808eSJason M. Bills std::string thresholdInterface;
223*69ae808eSJason M. Bills std::string event;
224*69ae808eSJason M. Bills bool assert;
225*69ae808eSJason M. Bills double assertValue;
226*69ae808eSJason M. Bills
227*69ae808eSJason M. Bills try
228*69ae808eSJason M. Bills {
229*69ae808eSJason M. Bills message.read(sensorName, thresholdInterface, event, assert,
230*69ae808eSJason M. Bills assertValue);
231*69ae808eSJason M. Bills }
232*69ae808eSJason M. Bills catch (sdbusplus::exception_t&)
233*69ae808eSJason M. Bills {
234*69ae808eSJason M. Bills return;
235*69ae808eSJason M. Bills }
236*69ae808eSJason M. Bills if constexpr (debug)
237*69ae808eSJason M. Bills {
238*69ae808eSJason M. Bills std::cerr << "Threshold callback: SensorName = " << sensorName
239*69ae808eSJason M. Bills << ", Event = " << event << ", Asserted = " << assert
240*69ae808eSJason M. Bills << "\n";
241*69ae808eSJason M. Bills }
242*69ae808eSJason M. Bills
243*69ae808eSJason M. Bills if (event == "CriticalAlarmLow")
244*69ae808eSJason M. Bills {
245*69ae808eSJason M. Bills criticalAssertMap[message.get_path()]["low"] = assert;
246*69ae808eSJason M. Bills }
247*69ae808eSJason M. Bills else if (event == "CriticalAlarmHigh")
248*69ae808eSJason M. Bills {
249*69ae808eSJason M. Bills criticalAssertMap[message.get_path()]["high"] = assert;
250*69ae808eSJason M. Bills }
251*69ae808eSJason M. Bills else if (event == "WarningAlarmLow")
252*69ae808eSJason M. Bills {
253*69ae808eSJason M. Bills warningAssertMap[message.get_path()]["low"] = assert;
254*69ae808eSJason M. Bills }
255*69ae808eSJason M. Bills else if (event == "WarningAlarmHigh")
256*69ae808eSJason M. Bills {
257*69ae808eSJason M. Bills warningAssertMap[message.get_path()]["high"] = assert;
258*69ae808eSJason M. Bills }
259*69ae808eSJason M. Bills
260*69ae808eSJason M. Bills associationManager->setSensorAssociations(
261*69ae808eSJason M. Bills assertedInMap(criticalAssertMap),
262*69ae808eSJason M. Bills assertedInMap(warningAssertMap));
263*69ae808eSJason M. Bills
264*69ae808eSJason M. Bills updateLedStatus(conn);
265*69ae808eSJason M. Bills });
266*69ae808eSJason M. Bills }
267*69ae808eSJason M. Bills
createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection> & conn)268*69ae808eSJason M. Bills void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
269*69ae808eSJason M. Bills {
270*69ae808eSJason M. Bills static sdbusplus::bus::match_t match(
271*69ae808eSJason M. Bills static_cast<sdbusplus::bus_t&>(*conn),
272*69ae808eSJason M. Bills "type='signal',interface='org.freedesktop.DBus.Properties',"
273*69ae808eSJason M. Bills "arg0namespace='" +
274*69ae808eSJason M. Bills std::string(associationIface) + "'",
275*69ae808eSJason M. Bills [&conn](sdbusplus::message_t& message) {
276*69ae808eSJason M. Bills if (message.get_path() == rootPath)
277*69ae808eSJason M. Bills {
278*69ae808eSJason M. Bills return; // it's us
279*69ae808eSJason M. Bills }
280*69ae808eSJason M. Bills std::string objectName;
281*69ae808eSJason M. Bills boost::container::flat_map<std::string,
282*69ae808eSJason M. Bills std::variant<std::vector<Association>>>
283*69ae808eSJason M. Bills values;
284*69ae808eSJason M. Bills try
285*69ae808eSJason M. Bills {
286*69ae808eSJason M. Bills message.read(objectName, values);
287*69ae808eSJason M. Bills }
288*69ae808eSJason M. Bills catch (sdbusplus::exception_t&)
289*69ae808eSJason M. Bills {
290*69ae808eSJason M. Bills return;
291*69ae808eSJason M. Bills }
292*69ae808eSJason M. Bills
293*69ae808eSJason M. Bills if constexpr (debug)
294*69ae808eSJason M. Bills {
295*69ae808eSJason M. Bills std::cerr << "Association callback " << message.get_path()
296*69ae808eSJason M. Bills << "\n";
297*69ae808eSJason M. Bills }
298*69ae808eSJason M. Bills
299*69ae808eSJason M. Bills auto findAssociations = values.find("Associations");
300*69ae808eSJason M. Bills if (findAssociations == values.end())
301*69ae808eSJason M. Bills {
302*69ae808eSJason M. Bills return;
303*69ae808eSJason M. Bills }
304*69ae808eSJason M. Bills const std::vector<Association>* associations =
305*69ae808eSJason M. Bills std::get_if<std::vector<Association>>(
306*69ae808eSJason M. Bills &findAssociations->second);
307*69ae808eSJason M. Bills
308*69ae808eSJason M. Bills if (associations == nullptr)
309*69ae808eSJason M. Bills {
310*69ae808eSJason M. Bills std::cerr << "Illegal Association on " << message.get_path()
311*69ae808eSJason M. Bills << "\n";
312*69ae808eSJason M. Bills return;
313*69ae808eSJason M. Bills }
314*69ae808eSJason M. Bills
315*69ae808eSJason M. Bills bool localWarning = false;
316*69ae808eSJason M. Bills bool localCritical = false;
317*69ae808eSJason M. Bills bool globalWarning = false;
318*69ae808eSJason M. Bills bool globalCritical = false;
319*69ae808eSJason M. Bills
320*69ae808eSJason M. Bills for (const auto& [forward, reverse, path] : *associations)
321*69ae808eSJason M. Bills {
322*69ae808eSJason M. Bills if (path == rootPath)
323*69ae808eSJason M. Bills {
324*69ae808eSJason M. Bills globalWarning = globalWarning ? true : reverse == "warning";
325*69ae808eSJason M. Bills globalCritical =
326*69ae808eSJason M. Bills globalCritical ? true : reverse == "critical";
327*69ae808eSJason M. Bills
328*69ae808eSJason M. Bills if constexpr (1)
329*69ae808eSJason M. Bills {
330*69ae808eSJason M. Bills std::cerr << "got global ";
331*69ae808eSJason M. Bills }
332*69ae808eSJason M. Bills }
333*69ae808eSJason M. Bills else
334*69ae808eSJason M. Bills {
335*69ae808eSJason M. Bills localWarning = localWarning ? true : reverse == "warning";
336*69ae808eSJason M. Bills localCritical =
337*69ae808eSJason M. Bills localCritical ? true : reverse == "critical";
338*69ae808eSJason M. Bills }
339*69ae808eSJason M. Bills if (globalCritical && localCritical)
340*69ae808eSJason M. Bills {
341*69ae808eSJason M. Bills break;
342*69ae808eSJason M. Bills }
343*69ae808eSJason M. Bills }
344*69ae808eSJason M. Bills
345*69ae808eSJason M. Bills bool fatal = globalCritical && localCritical;
346*69ae808eSJason M. Bills bool critical = globalWarning && localCritical;
347*69ae808eSJason M. Bills bool warning = globalWarning && !critical;
348*69ae808eSJason M. Bills
349*69ae808eSJason M. Bills fatalAssertMap[message.get_path()]["association"] = fatal;
350*69ae808eSJason M. Bills criticalAssertMap[message.get_path()]["association"] = critical;
351*69ae808eSJason M. Bills warningAssertMap[message.get_path()]["association"] = warning;
352*69ae808eSJason M. Bills
353*69ae808eSJason M. Bills updateLedStatus(conn);
354*69ae808eSJason M. Bills });
355*69ae808eSJason M. Bills }
356*69ae808eSJason M. Bills
main(int,char **)357*69ae808eSJason M. Bills int main(int /*argc*/, char** /*argv*/)
358*69ae808eSJason M. Bills {
359*69ae808eSJason M. Bills boost::asio::io_context io;
360*69ae808eSJason M. Bills auto conn = std::make_shared<sdbusplus::asio::connection>(io);
361*69ae808eSJason M. Bills conn->request_name("xyz.openbmc_project.CallbackManager");
362*69ae808eSJason M. Bills sdbusplus::asio::object_server objServer(conn);
363*69ae808eSJason M. Bills std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
364*69ae808eSJason M. Bills objServer.add_interface(rootPath,
365*69ae808eSJason M. Bills "xyz.openbmc_project.CallbackManager");
366*69ae808eSJason M. Bills rootIface->register_method("RetriggerLEDUpdate", [&conn]() {
367*69ae808eSJason M. Bills updateLedStatus(conn, true);
368*69ae808eSJason M. Bills });
369*69ae808eSJason M. Bills rootIface->initialize();
370*69ae808eSJason M. Bills
371*69ae808eSJason M. Bills std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
372*69ae808eSJason M. Bills objServer.add_interface(rootPath, globalInventoryIface);
373*69ae808eSJason M. Bills inventoryIface->initialize();
374*69ae808eSJason M. Bills
375*69ae808eSJason M. Bills associationManager = std::make_unique<AssociationManager>(objServer, conn);
376*69ae808eSJason M. Bills
377*69ae808eSJason M. Bills createThresholdMatch(conn);
378*69ae808eSJason M. Bills createAssociationMatch(conn);
379*69ae808eSJason M. Bills updateLedStatus(conn);
380*69ae808eSJason M. Bills
381*69ae808eSJason M. Bills io.run();
382*69ae808eSJason M. Bills
383*69ae808eSJason M. Bills return 0;
384*69ae808eSJason M. Bills }
385