xref: /openbmc/dbus-sensors/src/Utils.cpp (revision eacbfdd1)
1 /*
2 // Copyright (c) 2017 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 "Utils.hpp"
18 
19 #include "dbus-sensor_config.h"
20 
21 #include "DeviceMgmt.hpp"
22 #include "VariantVisitors.hpp"
23 
24 #include <boost/asio/error.hpp>
25 #include <boost/asio/steady_timer.hpp>
26 #include <boost/container/flat_map.hpp>
27 #include <sdbusplus/asio/connection.hpp>
28 #include <sdbusplus/asio/object_server.hpp>
29 #include <sdbusplus/bus.hpp>
30 #include <sdbusplus/bus/match.hpp>
31 #include <sdbusplus/exception.hpp>
32 #include <sdbusplus/message.hpp>
33 #include <sdbusplus/message/native_types.hpp>
34 
35 #include <algorithm>
36 #include <array>
37 #include <chrono>
38 #include <cstddef>
39 #include <cstring>
40 #include <filesystem>
41 #include <fstream>
42 #include <functional>
43 #include <iostream>
44 #include <iterator>
45 #include <memory>
46 #include <optional>
47 #include <regex>
48 #include <set>
49 #include <span>
50 #include <stdexcept>
51 #include <string>
52 #include <string_view>
53 #include <system_error>
54 #include <tuple>
55 #include <utility>
56 #include <variant>
57 #include <vector>
58 
59 namespace fs = std::filesystem;
60 
61 static bool powerStatusOn = false;
62 static bool biosHasPost = false;
63 static bool manufacturingMode = false;
64 static bool chassisStatusOn = false;
65 
66 static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
67 static std::unique_ptr<sdbusplus::bus::match_t> postMatch = nullptr;
68 static std::unique_ptr<sdbusplus::bus::match_t> chassisMatch = nullptr;
69 
70 /**
71  * return the contents of a file
72  * @param[in] hwmonFile - the path to the file to read
73  * @return the contents of the file as a string or nullopt if the file could not
74  * be opened.
75  */
76 
openAndRead(const std::string & hwmonFile)77 std::optional<std::string> openAndRead(const std::string& hwmonFile)
78 {
79     std::string fileVal;
80     std::ifstream fileStream(hwmonFile);
81     if (!fileStream.is_open())
82     {
83         return std::nullopt;
84     }
85     std::getline(fileStream, fileVal);
86     return fileVal;
87 }
88 
89 /**
90  * given a hwmon temperature base name if valid return the full path else
91  * nullopt
92  * @param[in] directory - the hwmon sysfs directory
93  * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
94  * is empty then *everything* is permitted.
95  * @return a string to the full path of the file to create a temp sensor with or
96  * nullopt to indicate that no sensor should be created for this basename.
97  */
98 std::optional<std::string>
getFullHwmonFilePath(const std::string & directory,const std::string & hwmonBaseName,const std::set<std::string> & permitSet)99     getFullHwmonFilePath(const std::string& directory,
100                          const std::string& hwmonBaseName,
101                          const std::set<std::string>& permitSet)
102 {
103     std::optional<std::string> result;
104     std::string filename;
105     if (permitSet.empty())
106     {
107         result = directory + "/" + hwmonBaseName + "_input";
108         return result;
109     }
110     filename = directory + "/" + hwmonBaseName + "_label";
111     auto searchVal = openAndRead(filename);
112     if (!searchVal)
113     {
114         /* if the hwmon temp doesn't have a corresponding label file
115          * then use the hwmon temperature base name
116          */
117         searchVal = hwmonBaseName;
118     }
119     if (permitSet.find(*searchVal) != permitSet.end())
120     {
121         result = directory + "/" + hwmonBaseName + "_input";
122     }
123     return result;
124 }
125 
126 /**
127  * retrieve a set of basenames and labels to allow sensor creation for.
128  * @param[in] config - a map representing the configuration for a specific
129  * device
130  * @return a set of basenames and labels to allow sensor creation for. An empty
131  * set indicates that everything is permitted.
132  */
getPermitSet(const SensorBaseConfigMap & config)133 std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
134 {
135     auto permitAttribute = config.find("Labels");
136     std::set<std::string> permitSet;
137     if (permitAttribute != config.end())
138     {
139         try
140         {
141             auto val =
142                 std::get<std::vector<std::string>>(permitAttribute->second);
143 
144             permitSet.insert(std::make_move_iterator(val.begin()),
145                              std::make_move_iterator(val.end()));
146         }
147         catch (const std::bad_variant_access& err)
148         {
149             std::cerr << err.what()
150                       << ":PermitList does not contain a list, wrong "
151                          "variant type.\n";
152         }
153     }
154     return permitSet;
155 }
156 
getSensorConfiguration(const std::string & type,const std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,ManagedObjectType & resp,bool useCache)157 bool getSensorConfiguration(
158     const std::string& type,
159     const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
160     ManagedObjectType& resp, bool useCache)
161 {
162     static ManagedObjectType managedObj;
163     std::string typeIntf = configInterfaceName(type);
164 
165     if (!useCache)
166     {
167         managedObj.clear();
168         sdbusplus::message_t getManagedObjects =
169             dbusConnection->new_method_call(
170                 entityManagerName, "/xyz/openbmc_project/inventory",
171                 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
172         try
173         {
174             sdbusplus::message_t reply =
175                 dbusConnection->call(getManagedObjects);
176             reply.read(managedObj);
177         }
178         catch (const sdbusplus::exception_t& e)
179         {
180             std::cerr << "While calling GetManagedObjects on service:"
181                       << entityManagerName << " exception name:" << e.name()
182                       << "and description:" << e.description()
183                       << " was thrown\n";
184             return false;
185         }
186     }
187     for (const auto& pathPair : managedObj)
188     {
189         for (const auto& [intf, cfg] : pathPair.second)
190         {
191             if (intf.starts_with(typeIntf))
192             {
193                 resp.emplace(pathPair);
194                 break;
195             }
196         }
197     }
198     return true;
199 }
200 
findFiles(const fs::path & dirPath,std::string_view matchString,std::vector<fs::path> & foundPaths,int symlinkDepth)201 bool findFiles(const fs::path& dirPath, std::string_view matchString,
202                std::vector<fs::path>& foundPaths, int symlinkDepth)
203 {
204     std::error_code ec;
205     if (!fs::exists(dirPath, ec))
206     {
207         return false;
208     }
209 
210     std::vector<std::regex> matchPieces;
211 
212     size_t pos = 0;
213     std::string token;
214     // Generate the regex expressions list from the match we were given
215     while ((pos = matchString.find('/')) != std::string::npos)
216     {
217         token = matchString.substr(0, pos);
218         matchPieces.emplace_back(token);
219         matchString.remove_prefix(pos + 1);
220     }
221     matchPieces.emplace_back(std::string{matchString});
222 
223     // Check if the match string contains directories, and skip the match of
224     // subdirectory if not
225     if (matchPieces.size() <= 1)
226     {
227         std::regex search(std::string{matchString});
228         std::smatch match;
229         for (auto p = fs::recursive_directory_iterator(
230                  dirPath, fs::directory_options::follow_directory_symlink);
231              p != fs::recursive_directory_iterator(); ++p)
232         {
233             std::string path = p->path().string();
234             if (!is_directory(*p))
235             {
236                 if (std::regex_search(path, match, search))
237                 {
238                     foundPaths.emplace_back(p->path());
239                 }
240             }
241             if (p.depth() >= symlinkDepth)
242             {
243                 p.disable_recursion_pending();
244             }
245         }
246         return true;
247     }
248 
249     // The match string contains directories, verify each level of sub
250     // directories
251     for (auto p = fs::recursive_directory_iterator(
252              dirPath, fs::directory_options::follow_directory_symlink);
253          p != fs::recursive_directory_iterator(); ++p)
254     {
255         std::vector<std::regex>::iterator matchPiece = matchPieces.begin();
256         fs::path::iterator pathIt = p->path().begin();
257         for (const fs::path& dir : dirPath)
258         {
259             if (dir.empty())
260             {
261                 // When the path ends with '/', it gets am empty path
262                 // skip such case.
263                 break;
264             }
265             pathIt++;
266         }
267 
268         while (pathIt != p->path().end())
269         {
270             // Found a path deeper than match.
271             if (matchPiece == matchPieces.end())
272             {
273                 p.disable_recursion_pending();
274                 break;
275             }
276             std::smatch match;
277             std::string component = pathIt->string();
278             std::regex regexPiece(*matchPiece);
279             if (!std::regex_match(component, match, regexPiece))
280             {
281                 // path prefix doesn't match, no need to iterate further
282                 p.disable_recursion_pending();
283                 break;
284             }
285             matchPiece++;
286             pathIt++;
287         }
288 
289         if (!is_directory(*p))
290         {
291             if (matchPiece == matchPieces.end())
292             {
293                 foundPaths.emplace_back(p->path());
294             }
295         }
296 
297         if (p.depth() >= symlinkDepth)
298         {
299             p.disable_recursion_pending();
300         }
301     }
302     return true;
303 }
304 
isPowerOn()305 bool isPowerOn()
306 {
307     if (!powerMatch)
308     {
309         throw std::runtime_error("Power Match Not Created");
310     }
311     return powerStatusOn;
312 }
313 
hasBiosPost()314 bool hasBiosPost()
315 {
316     if (!postMatch)
317     {
318         throw std::runtime_error("Post Match Not Created");
319     }
320     return biosHasPost;
321 }
322 
isChassisOn()323 bool isChassisOn()
324 {
325     if (!chassisMatch)
326     {
327         throw std::runtime_error("Chassis On Match Not Created");
328     }
329     return chassisStatusOn;
330 }
331 
readingStateGood(const PowerState & powerState)332 bool readingStateGood(const PowerState& powerState)
333 {
334     if (powerState == PowerState::on && !isPowerOn())
335     {
336         return false;
337     }
338     if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn()))
339     {
340         return false;
341     }
342     if (powerState == PowerState::chassisOn && !isChassisOn())
343     {
344         return false;
345     }
346 
347     return true;
348 }
349 
350 static void
getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t retries=2)351     getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
352                    size_t retries = 2)
353 {
354     conn->async_method_call(
355         [conn, retries](boost::system::error_code ec,
356                         const std::variant<std::string>& state) {
357         if (ec)
358         {
359             if (retries != 0U)
360             {
361                 auto timer = std::make_shared<boost::asio::steady_timer>(
362                     conn->get_io_context());
363                 timer->expires_after(std::chrono::seconds(15));
364                 timer->async_wait(
365                     [timer, conn, retries](boost::system::error_code) {
366                     getPowerStatus(conn, retries - 1);
367                 });
368                 return;
369             }
370 
371             // we commonly come up before power control, we'll capture the
372             // property change later
373             std::cerr << "error getting power status " << ec.message() << "\n";
374             return;
375         }
376         powerStatusOn = std::get<std::string>(state).ends_with(".Running");
377     },
378         power::busname, power::path, properties::interface, properties::get,
379         power::interface, power::property);
380 }
381 
382 static void
getPostStatus(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t retries=2)383     getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
384                   size_t retries = 2)
385 {
386     conn->async_method_call(
387         [conn, retries](boost::system::error_code ec,
388                         const std::variant<std::string>& state) {
389         if (ec)
390         {
391             if (retries != 0U)
392             {
393                 auto timer = std::make_shared<boost::asio::steady_timer>(
394                     conn->get_io_context());
395                 timer->expires_after(std::chrono::seconds(15));
396                 timer->async_wait(
397                     [timer, conn, retries](boost::system::error_code) {
398                     getPostStatus(conn, retries - 1);
399                 });
400                 return;
401             }
402             // we commonly come up before power control, we'll capture the
403             // property change later
404             std::cerr << "error getting post status " << ec.message() << "\n";
405             return;
406         }
407         const auto& value = std::get<std::string>(state);
408         biosHasPost = (value != "Inactive") &&
409                       (value != "xyz.openbmc_project.State.OperatingSystem."
410                                 "Status.OSStatus.Inactive");
411     },
412         post::busname, post::path, properties::interface, properties::get,
413         post::interface, post::property);
414 }
415 
416 static void
getChassisStatus(const std::shared_ptr<sdbusplus::asio::connection> & conn,size_t retries=2)417     getChassisStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
418                      size_t retries = 2)
419 {
420     conn->async_method_call(
421         [conn, retries](boost::system::error_code ec,
422                         const std::variant<std::string>& state) {
423         if (ec)
424         {
425             if (retries != 0U)
426             {
427                 auto timer = std::make_shared<boost::asio::steady_timer>(
428                     conn->get_io_context());
429                 timer->expires_after(std::chrono::seconds(15));
430                 timer->async_wait(
431                     [timer, conn, retries](boost::system::error_code) {
432                     getChassisStatus(conn, retries - 1);
433                 });
434                 return;
435             }
436 
437             // we commonly come up before power control, we'll capture the
438             // property change later
439             std::cerr << "error getting chassis power status " << ec.message()
440                       << "\n";
441             return;
442         }
443         chassisStatusOn = std::get<std::string>(state).ends_with(chassis::sOn);
444     },
445         chassis::busname, chassis::path, properties::interface, properties::get,
446         chassis::interface, chassis::property);
447 }
448 
setupPowerMatchCallback(const std::shared_ptr<sdbusplus::asio::connection> & conn,std::function<void (PowerState type,bool state)> && hostStatusCallback)449 void setupPowerMatchCallback(
450     const std::shared_ptr<sdbusplus::asio::connection>& conn,
451     std::function<void(PowerState type, bool state)>&& hostStatusCallback)
452 {
453     static boost::asio::steady_timer timer(conn->get_io_context());
454     static boost::asio::steady_timer timerChassisOn(conn->get_io_context());
455     // create a match for powergood changes, first time do a method call to
456     // cache the correct value
457     if (powerMatch)
458     {
459         return;
460     }
461 
462     powerMatch = std::make_unique<sdbusplus::bus::match_t>(
463         static_cast<sdbusplus::bus_t&>(*conn),
464         "type='signal',interface='" + std::string(properties::interface) +
465             "',path='" + std::string(power::path) + "',arg0='" +
466             std::string(power::interface) + "'",
467         [hostStatusCallback](sdbusplus::message_t& message) {
468         std::string objectName;
469         boost::container::flat_map<std::string, std::variant<std::string>>
470             values;
471         message.read(objectName, values);
472         auto findState = values.find(power::property);
473         if (findState != values.end())
474         {
475             bool on =
476                 std::get<std::string>(findState->second).ends_with(".Running");
477             if (!on)
478             {
479                 timer.cancel();
480                 powerStatusOn = false;
481                 hostStatusCallback(PowerState::on, powerStatusOn);
482                 return;
483             }
484             // on comes too quickly
485             timer.expires_after(std::chrono::seconds(10));
486             timer.async_wait(
487                 [hostStatusCallback](boost::system::error_code ec) {
488                 if (ec == boost::asio::error::operation_aborted)
489                 {
490                     return;
491                 }
492                 if (ec)
493                 {
494                     std::cerr << "Timer error " << ec.message() << "\n";
495                     return;
496                 }
497                 powerStatusOn = true;
498                 hostStatusCallback(PowerState::on, powerStatusOn);
499             });
500         }
501     });
502 
503     postMatch = std::make_unique<sdbusplus::bus::match_t>(
504         static_cast<sdbusplus::bus_t&>(*conn),
505         "type='signal',interface='" + std::string(properties::interface) +
506             "',path='" + std::string(post::path) + "',arg0='" +
507             std::string(post::interface) + "'",
508         [hostStatusCallback](sdbusplus::message_t& message) {
509         std::string objectName;
510         boost::container::flat_map<std::string, std::variant<std::string>>
511             values;
512         message.read(objectName, values);
513         auto findState = values.find(post::property);
514         if (findState != values.end())
515         {
516             auto& value = std::get<std::string>(findState->second);
517             biosHasPost = (value != "Inactive") &&
518                           (value != "xyz.openbmc_project.State.OperatingSystem."
519                                     "Status.OSStatus.Inactive");
520             hostStatusCallback(PowerState::biosPost, biosHasPost);
521         }
522     });
523 
524     chassisMatch = std::make_unique<sdbusplus::bus::match_t>(
525         static_cast<sdbusplus::bus_t&>(*conn),
526         "type='signal',interface='" + std::string(properties::interface) +
527             "',path='" + std::string(chassis::path) + "',arg0='" +
528             std::string(chassis::interface) + "'",
529         [hostStatusCallback = std::move(hostStatusCallback)](
530             sdbusplus::message_t& message) {
531         std::string objectName;
532         boost::container::flat_map<std::string, std::variant<std::string>>
533             values;
534         message.read(objectName, values);
535         auto findState = values.find(chassis::property);
536         if (findState != values.end())
537         {
538             bool on = std::get<std::string>(findState->second)
539                           .ends_with(chassis::sOn);
540             if (!on)
541             {
542                 timerChassisOn.cancel();
543                 chassisStatusOn = false;
544                 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
545                 return;
546             }
547             // on comes too quickly
548             timerChassisOn.expires_after(std::chrono::seconds(10));
549             timerChassisOn.async_wait(
550                 [hostStatusCallback](boost::system::error_code ec) {
551                 if (ec == boost::asio::error::operation_aborted)
552                 {
553                     return;
554                 }
555                 if (ec)
556                 {
557                     std::cerr << "Timer error " << ec.message() << "\n";
558                     return;
559                 }
560                 chassisStatusOn = true;
561                 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
562             });
563         }
564     });
565     getPowerStatus(conn);
566     getPostStatus(conn);
567     getChassisStatus(conn);
568 }
569 
setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection> & conn)570 void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
571 {
572     setupPowerMatchCallback(conn, [](PowerState, bool) {});
573 }
574 
575 // replaces limits if MinReading and MaxReading are found.
findLimits(std::pair<double,double> & limits,const SensorBaseConfiguration * data)576 void findLimits(std::pair<double, double>& limits,
577                 const SensorBaseConfiguration* data)
578 {
579     if (data == nullptr)
580     {
581         return;
582     }
583     auto maxFind = data->second.find("MaxReading");
584     auto minFind = data->second.find("MinReading");
585 
586     if (minFind != data->second.end())
587     {
588         limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
589     }
590     if (maxFind != data->second.end())
591     {
592         limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
593     }
594 }
595 
createAssociation(std::shared_ptr<sdbusplus::asio::dbus_interface> & association,const std::string & path)596 void createAssociation(
597     std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
598     const std::string& path)
599 {
600     if (association)
601     {
602         fs::path p(path);
603 
604         std::vector<Association> associations;
605         associations.emplace_back("chassis", "all_sensors",
606                                   p.parent_path().string());
607         association->register_property("Associations", associations);
608         association->initialize();
609     }
610 }
611 
setInventoryAssociation(const std::shared_ptr<sdbusplus::asio::dbus_interface> & association,const std::string & inventoryPath,const std::string & chassisPath)612 void setInventoryAssociation(
613     const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
614     const std::string& inventoryPath, const std::string& chassisPath)
615 {
616     if (association)
617     {
618         std::vector<Association> associations;
619         associations.emplace_back("inventory", "sensors", inventoryPath);
620         associations.emplace_back("chassis", "all_sensors", chassisPath);
621 
622         association->register_property("Associations", associations);
623         association->initialize();
624     }
625 }
626 
findContainingChassis(std::string_view configParent,const GetSubTreeType & subtree)627 std::optional<std::string> findContainingChassis(std::string_view configParent,
628                                                  const GetSubTreeType& subtree)
629 {
630     // A parent that is a chassis takes precedence
631     for (const auto& [obj, services] : subtree)
632     {
633         if (obj == configParent)
634         {
635             return obj;
636         }
637     }
638 
639     // If the parent is not a chassis, the system chassis is used. This does not
640     // work if there is more than one System, but we assume there is only one
641     // today.
642     for (const auto& [obj, services] : subtree)
643     {
644         for (const auto& [service, interfaces] : services)
645         {
646             if (std::find(interfaces.begin(), interfaces.end(),
647                           "xyz.openbmc_project.Inventory.Item.System") !=
648                 interfaces.end())
649             {
650                 return obj;
651             }
652         }
653     }
654     return std::nullopt;
655 }
656 
createInventoryAssoc(const std::shared_ptr<sdbusplus::asio::connection> & conn,const std::shared_ptr<sdbusplus::asio::dbus_interface> & association,const std::string & path)657 void createInventoryAssoc(
658     const std::shared_ptr<sdbusplus::asio::connection>& conn,
659     const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
660     const std::string& path)
661 {
662     if (!association)
663     {
664         return;
665     }
666 
667     constexpr auto allInterfaces = std::to_array({
668         "xyz.openbmc_project.Inventory.Item.Board",
669         "xyz.openbmc_project.Inventory.Item.Chassis",
670     });
671 
672     conn->async_method_call(
673         [association, path](const boost::system::error_code ec,
674                             const GetSubTreeType& subtree) {
675         // The parent of the config is always the inventory object, and may be
676         // the associated chassis. If the parent is not itself a chassis or
677         // board, the sensor is associated with the system chassis.
678         std::string parent = fs::path(path).parent_path().string();
679         if (ec)
680         {
681             // In case of error, set the default associations and
682             // initialize the association Interface.
683             setInventoryAssociation(association, parent, parent);
684             return;
685         }
686         setInventoryAssociation(
687             association, parent,
688             findContainingChassis(parent, subtree).value_or(parent));
689     },
690         mapper::busName, mapper::path, mapper::interface, "GetSubTree",
691         "/xyz/openbmc_project/inventory/system", 2, allInterfaces);
692 }
693 
readFile(const std::string & thresholdFile,const double & scaleFactor)694 std::optional<double> readFile(const std::string& thresholdFile,
695                                const double& scaleFactor)
696 {
697     std::string line;
698     std::ifstream labelFile(thresholdFile);
699     if (labelFile.good())
700     {
701         std::getline(labelFile, line);
702         labelFile.close();
703 
704         try
705         {
706             return std::stod(line) / scaleFactor;
707         }
708         catch (const std::invalid_argument&)
709         {
710             return std::nullopt;
711         }
712     }
713     return std::nullopt;
714 }
715 
716 std::optional<std::tuple<std::string, std::string, std::string>>
splitFileName(const fs::path & filePath)717     splitFileName(const fs::path& filePath)
718 {
719     if (filePath.has_filename())
720     {
721         const auto fileName = filePath.filename().string();
722 
723         size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
724         size_t itemPos = std::strcspn(fileName.c_str(), "_");
725 
726         if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
727         {
728             return std::make_optional(
729                 std::make_tuple(fileName.substr(0, numberPos),
730                                 fileName.substr(numberPos, itemPos - numberPos),
731                                 fileName.substr(itemPos + 1, fileName.size())));
732         }
733     }
734     return std::nullopt;
735 }
736 
handleSpecialModeChange(const std::string & manufacturingModeStatus)737 static void handleSpecialModeChange(const std::string& manufacturingModeStatus)
738 {
739     manufacturingMode = false;
740     if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
741                                    "SpecialMode.Modes.Manufacturing")
742     {
743         manufacturingMode = true;
744     }
745     if (validateUnsecureFeature == 1)
746     {
747         if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
748                                        "SpecialMode.Modes.ValidationUnsecure")
749         {
750             manufacturingMode = true;
751         }
752     }
753 }
754 
setupManufacturingModeMatch(sdbusplus::asio::connection & conn)755 void setupManufacturingModeMatch(sdbusplus::asio::connection& conn)
756 {
757     namespace rules = sdbusplus::bus::match::rules;
758     static constexpr const char* specialModeInterface =
759         "xyz.openbmc_project.Security.SpecialMode";
760 
761     const std::string filterSpecialModeIntfAdd =
762         rules::interfacesAdded() +
763         rules::argNpath(0, "/xyz/openbmc_project/security/special_mode");
764     static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch =
765         std::make_unique<sdbusplus::bus::match_t>(
766             conn, filterSpecialModeIntfAdd, [](sdbusplus::message_t& m) {
767         sdbusplus::message::object_path path;
768         using PropertyMap =
769             boost::container::flat_map<std::string, std::variant<std::string>>;
770         boost::container::flat_map<std::string, PropertyMap> interfaceAdded;
771         m.read(path, interfaceAdded);
772         auto intfItr = interfaceAdded.find(specialModeInterface);
773         if (intfItr == interfaceAdded.end())
774         {
775             return;
776         }
777         PropertyMap& propertyList = intfItr->second;
778         auto itr = propertyList.find("SpecialMode");
779         if (itr == propertyList.end())
780         {
781             std::cerr << "error getting  SpecialMode property "
782                       << "\n";
783             return;
784         }
785         auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second);
786         handleSpecialModeChange(*manufacturingModeStatus);
787     });
788 
789     const std::string filterSpecialModeChange =
790         rules::type::signal() + rules::member("PropertiesChanged") +
791         rules::interface("org.freedesktop.DBus.Properties") +
792         rules::argN(0, specialModeInterface);
793     static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch =
794         std::make_unique<sdbusplus::bus::match_t>(conn, filterSpecialModeChange,
795                                                   [](sdbusplus::message_t& m) {
796         std::string interfaceName;
797         boost::container::flat_map<std::string, std::variant<std::string>>
798             propertiesChanged;
799 
800         m.read(interfaceName, propertiesChanged);
801         auto itr = propertiesChanged.find("SpecialMode");
802         if (itr == propertiesChanged.end())
803         {
804             return;
805         }
806         auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second);
807         handleSpecialModeChange(*manufacturingModeStatus);
808     });
809 
810     conn.async_method_call(
811         [](const boost::system::error_code ec,
812            const std::variant<std::string>& getManufactMode) {
813         if (ec)
814         {
815             std::cerr << "error getting  SpecialMode status " << ec.message()
816                       << "\n";
817             return;
818         }
819         const auto* manufacturingModeStatus =
820             std::get_if<std::string>(&getManufactMode);
821         handleSpecialModeChange(*manufacturingModeStatus);
822     },
823         "xyz.openbmc_project.SpecialMode",
824         "/xyz/openbmc_project/security/special_mode",
825         "org.freedesktop.DBus.Properties", "Get", specialModeInterface,
826         "SpecialMode");
827 }
828 
getManufacturingMode()829 bool getManufacturingMode()
830 {
831     return manufacturingMode;
832 }
833 
834 std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
setupPropertiesChangedMatches(sdbusplus::asio::connection & bus,std::span<const char * const> types,const std::function<void (sdbusplus::message_t &)> & handler)835     setupPropertiesChangedMatches(
836         sdbusplus::asio::connection& bus, std::span<const char* const> types,
837         const std::function<void(sdbusplus::message_t&)>& handler)
838 {
839     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
840     for (const char* type : types)
841     {
842         auto match = std::make_unique<sdbusplus::bus::match_t>(
843             static_cast<sdbusplus::bus_t&>(bus),
844             "type='signal',member='PropertiesChanged',path_namespace='" +
845                 std::string(inventoryPath) + "',arg0namespace='" +
846                 configInterfaceName(type) + "'",
847             handler);
848         matches.emplace_back(std::move(match));
849     }
850     return matches;
851 }
852 
853 std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
setupPropertiesChangedMatches(sdbusplus::asio::connection & bus,const I2CDeviceTypeMap & typeMap,const std::function<void (sdbusplus::message_t &)> & handler)854     setupPropertiesChangedMatches(
855         sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
856         const std::function<void(sdbusplus::message_t&)>& handler)
857 {
858     std::vector<const char*> types;
859     types.reserve(typeMap.size());
860     for (const auto& [type, dt] : typeMap)
861     {
862         types.push_back(type.data());
863     }
864     return setupPropertiesChangedMatches(bus, {types}, handler);
865 }
866