xref: /openbmc/phosphor-host-ipmid/user_channel/channel_mgmt.cpp (revision a0e545d3be961c62975718121e48b8b07740e38a)
1 /*
2 // Copyright (c) 2018 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 
17 #include "channel_mgmt.hpp"
18 
19 #include "user_layer.hpp"
20 
21 #include <ifaddrs.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 
26 #include <boost/interprocess/sync/scoped_lock.hpp>
27 #include <ipmid/utils.hpp>
28 #include <phosphor-logging/lg2.hpp>
29 #include <sdbusplus/bus/match.hpp>
30 #include <sdbusplus/server/object.hpp>
31 
32 #include <cerrno>
33 #include <exception>
34 #include <filesystem>
35 #include <fstream>
36 #include <unordered_map>
37 
38 namespace ipmi
39 {
40 
41 static constexpr const char* channelAccessDefaultFilename =
42     "/usr/share/ipmi-providers/channel_access.json";
43 static constexpr const char* channelConfigDefaultFilename =
44     "/usr/share/ipmi-providers/channel_config.json";
45 static constexpr const char* channelNvDataFilename =
46     "/var/lib/ipmi/channel_access_nv.json";
47 static constexpr const char* channelVolatileDataFilename =
48     "/run/ipmi/channel_access_volatile.json";
49 
50 // TODO: Get the service name dynamically..
51 static constexpr const char* networkIntfServiceName =
52     "xyz.openbmc_project.Network";
53 static constexpr const char* networkIntfObjectBasePath =
54     "/xyz/openbmc_project/network";
55 static constexpr const char* networkChConfigIntfName =
56     "xyz.openbmc_project.Channel.ChannelAccess";
57 static constexpr const char* privilegePropertyString = "MaxPrivilege";
58 static constexpr const char* dBusPropertiesInterface =
59     "org.freedesktop.DBus.Properties";
60 static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
61 static constexpr const char* interfaceAddedSignal = "InterfacesAdded";
62 static constexpr const char* interfaceRemovedSignal = "InterfacesRemoved";
63 
64 // STRING DEFINES: Should sync with key's in JSON
65 static constexpr const char* nameString = "name";
66 static constexpr const char* isValidString = "is_valid";
67 static constexpr const char* activeSessionsString = "active_sessions";
68 static constexpr const char* maxTransferSizeString = "max_transfer_size";
69 static constexpr const char* channelInfoString = "channel_info";
70 static constexpr const char* mediumTypeString = "medium_type";
71 static constexpr const char* protocolTypeString = "protocol_type";
72 static constexpr const char* sessionSupportedString = "session_supported";
73 static constexpr const char* isIpmiString = "is_ipmi";
74 static constexpr const char* isManagementNIC = "is_management_nic";
75 static constexpr const char* accessModeString = "access_mode";
76 static constexpr const char* userAuthDisabledString = "user_auth_disabled";
77 static constexpr const char* perMsgAuthDisabledString = "per_msg_auth_disabled";
78 static constexpr const char* alertingDisabledString = "alerting_disabled";
79 static constexpr const char* privLimitString = "priv_limit";
80 
81 // Default values
82 static constexpr const char* defaultChannelName = "RESERVED";
83 static constexpr const uint8_t defaultMediumType =
84     static_cast<uint8_t>(EChannelMediumType::reserved);
85 static constexpr const uint8_t defaultProtocolType =
86     static_cast<uint8_t>(EChannelProtocolType::reserved);
87 static constexpr const uint8_t defaultSessionSupported =
88     static_cast<uint8_t>(EChannelSessSupported::none);
89 static constexpr const uint8_t defaultAuthType =
90     static_cast<uint8_t>(EAuthType::none);
91 static constexpr const bool defaultIsIpmiState = false;
92 static constexpr size_t smallChannelSize = 64;
93 
94 std::unique_ptr<sdbusplus::bus::match_t> chPropertiesSignal
95     __attribute__((init_priority(101)));
96 
97 std::unique_ptr<sdbusplus::bus::match_t> chInterfaceAddedSignal
98     __attribute__((init_priority(101)));
99 
100 std::unique_ptr<sdbusplus::bus::match_t> chInterfaceRemovedSignal
101     __attribute__((init_priority(101)));
102 
103 // String mappings use in JSON config file
104 static std::unordered_map<std::string, EChannelMediumType> mediumTypeMap = {
105     {"reserved", EChannelMediumType::reserved},
106     {"ipmb", EChannelMediumType::ipmb},
107     {"icmb-v1.0", EChannelMediumType::icmbV10},
108     {"icmb-v0.9", EChannelMediumType::icmbV09},
109     {"lan-802.3", EChannelMediumType::lan8032},
110     {"serial", EChannelMediumType::serial},
111     {"other-lan", EChannelMediumType::otherLan},
112     {"pci-smbus", EChannelMediumType::pciSmbus},
113     {"smbus-v1.0", EChannelMediumType::smbusV11},
114     {"smbus-v2.0", EChannelMediumType::smbusV20},
115     {"usb-1x", EChannelMediumType::usbV1x},
116     {"usb-2x", EChannelMediumType::usbV2x},
117     {"system-interface", EChannelMediumType::systemInterface},
118     {"oem", EChannelMediumType::oem},
119     {"unknown", EChannelMediumType::unknown}};
120 
121 static std::unordered_map<EInterfaceIndex, std::string> interfaceMap = {
122     {interfaceKCS, "SMS"},
123     {interfaceLAN1, "eth0"},
124     {interfaceUnknown, "unknown"}};
125 
126 static std::unordered_map<std::string, EChannelProtocolType> protocolTypeMap = {
127     {"na", EChannelProtocolType::na},
128     {"ipmb-1.0", EChannelProtocolType::ipmbV10},
129     {"icmb-2.0", EChannelProtocolType::icmbV11},
130     {"reserved", EChannelProtocolType::reserved},
131     {"ipmi-smbus", EChannelProtocolType::ipmiSmbus},
132     {"kcs", EChannelProtocolType::kcs},
133     {"smic", EChannelProtocolType::smic},
134     {"bt-10", EChannelProtocolType::bt10},
135     {"bt-15", EChannelProtocolType::bt15},
136     {"tmode", EChannelProtocolType::tMode},
137     {"oem", EChannelProtocolType::oem}};
138 
139 static std::array<std::string, 4> accessModeList = {
140     "disabled", "pre-boot", "always_available", "shared"};
141 
142 static std::array<std::string, 4> sessionSupportList = {
143     "session-less", "single-session", "multi-session", "session-based"};
144 
145 const std::array<std::string, PRIVILEGE_OEM + 1> privList = {
146     "priv-reserved", "priv-callback", "priv-user",
147     "priv-operator", "priv-admin",    "priv-oem"};
148 
getChannelName(const uint8_t chNum)149 std::string ChannelConfig::getChannelName(const uint8_t chNum)
150 {
151     if (!isValidChannel(chNum))
152     {
153         lg2::error("Get channel name - Invalid channel number: {CHANNEL_ID}",
154                    "CHANNEL_ID", chNum);
155         throw std::invalid_argument("Invalid channel number");
156     }
157 
158     return channelData[chNum].chName;
159 }
160 
convertToChannelNumberFromChannelName(const std::string & chName)161 int ChannelConfig::convertToChannelNumberFromChannelName(
162     const std::string& chName)
163 {
164     for (const auto& it : channelData)
165     {
166         if (it.chName == chName)
167         {
168             return it.chID;
169         }
170     }
171     lg2::error("Invalid channel name: {CHANNEL}", "CHANNEL", chName);
172     throw std::invalid_argument("Invalid channel name");
173 
174     return -1;
175 }
176 
getChannelNameFromPath(const std::string & path)177 std::string ChannelConfig::getChannelNameFromPath(const std::string& path)
178 {
179     const size_t length = strlen(networkIntfObjectBasePath);
180     if (((length + 1) >= path.size()) ||
181         path.compare(0, length, networkIntfObjectBasePath))
182     {
183         lg2::error("Invalid object path: {PATH}", "PATH", path);
184         throw std::invalid_argument("Invalid object path");
185     }
186     std::string chName(path, length + 1);
187     return chName;
188 }
189 
processChAccessPropChange(const std::string & path,const DbusChObjProperties & chProperties)190 void ChannelConfig::processChAccessPropChange(
191     const std::string& path, const DbusChObjProperties& chProperties)
192 {
193     // Get interface name from path. ex: '/xyz/openbmc_project/network/eth0'
194     std::string chName;
195     try
196     {
197         chName = getChannelNameFromPath(path);
198     }
199     catch (const std::invalid_argument& e)
200     {
201         lg2::error("Exception: {MSG}", "MSG", e.what());
202         return;
203     }
204 
205     // Get the MaxPrivilege property value from the signal
206     std::string intfPrivStr;
207     std::string propName;
208     for (const auto& prop : chProperties)
209     {
210         if (prop.first == privilegePropertyString)
211         {
212             propName = privilegePropertyString;
213             intfPrivStr = std::get<std::string>(prop.second);
214             break;
215         }
216     }
217 
218     if (propName != privilegePropertyString)
219     {
220         lg2::error("Unknown signal caught.");
221         return;
222     }
223 
224     if (intfPrivStr.empty())
225     {
226         lg2::error("Invalid privilege string for intf {INTF}", "INTF", chName);
227         return;
228     }
229 
230     uint8_t intfPriv = 0;
231     int chNum;
232     try
233     {
234         intfPriv = static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
235         chNum = convertToChannelNumberFromChannelName(chName);
236     }
237     catch (const std::invalid_argument& e)
238     {
239         lg2::error("Exception: {MSG}", "MSG", e.what());
240         return;
241     }
242 
243     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
244         channelLock{*channelMutex};
245     // skip updating the values, if this property change originated from IPMI.
246     if (signalFlag & (1 << chNum))
247     {
248         signalFlag &= ~(1 << chNum);
249         lg2::debug("Request originated from IPMI so ignoring signal");
250         return;
251     }
252 
253     // Update both volatile & Non-volatile, if there is mismatch.
254     // as property change other than IPMI, has to update both volatile &
255     // non-volatile data.
256     checkAndReloadVolatileData();
257     checkAndReloadNVData();
258     if (channelData[chNum].chAccess.chNonVolatileData.privLimit != intfPriv)
259     {
260         // Update NV data
261         channelData[chNum].chAccess.chNonVolatileData.privLimit = intfPriv;
262         if (writeChannelPersistData() != 0)
263         {
264             lg2::error("Failed to update the persist data file");
265             return;
266         }
267 
268         // Update Volatile data
269         if (channelData[chNum].chAccess.chVolatileData.privLimit != intfPriv)
270         {
271             channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
272             if (writeChannelVolatileData() != 0)
273             {
274                 lg2::error("Failed to update the volatile data file");
275                 return;
276             }
277         }
278     }
279 
280     return;
281 }
282 
getChannelConfigObject()283 ChannelConfig& getChannelConfigObject()
284 {
285     static ChannelConfig channelConfig;
286     return channelConfig;
287 }
288 
~ChannelConfig()289 ChannelConfig::~ChannelConfig()
290 {
291     if (signalHndlrObjectState)
292     {
293         chPropertiesSignal.reset();
294         chInterfaceAddedSignal.reset();
295         chInterfaceRemovedSignal.reset();
296         sigHndlrLock.unlock();
297     }
298 }
299 
ChannelConfig()300 ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection())
301 {
302     std::ofstream mutexCleanUpFile;
303     mutexCleanUpFile.open(ipmiChMutexCleanupLockFile,
304                           std::ofstream::out | std::ofstream::app);
305     if (!mutexCleanUpFile.good())
306     {
307         lg2::debug("Unable to open mutex cleanup file");
308         return;
309     }
310     mutexCleanUpFile.close();
311     mutexCleanupLock =
312         boost::interprocess::file_lock(ipmiChMutexCleanupLockFile);
313     if (mutexCleanupLock.try_lock())
314     {
315         boost::interprocess::named_recursive_mutex::remove(ipmiChannelMutex);
316         channelMutex =
317             std::make_unique<boost::interprocess::named_recursive_mutex>(
318                 boost::interprocess::open_or_create, ipmiChannelMutex);
319         mutexCleanupLock.lock_sharable();
320     }
321     else
322     {
323         mutexCleanupLock.lock_sharable();
324         channelMutex =
325             std::make_unique<boost::interprocess::named_recursive_mutex>(
326                 boost::interprocess::open_or_create, ipmiChannelMutex);
327     }
328 
329     initChannelPersistData();
330 
331     sigHndlrLock = boost::interprocess::file_lock(channelNvDataFilename);
332     // Register it for single object and single process either netipmid /
333     // host-ipmid
334     if (chPropertiesSignal == nullptr && sigHndlrLock.try_lock())
335     {
336         lg2::debug("Registering channel signal handler.");
337         chPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
338             bus,
339             sdbusplus::bus::match::rules::path_namespace(
340                 networkIntfObjectBasePath) +
341                 sdbusplus::bus::match::rules::type::signal() +
342                 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
343                 sdbusplus::bus::match::rules::interface(
344                     dBusPropertiesInterface) +
345                 sdbusplus::bus::match::rules::argN(0, networkChConfigIntfName),
346             [&](sdbusplus::message_t& msg) {
347                 DbusChObjProperties props;
348                 std::string iface;
349                 std::string path = msg.get_path();
350                 msg.read(iface, props);
351                 processChAccessPropChange(path, props);
352             });
353         signalHndlrObjectState = true;
354 
355         chInterfaceAddedSignal = std::make_unique<sdbusplus::bus::match_t>(
356             bus,
357             sdbusplus::bus::match::rules::type::signal() +
358                 sdbusplus::bus::match::rules::member(interfaceAddedSignal) +
359                 sdbusplus::bus::match::rules::argNpath(
360                     0, std::string(networkIntfObjectBasePath) + "/"),
361             [&](sdbusplus::message_t&) { initChannelPersistData(); });
362 
363         chInterfaceRemovedSignal = std::make_unique<sdbusplus::bus::match_t>(
364             bus,
365             sdbusplus::bus::match::rules::type::signal() +
366                 sdbusplus::bus::match::rules::member(interfaceRemovedSignal) +
367                 sdbusplus::bus::match::rules::argNpath(
368                     0, std::string(networkIntfObjectBasePath) + "/"),
369             [&](sdbusplus::message_t&) { initChannelPersistData(); });
370     }
371 }
372 
isValidChannel(const uint8_t chNum)373 bool ChannelConfig::isValidChannel(const uint8_t chNum)
374 {
375     if (chNum >= maxIpmiChannels)
376     {
377         lg2::debug("Invalid channel ID - Out of range");
378         return false;
379     }
380 
381     return channelData[chNum].isChValid;
382 }
383 
getChannelSessionSupport(const uint8_t chNum)384 EChannelSessSupported ChannelConfig::getChannelSessionSupport(
385     const uint8_t chNum)
386 {
387     EChannelSessSupported chSessSupport =
388         (EChannelSessSupported)channelData[chNum].chInfo.sessionSupported;
389     return chSessSupport;
390 }
391 
isValidAuthType(const uint8_t chNum,const EAuthType & authType)392 bool ChannelConfig::isValidAuthType(const uint8_t chNum,
393                                     const EAuthType& authType)
394 {
395     if ((authType < EAuthType::md2) || (authType > EAuthType::oem))
396     {
397         lg2::debug("Invalid authentication type");
398         return false;
399     }
400 
401     uint8_t authTypeSupported = channelData[chNum].chInfo.authTypeSupported;
402     if (!(authTypeSupported & (1 << static_cast<uint8_t>(authType))))
403     {
404         lg2::debug("Authentication type is not supported.");
405         return false;
406     }
407 
408     return true;
409 }
410 
getChannelActiveSessions(const uint8_t chNum)411 int ChannelConfig::getChannelActiveSessions(const uint8_t chNum)
412 {
413     // TODO: TEMPORARY FIX
414     // Channels active session count is managed separately
415     // by monitoring channel session which includes LAN and
416     // RAKP layer changes. This will be updated, once the
417     // authentication part is implemented.
418     return channelData[chNum].activeSessCount;
419 }
420 
getChannelMaxTransferSize(uint8_t chNum)421 size_t ChannelConfig::getChannelMaxTransferSize(uint8_t chNum)
422 {
423     return channelData[chNum].maxTransferSize;
424 }
425 
getChannelInfo(const uint8_t chNum,ChannelInfo & chInfo)426 Cc ChannelConfig::getChannelInfo(const uint8_t chNum, ChannelInfo& chInfo)
427 {
428     if (!isValidChannel(chNum))
429     {
430         lg2::debug("Get channel info - Invalid channel: {CHANNEL}", "CHANNEL",
431                    chNum);
432         return ccInvalidFieldRequest;
433     }
434 
435     std::copy_n(reinterpret_cast<uint8_t*>(&channelData[chNum].chInfo),
436                 sizeof(channelData[chNum].chInfo),
437                 reinterpret_cast<uint8_t*>(&chInfo));
438     return ccSuccess;
439 }
440 
getChannelAccessData(const uint8_t chNum,ChannelAccess & chAccessData)441 Cc ChannelConfig::getChannelAccessData(const uint8_t chNum,
442                                        ChannelAccess& chAccessData)
443 {
444     if (!isValidChannel(chNum))
445     {
446         lg2::debug("Get channel access data - Invalid channel: {CHANNEL}",
447                    "CHANNEL", chNum);
448         return ccInvalidFieldRequest;
449     }
450 
451     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
452     {
453         lg2::debug("Session-less channel doesn't have access data.");
454         return ccActionNotSupportedForChannel;
455     }
456 
457     if (checkAndReloadVolatileData() != 0)
458     {
459         return ccUnspecifiedError;
460     }
461 
462     std::copy_n(
463         reinterpret_cast<uint8_t*>(&channelData[chNum].chAccess.chVolatileData),
464         sizeof(channelData[chNum].chAccess.chVolatileData),
465         reinterpret_cast<uint8_t*>(&chAccessData));
466 
467     return ccSuccess;
468 }
469 
setChannelAccessData(const uint8_t chNum,const ChannelAccess & chAccessData,const uint8_t setFlag)470 Cc ChannelConfig::setChannelAccessData(const uint8_t chNum,
471                                        const ChannelAccess& chAccessData,
472                                        const uint8_t setFlag)
473 {
474     if (!isValidChannel(chNum))
475     {
476         lg2::debug("Set channel info - Invalid channel: {CHANNEL}", "CHANNEL",
477                    chNum);
478         return ccInvalidFieldRequest;
479     }
480 
481     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
482     {
483         lg2::debug("Session-less channel doesn't have access data.");
484         return ccActionNotSupportedForChannel;
485     }
486 
487     if ((setFlag & setAccessMode) &&
488         (!isValidAccessMode(chAccessData.accessMode)))
489     {
490         lg2::debug("Invalid access mode specified");
491         return ccAccessModeNotSupportedForChannel;
492     }
493     if ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))
494     {
495         lg2::debug("Invalid privilege limit specified");
496         return ccInvalidFieldRequest;
497     }
498 
499     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
500         channelLock{*channelMutex};
501 
502     if (checkAndReloadVolatileData() != 0)
503     {
504         return ccUnspecifiedError;
505     }
506 
507     if (setFlag & setAccessMode)
508     {
509         channelData[chNum].chAccess.chVolatileData.accessMode =
510             chAccessData.accessMode;
511     }
512     if (setFlag & setUserAuthEnabled)
513     {
514         channelData[chNum].chAccess.chVolatileData.userAuthDisabled =
515             chAccessData.userAuthDisabled;
516     }
517     if (setFlag & setMsgAuthEnabled)
518     {
519         channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled =
520             chAccessData.perMsgAuthDisabled;
521     }
522     if (setFlag & setAlertingEnabled)
523     {
524         channelData[chNum].chAccess.chVolatileData.alertingDisabled =
525             chAccessData.alertingDisabled;
526     }
527     if (setFlag & setPrivLimit)
528     {
529         channelData[chNum].chAccess.chVolatileData.privLimit =
530             chAccessData.privLimit;
531     }
532 
533     // Write Volatile data to file
534     if (writeChannelVolatileData() != 0)
535     {
536         lg2::debug("Failed to update the channel volatile data");
537         return ccUnspecifiedError;
538     }
539     return ccSuccess;
540 }
541 
getChannelAccessPersistData(const uint8_t chNum,ChannelAccess & chAccessData)542 Cc ChannelConfig::getChannelAccessPersistData(const uint8_t chNum,
543                                               ChannelAccess& chAccessData)
544 {
545     if (!isValidChannel(chNum))
546     {
547         lg2::debug(
548             "Get channel access persist data - Invalid channel: {CHANNEL}",
549             "CHANNEL", chNum);
550         return ccInvalidFieldRequest;
551     }
552 
553     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
554     {
555         lg2::debug("Session-less channel doesn't have access data.");
556         return ccActionNotSupportedForChannel;
557     }
558 
559     if (checkAndReloadNVData() != 0)
560     {
561         return ccUnspecifiedError;
562     }
563 
564     std::copy_n(reinterpret_cast<uint8_t*>(
565                     &channelData[chNum].chAccess.chNonVolatileData),
566                 sizeof(channelData[chNum].chAccess.chNonVolatileData),
567                 reinterpret_cast<uint8_t*>(&chAccessData));
568 
569     return ccSuccess;
570 }
571 
setChannelAccessPersistData(const uint8_t chNum,const ChannelAccess & chAccessData,const uint8_t setFlag)572 Cc ChannelConfig::setChannelAccessPersistData(const uint8_t chNum,
573                                               const ChannelAccess& chAccessData,
574                                               const uint8_t setFlag)
575 {
576     if (!isValidChannel(chNum))
577     {
578         lg2::debug(
579             "Set channel access persist data - Invalid channel: {CHANNEL}",
580             "CHANNEL", chNum);
581         return ccInvalidFieldRequest;
582     }
583 
584     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
585     {
586         lg2::debug("Session-less channel doesn't have access data.");
587         return ccActionNotSupportedForChannel;
588     }
589 
590     if ((setFlag & setAccessMode) &&
591         (!isValidAccessMode(chAccessData.accessMode)))
592     {
593         lg2::debug("Invalid access mode specified");
594         return ccAccessModeNotSupportedForChannel;
595     }
596     if ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))
597     {
598         lg2::debug("Invalid privilege limit specified");
599         return ccInvalidFieldRequest;
600     }
601 
602     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
603         channelLock{*channelMutex};
604 
605     if (checkAndReloadNVData() != 0)
606     {
607         return ccUnspecifiedError;
608     }
609 
610     if (setFlag & setAccessMode)
611     {
612         channelData[chNum].chAccess.chNonVolatileData.accessMode =
613             chAccessData.accessMode;
614     }
615     if (setFlag & setUserAuthEnabled)
616     {
617         channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled =
618             chAccessData.userAuthDisabled;
619     }
620     if (setFlag & setMsgAuthEnabled)
621     {
622         channelData[chNum].chAccess.chNonVolatileData.perMsgAuthDisabled =
623             chAccessData.perMsgAuthDisabled;
624     }
625     if (setFlag & setAlertingEnabled)
626     {
627         channelData[chNum].chAccess.chNonVolatileData.alertingDisabled =
628             chAccessData.alertingDisabled;
629     }
630     if (setFlag & setPrivLimit)
631     {
632         // Send Update to network channel config interfaces over dbus
633         std::string privStr = convertToPrivLimitString(chAccessData.privLimit);
634         std::string networkIntfObj = std::string(networkIntfObjectBasePath) +
635                                      "/" + channelData[chNum].chName;
636         try
637         {
638             if (0 != setDbusProperty(networkIntfServiceName, networkIntfObj,
639                                      networkChConfigIntfName,
640                                      privilegePropertyString, privStr))
641             {
642                 lg2::debug("Network interface '{INTERFACE}' does not exist",
643                            "INTERFACE", channelData[chNum].chName);
644                 return ccUnspecifiedError;
645             }
646         }
647         catch (const sdbusplus::exception_t& e)
648         {
649             lg2::error("Exception: Network interface does not exist");
650             return ccInvalidFieldRequest;
651         }
652         signalFlag |= (1 << chNum);
653         channelData[chNum].chAccess.chNonVolatileData.privLimit =
654             chAccessData.privLimit;
655     }
656 
657     // Write persistent data to file
658     if (writeChannelPersistData() != 0)
659     {
660         lg2::debug("Failed to update the presist data file");
661         return ccUnspecifiedError;
662     }
663     return ccSuccess;
664 }
665 
getChannelAuthTypeSupported(const uint8_t chNum,uint8_t & authTypeSupported)666 Cc ChannelConfig::getChannelAuthTypeSupported(const uint8_t chNum,
667                                               uint8_t& authTypeSupported)
668 {
669     if (!isValidChannel(chNum))
670     {
671         lg2::debug(
672             "Get channel auth type supported - Invalid channel: {CHANNEL}",
673             "CHANNEL", chNum);
674         return ccInvalidFieldRequest;
675     }
676 
677     authTypeSupported = channelData[chNum].chInfo.authTypeSupported;
678     return ccSuccess;
679 }
680 
getChannelEnabledAuthType(const uint8_t chNum,const uint8_t priv,EAuthType & authType)681 Cc ChannelConfig::getChannelEnabledAuthType(
682     const uint8_t chNum, const uint8_t priv, EAuthType& authType)
683 {
684     if (!isValidChannel(chNum))
685     {
686         lg2::debug("Get channel enabled auth type - Invalid channel: {CHANNEL}",
687                    "CHANNEL", chNum);
688         return ccInvalidFieldRequest;
689     }
690 
691     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
692     {
693         lg2::debug("Sessionless channel doesn't have access data.");
694         return ccInvalidFieldRequest;
695     }
696 
697     if (!isValidPrivLimit(priv))
698     {
699         lg2::debug("Invalid privilege specified.");
700         return ccInvalidFieldRequest;
701     }
702 
703     // TODO: Hardcoded for now. Need to implement.
704     authType = EAuthType::none;
705 
706     return ccSuccess;
707 }
708 
getUpdatedFileTime(const std::string & fileName)709 std::time_t ChannelConfig::getUpdatedFileTime(const std::string& fileName)
710 {
711     struct stat fileStat;
712     if (stat(fileName.c_str(), &fileStat) != 0)
713     {
714         lg2::debug("Error in getting last updated time stamp");
715         return -EIO;
716     }
717     return fileStat.st_mtime;
718 }
719 
convertToAccessModeIndex(const std::string & mode)720 EChannelAccessMode ChannelConfig::convertToAccessModeIndex(
721     const std::string& mode)
722 {
723     auto iter = std::find(accessModeList.begin(), accessModeList.end(), mode);
724     if (iter == accessModeList.end())
725     {
726         lg2::error("Invalid access mode: {MODE_STR}", "MODE_STR", mode);
727         throw std::invalid_argument("Invalid access mode.");
728     }
729 
730     return static_cast<EChannelAccessMode>(
731         std::distance(accessModeList.begin(), iter));
732 }
733 
convertToAccessModeString(const uint8_t value)734 std::string ChannelConfig::convertToAccessModeString(const uint8_t value)
735 {
736     if (accessModeList.size() <= value)
737     {
738         lg2::error("Invalid access mode: {MODE_IDX}", "MODE_IDX", value);
739         throw std::invalid_argument("Invalid access mode.");
740     }
741 
742     return accessModeList.at(value);
743 }
744 
convertToPrivLimitIndex(const std::string & value)745 CommandPrivilege ChannelConfig::convertToPrivLimitIndex(
746     const std::string& value)
747 {
748     auto iter = std::find(privList.begin(), privList.end(), value);
749     if (iter == privList.end())
750     {
751         lg2::error("Invalid privilege: {PRIV_STR}", "PRIV_STR", value);
752         throw std::invalid_argument("Invalid privilege.");
753     }
754 
755     return static_cast<CommandPrivilege>(std::distance(privList.begin(), iter));
756 }
757 
convertToPrivLimitString(const uint8_t value)758 std::string ChannelConfig::convertToPrivLimitString(const uint8_t value)
759 {
760     if (privList.size() <= value)
761     {
762         lg2::error("Invalid privilege: {PRIV_IDX.", "PRIV_IDX", value);
763         throw std::invalid_argument("Invalid privilege.");
764     }
765 
766     return privList.at(value);
767 }
768 
convertToSessionSupportIndex(const std::string & value)769 EChannelSessSupported ChannelConfig::convertToSessionSupportIndex(
770     const std::string& value)
771 {
772     auto iter =
773         std::find(sessionSupportList.begin(), sessionSupportList.end(), value);
774     if (iter == sessionSupportList.end())
775     {
776         lg2::error("Invalid session supported: {SESS_STR}", "SESS_STR", value);
777         throw std::invalid_argument("Invalid session supported.");
778     }
779 
780     return static_cast<EChannelSessSupported>(
781         std::distance(sessionSupportList.begin(), iter));
782 }
783 
convertToMediumTypeIndex(const std::string & value)784 EChannelMediumType ChannelConfig::convertToMediumTypeIndex(
785     const std::string& value)
786 {
787     std::unordered_map<std::string, EChannelMediumType>::iterator it =
788         mediumTypeMap.find(value);
789     if (it == mediumTypeMap.end())
790     {
791         lg2::error("Invalid medium type: {MEDIUM_STR}", "MEDIUM_STR", value);
792         throw std::invalid_argument("Invalid medium type.");
793     }
794 
795     return static_cast<EChannelMediumType>(it->second);
796 }
797 
convertToProtocolTypeIndex(const std::string & value)798 EChannelProtocolType ChannelConfig::convertToProtocolTypeIndex(
799     const std::string& value)
800 {
801     std::unordered_map<std::string, EChannelProtocolType>::iterator it =
802         protocolTypeMap.find(value);
803     if (it == protocolTypeMap.end())
804     {
805         lg2::error("Invalid protocol type: {PROTO_STR}", "PROTO_STR", value);
806         throw std::invalid_argument("Invalid protocol type.");
807     }
808 
809     return static_cast<EChannelProtocolType>(it->second);
810 }
811 
readJsonFile(const std::string & configFile)812 Json ChannelConfig::readJsonFile(const std::string& configFile)
813 {
814     std::ifstream jsonFile(configFile);
815     if (!jsonFile.good())
816     {
817         lg2::info("JSON file '{FILE_NAME}' not found", "FILE_NAME", configFile);
818         return nullptr;
819     }
820 
821     Json data = nullptr;
822     try
823     {
824         data = Json::parse(jsonFile, nullptr, false);
825     }
826     catch (const Json::parse_error& e)
827     {
828         lg2::debug("Corrupted channel config: {MSG}", "MSG", e.what());
829         throw std::runtime_error("Corrupted channel config file");
830     }
831 
832     return data;
833 }
834 
writeJsonFile(const std::string & configFile,const Json & jsonData)835 int ChannelConfig::writeJsonFile(const std::string& configFile,
836                                  const Json& jsonData)
837 {
838     const std::string tmpFile = configFile + "_tmp";
839     int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
840                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
841     if (fd < 0)
842     {
843         lg2::error("Error in creating json file '{FILE_NAME}'", "FILE_NAME",
844                    tmpFile);
845         return -EIO;
846     }
847     const auto& writeData = jsonData.dump();
848     if (write(fd, writeData.c_str(), writeData.size()) !=
849         static_cast<ssize_t>(writeData.size()))
850     {
851         close(fd);
852         lg2::error("Error in writing configuration file '{FILE_NAME}'",
853                    "FILE_NAME", tmpFile);
854         return -EIO;
855     }
856     close(fd);
857 
858     if (std::rename(tmpFile.c_str(), configFile.c_str()) != 0)
859     {
860         lg2::error("Error in renaming temporary data file '{FILE_NAME}'",
861                    "FILE_NAME", tmpFile);
862         return -EIO;
863     }
864 
865     return 0;
866 }
867 
setDefaultChannelConfig(const uint8_t chNum,const std::string & chName)868 void ChannelConfig::setDefaultChannelConfig(const uint8_t chNum,
869                                             const std::string& chName)
870 {
871     channelData[chNum].chName = chName;
872     channelData[chNum].chID = chNum;
873     channelData[chNum].isChValid = false;
874     channelData[chNum].activeSessCount = 0;
875     channelData[chNum].isManagementNIC = false;
876 
877     channelData[chNum].chInfo.mediumType = defaultMediumType;
878     channelData[chNum].chInfo.protocolType = defaultProtocolType;
879     channelData[chNum].chInfo.sessionSupported = defaultSessionSupported;
880     channelData[chNum].chInfo.isIpmi = defaultIsIpmiState;
881     channelData[chNum].chInfo.authTypeSupported = defaultAuthType;
882 }
883 
getManagementNICID()884 uint8_t ChannelConfig::getManagementNICID()
885 {
886     static bool idFound = false;
887     static uint8_t id = 0;
888 
889     if (idFound)
890     {
891         return id;
892     }
893 
894     for (uint8_t chIdx = 0; chIdx < maxIpmiChannels; chIdx++)
895     {
896         if (channelData[chIdx].isManagementNIC)
897         {
898             id = chIdx;
899             idFound = true;
900             break;
901         }
902     }
903 
904     if (!idFound)
905     {
906         id = static_cast<uint8_t>(EChannelID::chanLan1);
907         idFound = true;
908     }
909     return id;
910 }
911 
loadChannelConfig()912 int ChannelConfig::loadChannelConfig()
913 {
914     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
915         channelLock{*channelMutex};
916 
917     Json data = readJsonFile(channelConfigDefaultFilename);
918     if (data.empty())
919     {
920         lg2::debug("Error in opening IPMI Channel data file");
921         return -EIO;
922     }
923 
924     channelData.fill(ChannelProperties{});
925 
926     // Collect the list of NIC interfaces connected to the BMC. Use this
927     // information to only add IPMI channels that have active NIC interfaces.
928     struct ifaddrs *ifaddr = nullptr, *ifa = nullptr;
929     if (int err = getifaddrs(&ifaddr); err < 0)
930     {
931         lg2::debug("Unable to acquire network interfaces");
932         return -EIO;
933     }
934 
935     for (int chNum = 0; chNum < maxIpmiChannels; chNum++)
936     {
937         try
938         {
939             std::string chKey = std::to_string(chNum);
940             Json jsonChData = data[chKey].get<Json>();
941             if (jsonChData.is_null())
942             {
943                 // If user didn't want to configure specific channel (say
944                 // reserved channel), then load that index with default values.
945                 setDefaultChannelConfig(chNum, defaultChannelName);
946                 continue;
947             }
948             Json jsonChInfo = jsonChData[channelInfoString].get<Json>();
949             if (jsonChInfo.is_null())
950             {
951                 lg2::error("Invalid/corrupted channel config file");
952                 freeifaddrs(ifaddr);
953                 return -EBADMSG;
954             }
955 
956             bool channelFound = true;
957             // Confirm the LAN channel is present
958             if (jsonChInfo[mediumTypeString].get<std::string>() == "lan-802.3")
959             {
960                 channelFound = false;
961                 for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
962                 {
963                     if (jsonChData[nameString].get<std::string>() ==
964                         ifa->ifa_name)
965                     {
966                         channelFound = true;
967                         break;
968                     }
969                 }
970             }
971             ChannelProperties& chData = channelData[chNum];
972             chData.chID = chNum;
973             chData.chName = jsonChData[nameString].get<std::string>();
974             chData.isChValid = channelFound &&
975                                jsonChData[isValidString].get<bool>();
976             chData.activeSessCount = jsonChData.value(activeSessionsString, 0);
977             chData.maxTransferSize =
978                 jsonChData.value(maxTransferSizeString, smallChannelSize);
979             if (jsonChData.count(isManagementNIC) != 0)
980             {
981                 chData.isManagementNIC =
982                     jsonChData[isManagementNIC].get<bool>();
983             }
984 
985             std::string medTypeStr =
986                 jsonChInfo[mediumTypeString].get<std::string>();
987             chData.chInfo.mediumType =
988                 static_cast<uint8_t>(convertToMediumTypeIndex(medTypeStr));
989             std::string protoTypeStr =
990                 jsonChInfo[protocolTypeString].get<std::string>();
991             chData.chInfo.protocolType =
992                 static_cast<uint8_t>(convertToProtocolTypeIndex(protoTypeStr));
993             std::string sessStr =
994                 jsonChInfo[sessionSupportedString].get<std::string>();
995             chData.chInfo.sessionSupported =
996                 static_cast<uint8_t>(convertToSessionSupportIndex(sessStr));
997             chData.chInfo.isIpmi = jsonChInfo[isIpmiString].get<bool>();
998             chData.chInfo.authTypeSupported = defaultAuthType;
999         }
1000         catch (const Json::exception& e)
1001         {
1002             lg2::debug("Json Exception caught: {MSG}", "MSG", e.what());
1003             freeifaddrs(ifaddr);
1004 
1005             return -EBADMSG;
1006         }
1007         catch (const std::invalid_argument& e)
1008         {
1009             lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1010             freeifaddrs(ifaddr);
1011             return -EBADMSG;
1012         }
1013     }
1014     freeifaddrs(ifaddr);
1015 
1016     return 0;
1017 }
1018 
readChannelVolatileData()1019 int ChannelConfig::readChannelVolatileData()
1020 {
1021     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1022         channelLock{*channelMutex};
1023 
1024     Json data = readJsonFile(channelVolatileDataFilename);
1025     if (data == nullptr)
1026     {
1027         lg2::debug("Error in opening IPMI Channel data file");
1028         return -EIO;
1029     }
1030     try
1031     {
1032         // Fill in global structure
1033         for (auto it = data.begin(); it != data.end(); ++it)
1034         {
1035             std::string chKey = it.key();
1036             uint8_t chNum = std::stoi(chKey, nullptr, 10);
1037             if (chNum >= maxIpmiChannels)
1038             {
1039                 lg2::debug("Invalid channel access entry in config file");
1040                 throw std::out_of_range("Out of range - channel number");
1041             }
1042             Json jsonChData = it.value();
1043             if (!jsonChData.is_null())
1044             {
1045                 std::string accModeStr =
1046                     jsonChData[accessModeString].get<std::string>();
1047                 channelData[chNum].chAccess.chVolatileData.accessMode =
1048                     static_cast<uint8_t>(convertToAccessModeIndex(accModeStr));
1049                 channelData[chNum].chAccess.chVolatileData.userAuthDisabled =
1050                     jsonChData[userAuthDisabledString].get<bool>();
1051                 channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled =
1052                     jsonChData[perMsgAuthDisabledString].get<bool>();
1053                 channelData[chNum].chAccess.chVolatileData.alertingDisabled =
1054                     jsonChData[alertingDisabledString].get<bool>();
1055                 std::string privStr =
1056                     jsonChData[privLimitString].get<std::string>();
1057                 channelData[chNum].chAccess.chVolatileData.privLimit =
1058                     static_cast<uint8_t>(convertToPrivLimitIndex(privStr));
1059             }
1060             else
1061             {
1062                 lg2::error(
1063                     "Invalid/corrupted volatile channel access file '{FILE}'",
1064                     "FILE", channelVolatileDataFilename);
1065                 throw std::runtime_error(
1066                     "Corrupted volatile channel access file");
1067             }
1068         }
1069     }
1070     catch (const Json::exception& e)
1071     {
1072         lg2::debug("Json Exception caught: {MSG}", "MSG", e.what());
1073         throw std::runtime_error("Corrupted volatile channel access file");
1074     }
1075     catch (const std::invalid_argument& e)
1076     {
1077         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1078         throw std::runtime_error("Corrupted volatile channel access file");
1079     }
1080 
1081     // Update the timestamp
1082     voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename);
1083     return 0;
1084 }
1085 
readChannelPersistData()1086 int ChannelConfig::readChannelPersistData()
1087 {
1088     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1089         channelLock{*channelMutex};
1090 
1091     Json data = readJsonFile(channelNvDataFilename);
1092     if (data == nullptr)
1093     {
1094         lg2::debug("Error in opening IPMI Channel data file");
1095         return -EIO;
1096     }
1097     try
1098     {
1099         // Fill in global structure
1100         for (auto it = data.begin(); it != data.end(); ++it)
1101         {
1102             std::string chKey = it.key();
1103             uint8_t chNum = std::stoi(chKey, nullptr, 10);
1104             if (chNum >= maxIpmiChannels)
1105             {
1106                 lg2::debug("Invalid channel access entry in config file");
1107                 throw std::out_of_range("Out of range - channel number");
1108             }
1109             Json jsonChData = it.value();
1110             if (!jsonChData.is_null())
1111             {
1112                 std::string accModeStr =
1113                     jsonChData[accessModeString].get<std::string>();
1114                 channelData[chNum].chAccess.chNonVolatileData.accessMode =
1115                     static_cast<uint8_t>(convertToAccessModeIndex(accModeStr));
1116                 channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled =
1117                     jsonChData[userAuthDisabledString].get<bool>();
1118                 channelData[chNum]
1119                     .chAccess.chNonVolatileData.perMsgAuthDisabled =
1120                     jsonChData[perMsgAuthDisabledString].get<bool>();
1121                 channelData[chNum].chAccess.chNonVolatileData.alertingDisabled =
1122                     jsonChData[alertingDisabledString].get<bool>();
1123                 std::string privStr =
1124                     jsonChData[privLimitString].get<std::string>();
1125                 channelData[chNum].chAccess.chNonVolatileData.privLimit =
1126                     static_cast<uint8_t>(convertToPrivLimitIndex(privStr));
1127             }
1128             else
1129             {
1130                 lg2::error("Invalid/corrupted nv channel access file {FILE}",
1131                            "FILE", channelNvDataFilename);
1132                 throw std::runtime_error("Corrupted nv channel access file");
1133             }
1134         }
1135     }
1136     catch (const Json::exception& e)
1137     {
1138         lg2::debug("Json Exception caught: {MSG}", "MSG", e.what());
1139         throw std::runtime_error("Corrupted nv channel access file");
1140     }
1141     catch (const std::invalid_argument& e)
1142     {
1143         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1144         throw std::runtime_error("Corrupted nv channel access file");
1145     }
1146 
1147     // Update the timestamp
1148     nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename);
1149     return 0;
1150 }
1151 
writeChannelVolatileData()1152 int ChannelConfig::writeChannelVolatileData()
1153 {
1154     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1155         channelLock{*channelMutex};
1156     Json outData;
1157 
1158     try
1159     {
1160         for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
1161         {
1162             if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
1163             {
1164                 Json jsonObj;
1165                 std::string chKey = std::to_string(chNum);
1166                 std::string accModeStr = convertToAccessModeString(
1167                     channelData[chNum].chAccess.chVolatileData.accessMode);
1168                 jsonObj[accessModeString] = accModeStr;
1169                 jsonObj[userAuthDisabledString] =
1170                     channelData[chNum].chAccess.chVolatileData.userAuthDisabled;
1171                 jsonObj[perMsgAuthDisabledString] =
1172                     channelData[chNum]
1173                         .chAccess.chVolatileData.perMsgAuthDisabled;
1174                 jsonObj[alertingDisabledString] =
1175                     channelData[chNum].chAccess.chVolatileData.alertingDisabled;
1176                 std::string privStr = convertToPrivLimitString(
1177                     channelData[chNum].chAccess.chVolatileData.privLimit);
1178                 jsonObj[privLimitString] = privStr;
1179 
1180                 outData[chKey] = jsonObj;
1181             }
1182         }
1183     }
1184     catch (const std::invalid_argument& e)
1185     {
1186         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1187         return -EINVAL;
1188     }
1189 
1190     if (writeJsonFile(channelVolatileDataFilename, outData) != 0)
1191     {
1192         lg2::debug("Error in write JSON data to file");
1193         return -EIO;
1194     }
1195 
1196     // Update the timestamp
1197     voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename);
1198     return 0;
1199 }
1200 
writeChannelPersistData()1201 int ChannelConfig::writeChannelPersistData()
1202 {
1203     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1204         channelLock{*channelMutex};
1205     Json outData;
1206 
1207     try
1208     {
1209         for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
1210         {
1211             if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
1212             {
1213                 Json jsonObj;
1214                 std::string chKey = std::to_string(chNum);
1215                 std::string accModeStr = convertToAccessModeString(
1216                     channelData[chNum].chAccess.chNonVolatileData.accessMode);
1217                 jsonObj[accessModeString] = accModeStr;
1218                 jsonObj[userAuthDisabledString] =
1219                     channelData[chNum]
1220                         .chAccess.chNonVolatileData.userAuthDisabled;
1221                 jsonObj[perMsgAuthDisabledString] =
1222                     channelData[chNum]
1223                         .chAccess.chNonVolatileData.perMsgAuthDisabled;
1224                 jsonObj[alertingDisabledString] =
1225                     channelData[chNum]
1226                         .chAccess.chNonVolatileData.alertingDisabled;
1227                 std::string privStr = convertToPrivLimitString(
1228                     channelData[chNum].chAccess.chNonVolatileData.privLimit);
1229                 jsonObj[privLimitString] = privStr;
1230 
1231                 outData[chKey] = jsonObj;
1232             }
1233         }
1234     }
1235     catch (const std::invalid_argument& e)
1236     {
1237         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1238         return -EINVAL;
1239     }
1240 
1241     if (writeJsonFile(channelNvDataFilename, outData) != 0)
1242     {
1243         lg2::debug("Error in write JSON data to file");
1244         return -EIO;
1245     }
1246 
1247     // Update the timestamp
1248     nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename);
1249     return 0;
1250 }
1251 
checkAndReloadNVData()1252 int ChannelConfig::checkAndReloadNVData()
1253 {
1254     std::time_t updateTime = getUpdatedFileTime(channelNvDataFilename);
1255     int ret = 0;
1256     if (updateTime != nvFileLastUpdatedTime || updateTime == -EIO)
1257     {
1258         try
1259         {
1260             ret = readChannelPersistData();
1261         }
1262         catch (const std::exception& e)
1263         {
1264             lg2::error("Exception caught in readChannelPersistData: {MSG}",
1265                        "MSG", e.what());
1266             ret = -EIO;
1267         }
1268     }
1269     return ret;
1270 }
1271 
checkAndReloadVolatileData()1272 int ChannelConfig::checkAndReloadVolatileData()
1273 {
1274     std::time_t updateTime = getUpdatedFileTime(channelVolatileDataFilename);
1275     int ret = 0;
1276     if (updateTime != voltFileLastUpdatedTime || updateTime == -EIO)
1277     {
1278         try
1279         {
1280             ret = readChannelVolatileData();
1281         }
1282         catch (const std::exception& e)
1283         {
1284             lg2::error("Exception caught in readChannelVolatileData: {MSG}",
1285                        "MSG", e.what());
1286             ret = -EIO;
1287         }
1288     }
1289     return ret;
1290 }
1291 
setDbusProperty(const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const DbusVariant & value)1292 int ChannelConfig::setDbusProperty(
1293     const std::string& service, const std::string& objPath,
1294     const std::string& interface, const std::string& property,
1295     const DbusVariant& value)
1296 {
1297     try
1298     {
1299         auto method =
1300             bus.new_method_call(service.c_str(), objPath.c_str(),
1301                                 "org.freedesktop.DBus.Properties", "Set");
1302 
1303         method.append(interface, property, value);
1304 
1305         auto reply = bus.call(method);
1306     }
1307     catch (const sdbusplus::exception_t& e)
1308     {
1309         lg2::debug(
1310             "set-property {SERVICE}:{OBJPATH}/{INTERFACE}.{PROP} failed: {MSG}",
1311             "SERVICE", service, "OBJPATH", objPath, "INTERFACE", interface,
1312             "PROP", property, "MSG", e);
1313         return -EIO;
1314     }
1315 
1316     return 0;
1317 }
1318 
syncNetworkChannelConfig()1319 int ChannelConfig::syncNetworkChannelConfig()
1320 {
1321     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1322         channelLock{*channelMutex};
1323     bool isUpdated = false;
1324     for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
1325     {
1326         if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
1327         {
1328             std::string intfPrivStr;
1329             uint8_t intfPriv = 0;
1330             try
1331             {
1332                 std::string networkIntfObj =
1333                     std::string(networkIntfObjectBasePath) + "/" +
1334                     channelData[chNum].chName;
1335                 auto propValue = ipmi::getDbusProperty(
1336                     bus, networkIntfServiceName, networkIntfObj,
1337                     networkChConfigIntfName, privilegePropertyString);
1338 
1339                 intfPrivStr = std::get<std::string>(propValue);
1340                 intfPriv =
1341                     static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
1342             }
1343             catch (const std::bad_variant_access& e)
1344             {
1345                 lg2::debug("Network interface '{INTERFACE}' does not exist",
1346                            "INTERFACE", channelData[chNum].chName);
1347                 continue;
1348             }
1349             catch (const sdbusplus::exception_t& e)
1350             {
1351                 lg2::debug("Network interface '{INTERFACE}' does not exist",
1352                            "INTERFACE", channelData[chNum].chName);
1353                 continue;
1354             }
1355             catch (const std::invalid_argument& e)
1356             {
1357                 lg2::debug("exception: Invalid privilege");
1358                 continue;
1359             }
1360 
1361             if (channelData[chNum].chAccess.chNonVolatileData.privLimit !=
1362                 intfPriv)
1363             {
1364                 isUpdated = true;
1365                 channelData[chNum].chAccess.chNonVolatileData.privLimit =
1366                     intfPriv;
1367                 channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
1368             }
1369         }
1370     }
1371 
1372     if (isUpdated)
1373     {
1374         // Write persistent data to file
1375         if (writeChannelPersistData() != 0)
1376         {
1377             lg2::debug("Failed to update the persistent data file");
1378             return -EIO;
1379         }
1380         // Write Volatile data to file
1381         if (writeChannelVolatileData() != 0)
1382         {
1383             lg2::debug("Failed to update the channel volatile data");
1384             return -EIO;
1385         }
1386     }
1387 
1388     return 0;
1389 }
1390 
initChannelPersistData()1391 void ChannelConfig::initChannelPersistData()
1392 {
1393     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1394         channelLock{*channelMutex};
1395 
1396     /* Always read the channel config */
1397     if (loadChannelConfig() != 0)
1398     {
1399         lg2::error("Failed to read channel config file");
1400         throw std::ios_base::failure("Failed to load channel configuration");
1401     }
1402 
1403     /* Populate the channel persist data */
1404     if (readChannelPersistData() != 0)
1405     {
1406         // Copy default NV data to RW location
1407         std::filesystem::copy_file(channelAccessDefaultFilename,
1408                                    channelNvDataFilename);
1409 
1410         // Load the channel access NV data
1411         if (readChannelPersistData() != 0)
1412         {
1413             lg2::error("Failed to read channel access NV data");
1414             throw std::ios_base::failure(
1415                 "Failed to read channel access NV configuration");
1416         }
1417     }
1418 
1419     // First check the volatile data file
1420     // If not present, load the default values
1421     if (readChannelVolatileData() != 0)
1422     {
1423         // Copy default volatile data to temporary location
1424         // NV file(channelNvDataFilename) must have created by now.
1425         std::filesystem::copy_file(channelNvDataFilename,
1426                                    channelVolatileDataFilename);
1427 
1428         // Load the channel access volatile data
1429         if (readChannelVolatileData() != 0)
1430         {
1431             lg2::error("Failed to read channel access volatile data");
1432             throw std::ios_base::failure(
1433                 "Failed to read channel access volatile configuration");
1434         }
1435     }
1436 
1437     // Synchronize the channel config(priv) with network channel
1438     // configuration(priv) over dbus
1439     if (syncNetworkChannelConfig() != 0)
1440     {
1441         lg2::error(
1442             "Failed to synchronize data with network channel config over dbus");
1443         throw std::ios_base::failure(
1444             "Failed to synchronize data with network channel config over dbus");
1445     }
1446 
1447     lg2::debug("Successfully completed channel data initialization.");
1448     return;
1449 }
1450 
1451 } // namespace ipmi
1452