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