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