1 /*
2 // Copyright (c) 2019 Intel 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 "xyz/openbmc_project/Common/error.hpp"
17 
18 #include <ipmid/api.hpp>
19 #include <ipmid/utils.hpp>
20 #include <nlohmann/json.hpp>
21 #include <phosphor-logging/elog-errors.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <sdbusplus/timer.hpp>
24 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
25 
26 #include <fstream>
27 #include <iostream>
28 #include <regex>
29 #include <stdexcept>
30 #include <string_view>
31 
32 using namespace phosphor::logging;
33 
34 namespace ipmi::chassis
35 {
36 static constexpr const char* buttonIntf = "xyz.openbmc_project.Chassis.Buttons";
37 
38 const static constexpr char* idButtonPath =
39     "/xyz/openbmc_project/chassis/buttons/id";
40 static constexpr const char* powerButtonPath =
41     "/xyz/openbmc_project/chassis/buttons/power";
42 static constexpr const char* resetButtonPath =
43     "/xyz/openbmc_project/chassis/buttons/reset";
44 static constexpr const char* interruptButtonPath =
45     "/xyz/openbmc_project/chassis/buttons/nmi";
46 
47 const static constexpr char* idButtonProp = "ButtonPressed";
48 
49 const static constexpr char* ledService =
50     "xyz.openbmc_project.LED.GroupManager";
51 const static constexpr char* ledIDOnObj =
52     "/xyz/openbmc_project/led/groups/enclosure_identify";
53 const static constexpr char* ledIDBlinkObj =
54     "/xyz/openbmc_project/led/groups/enclosure_identify_blink";
55 const static constexpr char* ledInterface = "xyz.openbmc_project.Led.Group";
56 const static constexpr char* ledProp = "Asserted";
57 enum class ChassisIDState
58 {
59     off = 0,
60     temporary = 1,
61     indefinite = 2,
62 };
63 static ChassisIDState chassisIDState = ChassisIDState::off;
64 
65 constexpr size_t defaultIdentifyTimeOut = 15;
66 
67 std::unique_ptr<sdbusplus::Timer> identifyTimer
68     __attribute__((init_priority(101)));
69 std::unique_ptr<sdbusplus::bus::match_t> matchPtr
70     __attribute__((init_priority(101)));
71 
72 static void registerChassisFunctions() __attribute__((constructor));
73 
74 static ipmi::ServiceCache LEDService(ledInterface, ledIDBlinkObj);
75 
enclosureIdentifyLed(const char * objName,bool isIdLedOn)76 void enclosureIdentifyLed(const char* objName, bool isIdLedOn)
77 {
78     auto bus = getSdBus();
79 
80     try
81     {
82         std::string service = LEDService.getService(*bus);
83         setDbusProperty(*bus, service, objName, ledInterface, ledProp,
84                         isIdLedOn);
85     }
86     catch (const std::exception& e)
87     {
88         log<level::ERR>("enclosureIdentifyLed: can't set property",
89                         entry("ERR=%s", e.what()));
90     }
91 }
92 
getIDState(const char * objName,bool & state)93 bool getIDState(const char* objName, bool& state)
94 {
95     auto bus = getSdBus();
96 
97     try
98     {
99         std::string service = LEDService.getService(*bus);
100         ipmi::Value enabled =
101             getDbusProperty(*bus, service, objName, ledInterface, ledProp);
102         state = std::get<bool>(enabled);
103     }
104     catch (const sdbusplus::exception_t& e)
105     {
106         log<level::ERR>("Fail to get property", entry("PATH=%s", objName),
107                         entry("ERROR=%s", e.what()));
108         return false;
109     }
110     return true;
111 }
112 
enclosureIdentifyLedBlinkOff()113 void enclosureIdentifyLedBlinkOff()
114 {
115     chassisIDState = ChassisIDState::off;
116     enclosureIdentifyLed(ledIDBlinkObj, false);
117 }
118 
idButtonPropChanged(sdbusplus::message_t & msg)119 void idButtonPropChanged(sdbusplus::message_t& msg)
120 {
121     bool asserted = false;
122     bool buttonPressed = false;
123 
124     std::map<std::string, ipmi::Value> props;
125     std::vector<std::string> inval;
126     std::string iface;
127     msg.read(iface, props, inval);
128 
129     for (const auto& t : props)
130     {
131         auto key = t.first;
132         auto value = t.second;
133 
134         if (key == idButtonProp)
135         {
136             buttonPressed = std::get<bool>(value);
137         }
138         break;
139     }
140 
141     if (buttonPressed)
142     {
143         if (identifyTimer->isRunning())
144         {
145             log<level::INFO>("ID timer is running");
146         }
147 
148         // make sure timer is stopped
149         identifyTimer->stop();
150 
151         if (!getIDState(ledIDBlinkObj, asserted))
152         {
153             return;
154         }
155 
156         if (asserted)
157         {
158             // LED is blinking, turn off the LED
159             chassisIDState = ChassisIDState::off;
160             enclosureIdentifyLed(ledIDBlinkObj, false);
161             enclosureIdentifyLed(ledIDOnObj, false);
162         }
163         else
164         {
165             // toggle the IED on/off
166             if (!getIDState(ledIDOnObj, asserted))
167             {
168                 return;
169             }
170             enclosureIdentifyLed(ledIDOnObj, !asserted);
171         }
172     }
173 }
174 
createIdentifyTimer()175 void createIdentifyTimer()
176 {
177     if (!identifyTimer)
178     {
179         identifyTimer =
180             std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedBlinkOff);
181     }
182 }
183 
ipmiChassisIdentify(std::optional<uint8_t> interval,std::optional<uint8_t> force)184 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
185                                     std::optional<uint8_t> force)
186 {
187     uint8_t identifyInterval = interval.value_or(defaultIdentifyTimeOut);
188     bool forceIdentify = force.value_or(0) & 0x01;
189 
190     enclosureIdentifyLed(ledIDOnObj, false);
191     identifyTimer->stop();
192 
193     if (identifyInterval || forceIdentify)
194     {
195         enclosureIdentifyLed(ledIDBlinkObj, true);
196         if (forceIdentify)
197         {
198             chassisIDState = ChassisIDState::indefinite;
199             return ipmi::responseSuccess();
200         }
201         chassisIDState = ChassisIDState::temporary;
202         // start the timer
203         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
204             std::chrono::seconds(identifyInterval));
205         identifyTimer->start(time);
206     }
207     else
208     {
209         chassisIDState = ChassisIDState::off;
210         enclosureIdentifyLed(ledIDBlinkObj, false);
211     }
212     return ipmi::responseSuccess();
213 }
214 
215 namespace power_policy
216 {
217 /* helper function for Get Chassis Status Command
218  */
getPowerRestorePolicy()219 std::optional<uint2_t> getPowerRestorePolicy()
220 {
221     constexpr const char* powerRestorePath =
222         "/xyz/openbmc_project/control/host0/power_restore_policy";
223     constexpr const char* powerRestoreIntf =
224         "xyz.openbmc_project.Control.Power.RestorePolicy";
225     uint2_t restorePolicy = 0;
226     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
227 
228     try
229     {
230         auto service =
231             ipmi::getService(*busp, powerRestoreIntf, powerRestorePath);
232 
233         ipmi::Value result =
234             ipmi::getDbusProperty(*busp, service, powerRestorePath,
235                                   powerRestoreIntf, "PowerRestorePolicy");
236         auto powerRestore = sdbusplus::xyz::openbmc_project::Control::Power::
237             server::RestorePolicy::convertPolicyFromString(
238                 std::get<std::string>(result));
239 
240         using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
241         switch (powerRestore)
242         {
243             case RestorePolicy::Policy::AlwaysOff:
244                 restorePolicy = 0x00;
245                 break;
246             case RestorePolicy::Policy::Restore:
247                 restorePolicy = 0x01;
248                 break;
249             case RestorePolicy::Policy::AlwaysOn:
250                 restorePolicy = 0x02;
251                 break;
252             default:
253                 break;
254         }
255     }
256     catch (const std::exception& e)
257     {
258         log<level::ERR>("Failed to fetch PowerRestorePolicy property",
259                         entry("ERROR=%s", e.what()),
260                         entry("PATH=%s", powerRestorePath),
261                         entry("INTERFACE=%s", powerRestoreIntf));
262         return std::nullopt;
263     }
264     return std::make_optional(restorePolicy);
265 }
266 
267 /*
268  * getPowerStatus
269  * helper function for Get Chassis Status Command
270  * return - optional value for pgood (no value on error)
271  */
getPowerStatus()272 std::optional<bool> getPowerStatus()
273 {
274     bool powerGood = false;
275     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
276     try
277     {
278         constexpr const char* chassisStatePath =
279             "/xyz/openbmc_project/state/chassis0";
280         constexpr const char* chassisStateIntf =
281             "xyz.openbmc_project.State.Chassis";
282         auto service =
283             ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
284 
285         ipmi::Value variant =
286             ipmi::getDbusProperty(*busp, service, chassisStatePath,
287                                   chassisStateIntf, "CurrentPowerState");
288         std::string powerState = std::get<std::string>(variant);
289         if (powerState == "xyz.openbmc_project.State.Chassis.PowerState.On")
290         {
291             powerGood = true;
292         }
293     }
294     catch (const std::exception& e)
295     {
296         log<level::ERR>("Failed to fetch power state property",
297                         entry("ERROR=%s", e.what()));
298         return std::nullopt;
299     }
300     return std::make_optional(powerGood);
301 }
302 
303 /*
304  * getACFailStatus
305  * helper function for Get Chassis Status Command
306  * return - bool value for ACFail (false on error)
307  */
getACFailStatus()308 bool getACFailStatus()
309 {
310     constexpr const char* acBootObj =
311         "/xyz/openbmc_project/control/host0/ac_boot";
312     constexpr const char* acBootIntf = "xyz.openbmc_project.Common.ACBoot";
313     std::string acFail;
314     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
315     try
316     {
317         auto service = ipmi::getService(*bus, acBootIntf, acBootObj);
318 
319         ipmi::Value variant = ipmi::getDbusProperty(*bus, service, acBootObj,
320                                                     acBootIntf, "ACBoot");
321         acFail = std::get<std::string>(variant);
322     }
323     catch (const std::exception& e)
324     {
325         log<level::ERR>(
326             "Failed to fetch ACBoot property", entry("ERROR=%s", e.what()),
327             entry("PATH=%s", acBootObj), entry("INTERFACE=%s", acBootIntf));
328     }
329     return acFail == "True";
330 }
331 } // namespace power_policy
332 
getButtonEnabled(const std::string & buttonPath)333 static std::optional<bool> getButtonEnabled(const std::string& buttonPath)
334 {
335     bool buttonDisabled = false;
336     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
337     try
338     {
339         auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
340         ipmi::Value disabled = ipmi::getDbusProperty(
341             *busp, service, buttonPath, buttonIntf, "ButtonMasked");
342         buttonDisabled = std::get<bool>(disabled);
343     }
344     catch (const sdbusplus::exception_t& e)
345     {
346         log<level::ERR>("Fail to get button disabled property",
347                         entry("PATH=%s", buttonPath.c_str()),
348                         entry("ERROR=%s", e.what()));
349         return std::nullopt;
350     }
351     return std::make_optional(buttonDisabled);
352 }
353 
setButtonEnabled(const std::string & buttonPath,const bool disabled)354 static bool setButtonEnabled(const std::string& buttonPath, const bool disabled)
355 {
356     try
357     {
358         auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
359         ipmi::setDbusProperty(*getSdBus(), service, buttonPath, buttonIntf,
360                               "ButtonMasked", disabled);
361     }
362     catch (const std::exception& e)
363     {
364         log<level::ERR>("Failed to set button disabled",
365                         entry("EXCEPTION=%s, REQUEST=%x", e.what(), disabled));
366         return -1;
367     }
368 
369     return 0;
370 }
371 
getRestartCause(ipmi::Context::ptr & ctx,std::string & restartCause)372 static bool getRestartCause(ipmi::Context::ptr& ctx, std::string& restartCause)
373 {
374     constexpr const char* restartCausePath =
375         "/xyz/openbmc_project/control/host0/restart_cause";
376     constexpr const char* restartCauseIntf =
377         "xyz.openbmc_project.Control.Host.RestartCause";
378 
379     std::string service;
380     boost::system::error_code ec =
381         ipmi::getService(ctx, restartCauseIntf, restartCausePath, service);
382 
383     if (!ec)
384     {
385         ec = ipmi::getDbusProperty(ctx, service, restartCausePath,
386                                    restartCauseIntf, "RestartCause",
387                                    restartCause);
388     }
389     if (ec)
390     {
391         log<level::ERR>("Failed to fetch RestartCause property",
392                         entry("ERROR=%s", ec.message().c_str()),
393                         entry("PATH=%s", restartCausePath),
394                         entry("INTERFACE=%s", restartCauseIntf));
395         return false;
396     }
397     return true;
398 }
399 
checkIPMIRestartCause(ipmi::Context::ptr & ctx,bool & ipmiRestartCause)400 static bool checkIPMIRestartCause(ipmi::Context::ptr& ctx,
401                                   bool& ipmiRestartCause)
402 {
403     std::string restartCause;
404     if (!getRestartCause(ctx, restartCause))
405     {
406         return false;
407     }
408     ipmiRestartCause =
409         (restartCause ==
410          "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand");
411     return true;
412 }
413 
414 //----------------------------------------------------------------------
415 // Get Chassis Status commands
416 //----------------------------------------------------------------------
417 ipmi::RspType<bool,    // Power is on
418               bool,    // Power overload
419               bool,    // Interlock
420               bool,    // power fault
421               bool,    // power control fault
422               uint2_t, // power restore policy
423               bool,    // reserved
424 
425               bool,    // AC failed
426               bool,    // last power down caused by a Power overload
427               bool,    // last power down caused by a power interlock
428               bool,    // last power down caused by power fault
429               bool, // last ‘Power is on’ state was entered via IPMI command
430               uint3_t, // reserved
431 
432               bool,    // Chassis intrusion active
433               bool,    // Front Panel Lockout active
434               bool,    // Drive Fault
435               bool,    // Cooling/fan fault detected
436               uint2_t, // Chassis Identify State
437               bool,    // Chassis Identify command and state info supported
438               bool,    // reserved
439 
440               bool,    // Power off button disabled
441               bool,    // Reset button disabled
442               bool,    // Diagnostic Interrupt button disabled
443               bool,    // Standby (sleep) button disabled
444               bool,    // Power off button disable allowed
445               bool,    // Reset button disable allowed
446               bool,    // Diagnostic Interrupt button disable allowed
447               bool     // Standby (sleep) button disable allowed
448               >
ipmiGetChassisStatus(ipmi::Context::ptr ctx)449     ipmiGetChassisStatus(ipmi::Context::ptr ctx)
450 {
451     std::optional<uint2_t> restorePolicy =
452         power_policy::getPowerRestorePolicy();
453     std::optional<bool> powerGood = power_policy::getPowerStatus();
454     if (!restorePolicy || !powerGood)
455     {
456         return ipmi::responseUnspecifiedError();
457     }
458 
459     //  Front Panel Button Capabilities and disable/enable status(Optional)
460     std::optional<bool> powerButtonReading = getButtonEnabled(powerButtonPath);
461     // allow disable if the interface is present
462     bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
463     // default return the button is enabled (not disabled)
464     bool powerButtonDisabled = false;
465     if (powerButtonDisableAllow)
466     {
467         // return the real value of the button status, if present
468         powerButtonDisabled = *powerButtonReading;
469     }
470 
471     std::optional<bool> resetButtonReading = getButtonEnabled(resetButtonPath);
472     // allow disable if the interface is present
473     bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
474     // default return the button is enabled (not disabled)
475     bool resetButtonDisabled = false;
476     if (resetButtonDisableAllow)
477     {
478         // return the real value of the button status, if present
479         resetButtonDisabled = *resetButtonReading;
480     }
481 
482     std::optional<bool> interruptButtonReading =
483         getButtonEnabled(interruptButtonPath);
484     // allow disable if the interface is present
485     bool interruptButtonDisableAllow =
486         static_cast<bool>(interruptButtonReading);
487     // default return the button is enabled (not disabled)
488     bool interruptButtonDisabled = false;
489     if (interruptButtonDisableAllow)
490     {
491         // return the real value of the button status, if present
492         interruptButtonDisabled = *interruptButtonReading;
493     }
494 
495     bool powerDownAcFailed = power_policy::getACFailStatus();
496 
497     bool powerStatusIPMI = false;
498     if (!checkIPMIRestartCause(ctx, powerStatusIPMI))
499     {
500         return ipmi::responseUnspecifiedError();
501     }
502 
503     bool chassisIntrusionActive = false;
504     constexpr const char* chassisIntrusionObj =
505         "/xyz/openbmc_project/Chassis/Intrusion";
506     constexpr const char* chassisIntrusionInf =
507         "xyz.openbmc_project.Chassis.Intrusion";
508 
509     std::string intrusionService;
510     boost::system::error_code ec = ipmi::getService(
511         ctx, chassisIntrusionInf, chassisIntrusionObj, intrusionService);
512     if (ec)
513     {
514         log<level::ERR>("Failed to get Chassis Intrusion service",
515                         entry("ERROR=%s", ec.message().c_str()));
516     }
517 
518     chassisIntrusionActive = !intrusionService.empty();
519 
520     // This response has a lot of hard-coded, unsupported fields
521     // They are set to false or 0
522     constexpr bool powerOverload = false;
523     constexpr bool chassisInterlock = false;
524     constexpr bool powerFault = false;
525     constexpr bool powerControlFault = false;
526     constexpr bool powerDownOverload = false;
527     constexpr bool powerDownInterlock = false;
528     constexpr bool powerDownPowerFault = false;
529     constexpr bool frontPanelLockoutActive = false;
530     constexpr bool driveFault = false;
531     constexpr bool coolingFanFault = false;
532     // chassisIdentifySupport set because this command is implemented
533     constexpr bool chassisIdentifySupport = true;
534     uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
535     constexpr bool sleepButtonDisabled = false;
536     constexpr bool sleepButtonDisableAllow = false;
537 
538     return ipmi::responseSuccess(
539         *powerGood, powerOverload, chassisInterlock, powerFault,
540         powerControlFault, *restorePolicy,
541         false, // reserved
542 
543         powerDownAcFailed, powerDownOverload, powerDownInterlock,
544         powerDownPowerFault, powerStatusIPMI,
545         uint3_t(0), // reserved
546 
547         chassisIntrusionActive, frontPanelLockoutActive, driveFault,
548         coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
549         false, // reserved
550 
551         powerButtonDisabled, resetButtonDisabled, interruptButtonDisabled,
552         sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
553         interruptButtonDisableAllow, sleepButtonDisableAllow);
554 }
555 
getRestartCauseValue(const std::string & cause)556 static uint4_t getRestartCauseValue(const std::string& cause)
557 {
558     uint4_t restartCauseValue = 0;
559     if (cause == "xyz.openbmc_project.State.Host.RestartCause.Unknown")
560     {
561         restartCauseValue = 0x0;
562     }
563     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.IpmiCommand")
564     {
565         restartCauseValue = 0x1;
566     }
567     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.ResetButton")
568     {
569         restartCauseValue = 0x2;
570     }
571     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.PowerButton")
572     {
573         restartCauseValue = 0x3;
574     }
575     else if (cause ==
576              "xyz.openbmc_project.State.Host.RestartCause.WatchdogTimer")
577     {
578         restartCauseValue = 0x4;
579     }
580     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.OEM")
581     {
582         restartCauseValue = 0x5;
583     }
584     else if (cause ==
585              "xyz.openbmc_project.State.Host.RestartCause.PowerPolicyAlwaysOn")
586     {
587         restartCauseValue = 0x6;
588     }
589     else if (cause == "xyz.openbmc_project.State.Host.RestartCause."
590                       "PowerPolicyPreviousState")
591     {
592         restartCauseValue = 0x7;
593     }
594     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.PEFReset")
595     {
596         restartCauseValue = 0x8;
597     }
598     else if (cause ==
599              "xyz.openbmc_project.State.Host.RestartCause.PEFPowerCycle")
600     {
601         restartCauseValue = 0x9;
602     }
603     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.SoftReset")
604     {
605         restartCauseValue = 0xa;
606     }
607     else if (cause == "xyz.openbmc_project.State.Host.RestartCause.RTCWakeup")
608     {
609         restartCauseValue = 0xb;
610     }
611     return restartCauseValue;
612 }
613 
614 ipmi::RspType<uint4_t, // Restart Cause
615               uint4_t, // reserved
616               uint8_t  // channel number
617               >
ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)618     ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)
619 {
620     std::string restartCauseStr;
621     if (!getRestartCause(ctx, restartCauseStr))
622     {
623         return ipmi::responseUnspecifiedError();
624     }
625     constexpr uint4_t reserved = 0;
626     auto channel = static_cast<uint8_t>(ctx->channel);
627     return ipmi::responseSuccess(getRestartCauseValue(restartCauseStr),
628                                  reserved, channel);
629 }
630 
ipmiSetFrontPanelButtonEnables(bool disablePowerButton,bool disableResetButton,bool disableInterruptButton,bool disableSleepButton,uint4_t reserved)631 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
632     bool disablePowerButton, bool disableResetButton,
633     bool disableInterruptButton, [[maybe_unused]] bool disableSleepButton,
634     uint4_t reserved)
635 {
636     if (reserved)
637     {
638         return ipmi::responseInvalidFieldRequest();
639     }
640     bool error = false;
641 
642     error |= setButtonEnabled(powerButtonPath, disablePowerButton);
643     error |= setButtonEnabled(resetButtonPath, disableResetButton);
644     error |= setButtonEnabled(interruptButtonPath, disableInterruptButton);
645 
646     if (error)
647     {
648         return ipmi::responseUnspecifiedError();
649     }
650 
651     return ipmi::responseSuccess();
652 }
653 
registerChassisFunctions(void)654 static void registerChassisFunctions(void)
655 {
656     log<level::INFO>("Registering Chassis commands");
657 
658     createIdentifyTimer();
659 
660     if (matchPtr == nullptr)
661     {
662         using namespace sdbusplus::bus::match::rules;
663         auto bus = getSdBus();
664 
665         matchPtr = std::make_unique<sdbusplus::bus::match_t>(
666             *bus,
667             sdbusplus::bus::match::rules::propertiesChanged(idButtonPath,
668                                                             buttonIntf),
669             std::bind(idButtonPropChanged, std::placeholders::_1));
670     }
671 
672     // <Chassis Identify>
673     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
674                           ipmi::chassis::cmdChassisIdentify,
675                           ipmi::Privilege::Operator, ipmiChassisIdentify);
676     // <Get Chassis Status>
677     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
678                           ipmi::chassis::cmdGetChassisStatus,
679                           ipmi::Privilege::User, ipmiGetChassisStatus);
680     // <Get System Restart Cause>
681     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
682                           ipmi::chassis::cmdGetSystemRestartCause,
683                           ipmi::Privilege::User, ipmiGetSystemRestartCause);
684     // <Set Front Panel Enables>
685     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
686                           ipmi::chassis::cmdSetFrontPanelButtonEnables,
687                           ipmi::Privilege::Admin,
688                           ipmiSetFrontPanelButtonEnables);
689 }
690 
691 } // namespace ipmi::chassis
692