1 #include <exception>
2 #include <vector>
3 #include <stdlib.h>
4 #include <dlfcn.h>
5 #include <errno.h>
6 #include <stdio.h>
7 #include <systemd/sd-bus.h>
8 #include <unistd.h>
9 #include <host-ipmid/ipmid-api.h>
10 #include <iostream>
11 #include <memory>
12 #include <algorithm>
13 #include <fstream>
14 #include <sstream>
15 #include <mapper.h>
16 #include "frup.h"
17 #include "fru-area.hpp"
18 #include "fru-gen.hpp"
19 #include <sdbusplus/server.hpp>
20 
21 // OpenBMC System Manager dbus framework
22 const char  *sys_object_name   =  "/org/openbmc/managers/System";
23 const char  *sys_intf_name     =  "org.openbmc.managers.System";
24 
25 extern const FruMap frus;
26 
27 // Association between interface and the dbus property
28 using InterfaceList = std::map<std::string,
29                                std::map<std::string, std::string>>;
30 
31 // Association between property and its value
32 using PropertiesList = std::map<std::string, std::string>;
33 
34 
35 //----------------------------------------------------------------
36 // Constructor
37 //----------------------------------------------------------------
38 ipmi_fru::ipmi_fru(const uint8_t fruid, const ipmi_fru_area_type type,
39                    sd_bus *bus_type, bool bmc_fru)
40 {
41     iv_fruid = fruid;
42     iv_type = type;
43     iv_bmc_fru = bmc_fru;
44     iv_bus_type = bus_type;
45     iv_valid = false;
46     iv_data = NULL;
47     iv_present = false;
48 
49     if(iv_type == IPMI_FRU_AREA_INTERNAL_USE)
50     {
51         iv_name = "INTERNAL_";
52     }
53     else if(iv_type == IPMI_FRU_AREA_CHASSIS_INFO)
54     {
55         iv_name = "CHASSIS_";
56     }
57     else if(iv_type == IPMI_FRU_AREA_BOARD_INFO)
58     {
59         iv_name = "BOARD_";
60     }
61     else if(iv_type == IPMI_FRU_AREA_PRODUCT_INFO)
62     {
63         iv_name = "PRODUCT_";
64     }
65     else if(iv_type == IPMI_FRU_AREA_MULTI_RECORD)
66     {
67         iv_name = "MULTI_";
68     }
69     else
70     {
71         iv_name = IPMI_FRU_AREA_TYPE_MAX;
72         fprintf(stderr, "ERROR: Invalid Area type :[%d]\n",iv_type);
73     }
74 }
75 
76 //-----------------------------------------------------
77 // For a FRU area type, accepts the data and updates
78 // area specific data.
79 //-----------------------------------------------------
80 void ipmi_fru::set_data(const uint8_t *data, const size_t len)
81 {
82     iv_len = len;
83     iv_data = new uint8_t[len];
84     memcpy(iv_data, data, len);
85 }
86 
87 //-----------------------------------------------------
88 // Sets the dbus parameters
89 //-----------------------------------------------------
90 void ipmi_fru::update_dbus_paths(const char *bus_name,
91                       const char *obj_path, const char *intf_name)
92 {
93     iv_bus_name = bus_name;
94     iv_obj_path = obj_path;
95     iv_intf_name = intf_name;
96 }
97 
98 //-------------------
99 // Destructor
100 //-------------------
101 ipmi_fru::~ipmi_fru()
102 {
103     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
104     sd_bus_message *response = NULL;
105     int rc = 0;
106 
107     if(iv_data != NULL)
108     {
109         delete [] iv_data;
110         iv_data = NULL;
111     }
112 
113     // If we have not been successful in doing some updates and we are a BMC
114     // fru, then need to set the fault bits.
115     bool valid_dbus = !(iv_bus_name.empty()) &&
116                       !(iv_obj_path.empty()) &&
117                       !(iv_intf_name.empty());
118 
119     // Based on bmc_fru, success in updating the FRU inventory we need to set
120     // some special bits.
121     if(iv_bmc_fru && valid_dbus)
122     {
123         // Set the Fault bit if we did not successfully process the fru
124         const char *fault_bit = iv_valid ? "False" : "True";
125 
126         rc = sd_bus_call_method(iv_bus_type,                // On the System Bus
127                                 iv_bus_name.c_str(),        // Service to contact
128                                 iv_obj_path.c_str(),        // Object path
129                                 iv_intf_name.c_str(),       // Interface name
130                                 "setFault",                 // Method to be called
131                                 &bus_error,                 // object to return error
132                                 &response,                  // Response message on success
133                                 "s",                        // input message (string)
134                                 fault_bit);                 // First argument to setFault
135 
136         if(rc <0)
137         {
138             fprintf(stderr,"Failed to set Fault bit, value:[%s] for fruid:[%d], path:[%s]\n",
139                     fault_bit, iv_fruid, iv_obj_path.c_str());
140         }
141         else
142         {
143             printf("Fault bit set to :[%s] for fruid:[%d], Path:[%s]\n",
144                     fault_bit, iv_fruid,iv_obj_path.c_str());
145         }
146 
147         sd_bus_error_free(&bus_error);
148         sd_bus_message_unref(response);
149 
150         // Set the Present bits
151         const char *present_bit = iv_present ? "True" : "False";
152 
153         rc = sd_bus_call_method(iv_bus_type,                // On the System Bus
154                                 iv_bus_name.c_str(),        // Service to contact
155                                 iv_obj_path.c_str(),        // Object path
156                                 iv_intf_name.c_str(),       // Interface name
157                                 "setPresent",               // Method to be called
158                                 &bus_error,                 // object to return error
159                                 &response,                  // Response message on success
160                                 "s",                        // input message (string)
161                                 present_bit);               // First argument to setPresent
162         if(rc < 0)
163         {
164             fprintf(stderr,"Failed to set Present bit for fruid:[%d], path:[%s]\n",
165                     iv_fruid, iv_obj_path.c_str());
166         }
167         else
168         {
169             printf("Present bit set to :[%s] for fruid:[%d], Path[%s]:\n",
170                     present_bit, iv_fruid, iv_obj_path.c_str());
171         }
172 
173         sd_bus_error_free(&bus_error);
174         sd_bus_message_unref(response);
175     }
176 }
177 
178 // Sets up the sd_bus structures for the given fru type
179 int ipmi_fru::setup_sd_bus_paths(void)
180 {
181     // Need this to get respective DBUS objects
182     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
183     sd_bus_message *response = NULL;
184     int rc = 0;
185 
186     // What we need is BOARD_1, PRODUCT_1, CHASSIS_1 etc..
187     char *inv_bus_name = NULL;
188     const char *inv_obj_path = NULL,
189                *inv_intf_name = NULL;
190     char fru_area_name[16] = {0};
191     char *sys_bus_name = NULL;
192     sprintf(fru_area_name,"%s%d",iv_name.c_str(), iv_fruid);
193 
194 #ifdef __IPMI_DEBUG__
195     printf("Getting sd_bus for :[%s]\n",fru_area_name);
196 #endif
197 
198     rc = mapper_get_service(iv_bus_type, sys_object_name, &sys_bus_name);
199     if(rc < 0)
200     {
201         fprintf(stderr, "Failed to get system manager service:[%s]\n",
202                 strerror(-rc));
203         goto exit;
204     }
205 
206     // We want to call a method "getObjectFromId" on System Bus that is
207     // made available over  OpenBmc system services.
208 
209     rc = sd_bus_call_method(iv_bus_type,                // On the System Bus
210                             sys_bus_name,               // Service to contact
211                             sys_object_name,            // Object path
212                             sys_intf_name,              // Interface name
213                             "getObjectFromId",          // Method to be called
214                             &bus_error,                 // object to return error
215                             &response,                  // Response message on success
216                             "ss",                       // input message (string,string)
217                             "FRU_STR",                  // First argument to getObjectFromId
218                             fru_area_name);             // Second Argument
219     if(rc < 0)
220     {
221         fprintf(stderr, "Failed to resolve fruid:[%d] to dbus: [%s]\n", iv_fruid, bus_error.message);
222     }
223     else
224     {
225         // Method getObjectFromId returns 2 parameters and all are strings, namely
226         // object_path and interface name for accessing that particular
227         // FRU over Inventory SDBUS manager. 'ss' here mentions that format.
228         rc = sd_bus_message_read(response, "(ss)", &inv_obj_path, &inv_intf_name);
229         if(rc < 0)
230         {
231             fprintf(stderr, "Failed to parse response message:[%s]\n", strerror(-rc));
232         }
233         else
234         {
235             rc = mapper_get_service(iv_bus_type, inv_obj_path, &inv_bus_name);
236             if(rc < 0)
237             {
238                 fprintf(stderr, "Failed to get inventory item service:[%s]\n",
239                         strerror(-rc));
240                 goto exit;
241             }
242             // Update the paths in the area object
243             update_dbus_paths(inv_bus_name, inv_obj_path, inv_intf_name);
244         }
245     }
246 
247 exit:
248 #ifdef __IPMI_DEBUG__
249             printf("fru_area=[%s], inv_bus_name=[%s], inv_obj_path=[%s], inv_intf_name=[%s]\n",
250                     fru_area_name, inv_bus_name, inv_obj_path, inv_intf_name);
251 #endif
252 
253     free(sys_bus_name);
254     free(inv_bus_name);
255     sd_bus_error_free(&bus_error);
256     sd_bus_message_unref(response);
257 
258     return rc;
259 }
260 
261 //------------------------------------------------
262 // Takes the pointer to stream of bytes and length
263 // and returns the 8 bit checksum
264 // This algo is per IPMI V2.0 spec
265 //-------------------------------------------------
266 unsigned char calculate_crc(const unsigned char *data, size_t len)
267 {
268     char crc = 0;
269     size_t byte = 0;
270 
271     for(byte = 0; byte < len; byte++)
272     {
273         crc += *data++;
274     }
275 
276     return(-crc);
277 }
278 
279 //---------------------------------------------------------------------
280 // Accepts a fru area offset in commom hdr and tells which area it is.
281 //---------------------------------------------------------------------
282 ipmi_fru_area_type get_fru_area_type(uint8_t area_offset)
283 {
284     ipmi_fru_area_type type = IPMI_FRU_AREA_TYPE_MAX;
285 
286     switch(area_offset)
287     {
288         case IPMI_FRU_INTERNAL_OFFSET:
289             type = IPMI_FRU_AREA_INTERNAL_USE;
290             break;
291 
292         case IPMI_FRU_CHASSIS_OFFSET:
293             type = IPMI_FRU_AREA_CHASSIS_INFO;
294             break;
295 
296         case IPMI_FRU_BOARD_OFFSET:
297             type = IPMI_FRU_AREA_BOARD_INFO;
298             break;
299 
300         case IPMI_FRU_PRODUCT_OFFSET:
301             type = IPMI_FRU_AREA_PRODUCT_INFO;
302             break;
303 
304         case IPMI_FRU_MULTI_OFFSET:
305             type = IPMI_FRU_AREA_MULTI_RECORD;
306             break;
307 
308         default:
309             type = IPMI_FRU_AREA_TYPE_MAX;
310     }
311 
312     return type;
313 }
314 
315 ///-----------------------------------------------
316 // Validates the data for crc and mandatory fields
317 ///-----------------------------------------------
318 int verify_fru_data(const uint8_t *data, const size_t len)
319 {
320     uint8_t checksum = 0;
321     int rc = -1;
322 
323     // Validate for first byte to always have a value of [1]
324     if(data[0] != IPMI_FRU_HDR_BYTE_ZERO)
325     {
326         fprintf(stderr, "Invalid entry:[%d] in byte-0\n",data[0]);
327         return rc;
328     }
329 #ifdef __IPMI_DEBUG__
330     else
331     {
332         printf("SUCCESS: Validated [0x%X] in entry_1 of fru_data\n",data[0]);
333     }
334 #endif
335 
336     // See if the calculated CRC matches with the embedded one.
337     // CRC to be calculated on all except the last one that is CRC itself.
338     checksum = calculate_crc(data, len - 1);
339     if(checksum != data[len-1])
340     {
341 #ifdef __IPMI_DEBUG__
342         fprintf(stderr, "Checksum mismatch."
343                 " Calculated:[0x%X], Embedded:[0x%X]\n",
344                 checksum, data[len]);
345 #endif
346         return rc;
347     }
348 #ifdef __IPMI_DEBUG__
349     else
350     {
351         printf("SUCCESS: Checksum matches:[0x%X]\n",checksum);
352     }
353 #endif
354 
355     return EXIT_SUCCESS;
356 }
357 
358 //------------------------------------------------------------------------
359 // Gets the value of the key from the fru dictionary of the given section.
360 // FRU dictionary is parsed fru data for all the sections.
361 //------------------------------------------------------------------------
362 
363 std::string getFRUValue(const std::string& section,
364                         const std::string& key,
365                         IPMIFruInfo& fruData)
366 {
367 
368     auto minIndexValue = 0;
369     auto maxIndexValue = 0;
370     std::string fruValue = "";
371     if (section == "Board")
372     {
373         minIndexValue = OPENBMC_VPD_KEY_BOARD_MFG_DATE;
374         maxIndexValue = OPENBMC_VPD_KEY_BOARD_MAX;
375     }
376     else if (section == "Product")
377     {
378         minIndexValue = OPENBMC_VPD_KEY_PRODUCT_MFR;
379         maxIndexValue = OPENBMC_VPD_KEY_PRODUCT_MAX;
380 
381     }
382     else if (section == "Chassis")
383     {
384         minIndexValue = OPENBMC_VPD_KEY_CHASSIS_TYPE;
385         maxIndexValue = OPENBMC_VPD_KEY_CHASSIS_MAX;
386     }
387 
388     auto first = fruData.cbegin() + minIndexValue;
389     auto last = first + (maxIndexValue - minIndexValue) + 1;
390 
391     auto itr = std::find_if(first, last,
392             [&key](auto& e){ return key == e.first; });
393 
394     if (itr != last)
395     {
396         fruValue = itr->second;
397     }
398     return fruValue;
399 
400 }
401 //Get the inventory service from the mapper.
402 auto getService(sdbusplus::bus::bus& bus,
403                          const std::string& intf,
404                          const std::string& path)
405 {
406     auto mapperCall = bus.new_method_call(
407             "xyz.openbmc_project.ObjectMapper",
408             "/xyz/openbmc_project/ObjectMapper",
409             "xyz.openbmc_project.ObjectMapper",
410             "GetObject");
411 
412     mapperCall.append(path);
413     mapperCall.append(std::vector<std::string>({intf}));
414 
415     auto mapperResponseMsg = bus.call(mapperCall);
416     if (mapperResponseMsg.is_method_error())
417     {
418         throw std::runtime_error("ERROR in mapper call");
419     }
420 
421     std::map<std::string, std::vector<std::string>> mapperResponse;
422     mapperResponseMsg.read(mapperResponse);
423 
424     if (mapperResponse.begin() == mapperResponse.end())
425     {
426         throw std::runtime_error("ERROR in reading the mapper response");
427     }
428 
429     return mapperResponse.begin()->first;
430 }
431 
432 //------------------------------------------------------------------------
433 // Takes FRU data, invokes Parser for each fru record area and updates
434 // Inventory
435 //------------------------------------------------------------------------
436 int ipmi_update_inventory(fru_area_vec_t& area_vec)
437 {
438     // Generic error reporter
439     int rc = 0;
440     uint8_t fruid = 0;
441     IPMIFruInfo fruData;
442 
443     // For each FRU area, extract the needed data , get it parsed and update
444     // the Inventory.
445     for (const auto& fruArea : area_vec)
446     {
447         fruid = fruArea->get_fruid();
448         // Fill the container with information
449         rc = parse_fru_area((fruArea)->get_type(), (void*)(fruArea)->get_data(),
450                             (fruArea)->get_len(), fruData);
451         if (rc < 0)
452         {
453             std::cerr << "ERROR parsing FRU records\n";
454             return rc;
455         }
456     } // END walking the vector of areas and updating
457 
458     // For each Fru we have the list of instances which needs to be updated.
459     // Each instance object implements certain interfaces.
460     // Each Interface is having Dbus properties.
461     // Each Dbus Property would be having metaData(eg section,VpdPropertyName).
462 
463     // Here we are just printing the object,interface and the properties.
464     // which needs to be called with the new inventory manager implementation.
465     auto bus = sdbusplus::bus::new_default();
466     using namespace std::string_literals;
467     static const auto intf = "xyz.openbmc_project.Inventory.Manager"s;
468     static const auto path = "/xyz/openbmc_project/Inventory"s;
469     std::string service;
470     try
471     {
472         service = getService(bus,intf,path);
473     }
474     catch (const std::runtime_error& e)
475     {
476         std::cerr << e.what() << "\n";
477         return -1;
478     }
479     auto notify = [&]()
480     {
481         return bus.new_method_call(
482                 service.c_str(),
483                 path.c_str(),
484                 intf.c_str(),
485                 "Notify");
486     };
487 
488     auto iter = frus.find(fruid);
489     if (iter == frus.end())
490     {
491         std::cerr << "ERROR Unable to get the fru info for FRU=" << fruid << "\n";
492         return -1;
493     }
494 
495     auto& instanceList = iter->second;
496     if (instanceList.size() <= 0)
497     {
498         std::cout << "Object List empty for this FRU=" << fruid << "\n";
499     }
500     for (auto& instance : instanceList)
501     {
502         InterfaceList interfaces;
503 
504         for (auto& interfaceList : instance.second)
505         {
506             PropertiesList prop;//store all the properties
507             for (auto& properties : interfaceList.second)
508             {
509                 std::string section, property, value;
510                 for (auto& info : properties.second)
511                 {
512                     if (info.first == "IPMIFruSection")
513                     {
514                         section = std::move(info.second);
515                     }
516                     if (info.first == "IPMIFruProperty")
517                     {
518                         property = std::move(info.second);
519                     }
520                 }
521 
522                 if (!section.empty() && !property.empty())
523                 {
524                     value = getFRUValue(section, property, fruData);
525                 }
526                 prop.emplace(std::move(properties.first), std::move(value));
527             }
528             interfaces.emplace(std::move(interfaceList.first), std::move(prop));
529         }
530         //Call the inventory manager
531         sdbusplus::message::object_path relPath = instance.first;
532 
533         auto m = notify();
534         m.append(relPath);
535         m.append(interfaces);
536         auto inventoryMgrResponseMsg = bus.call(m);
537         if (inventoryMgrResponseMsg.is_method_error())
538         {
539             std::cerr << "Error in notify call\n";
540             return -1;
541         }
542     }
543     return rc;
544 }
545 
546 ///----------------------------------------------------
547 // Checks if a particular fru area is populated or not
548 ///----------------------------------------------------
549 bool remove_invalid_area(const std::unique_ptr<ipmi_fru> &fru_area)
550 {
551     // Filter the ones that do not have dbus reference.
552     if((strlen((fru_area)->get_bus_name()) == 0) ||
553        (strlen((fru_area)->get_obj_path()) == 0)  ||
554        (strlen((fru_area)->get_intf_name()) == 0))
555     {
556         return true;
557     }
558     return false;
559 }
560 
561 ///----------------------------------------------------------------------------------
562 // Populates various FRU areas
563 // @prereq : This must be called only after validating common header.
564 ///----------------------------------------------------------------------------------
565 int ipmi_populate_fru_areas(uint8_t *fru_data, const size_t data_len,
566                             fru_area_vec_t & fru_area_vec)
567 {
568     size_t area_offset = 0;
569     int rc = -1;
570 
571     // Now walk the common header and see if the file size has atleast the last
572     // offset mentioned by the common_hdr. If the file size is less than the
573     // offset of any if the fru areas mentioned in the common header, then we do
574     // not have a complete file.
575     for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
576             fru_entry < (sizeof(struct common_header) -2); fru_entry++)
577     {
578         rc = -1;
579         // Actual offset in the payload is the offset mentioned in common header
580         // multipled by 8. Common header is always the first 8 bytes.
581         area_offset = fru_data[fru_entry] * IPMI_EIGHT_BYTES;
582         if(area_offset && (data_len < (area_offset + 2)))
583         {
584             // Our file size is less than what it needs to be. +2 because we are
585             // using area len that is at 2 byte off area_offset
586             fprintf(stderr, "fru file is incomplete. Size:[%zd]\n",data_len);
587             return rc;
588         }
589         else if(area_offset)
590         {
591             // Read 2 bytes to know the actual size of area.
592             uint8_t area_hdr[2] = {0};
593             memcpy(area_hdr, &((uint8_t *)fru_data)[area_offset], sizeof(area_hdr));
594 
595             // Size of this area will be the 2nd byte in the fru area header.
596             size_t  area_len = area_hdr[1] * IPMI_EIGHT_BYTES;
597             uint8_t area_data[area_len] = {0};
598 
599             printf("fru data size:[%zd], area offset:[%zd], area_size:[%zd]\n",
600                     data_len, area_offset, area_len);
601 
602             // See if we really have that much buffer. We have area offset amd
603             // from there, the actual len.
604             if(data_len < (area_len + area_offset))
605             {
606                 fprintf(stderr, "Incomplete Fru file.. Size:[%zd]\n",data_len);
607                 return rc;
608             }
609 
610             // Save off the data.
611             memcpy(area_data, &((uint8_t *)fru_data)[area_offset], area_len);
612 
613             // Validate the crc
614             rc = verify_fru_data(area_data, area_len);
615             if(rc < 0)
616             {
617                 fprintf(stderr, "Error validating fru area. offset:[%zd]\n",area_offset);
618                 return rc;
619             }
620             else
621             {
622                 printf("Successfully verified area checksum. offset:[%zd]\n",area_offset);
623             }
624 
625             // We already have a vector that is passed to us containing all
626             // of the fields populated. Update the data portion now.
627             for(auto& iter : fru_area_vec)
628             {
629                 if((iter)->get_type() == get_fru_area_type(fru_entry))
630                 {
631                     (iter)->set_data(area_data, area_len);
632                 }
633             }
634         } // If we have fru data present
635     } // Walk common_hdr
636 
637     // Not all the fields will be populated in a fru data. Mostly all cases will
638     // not have more than 2 or 3.
639     fru_area_vec.erase(std::remove_if(fru_area_vec.begin(), fru_area_vec.end(),
640                        remove_invalid_area), fru_area_vec.end());
641 
642     return EXIT_SUCCESS;
643 }
644 
645 ///---------------------------------------------------------
646 // Validates the fru data per ipmi common header constructs.
647 // Returns with updated common_hdr and also file_size
648 //----------------------------------------------------------
649 int ipmi_validate_common_hdr(const uint8_t *fru_data, const size_t data_len)
650 {
651     int rc = -1;
652 
653     uint8_t common_hdr[sizeof(struct common_header)] = {0};
654     if(data_len >= sizeof(common_hdr))
655     {
656         memcpy(common_hdr, fru_data, sizeof(common_hdr));
657     }
658     else
659     {
660         fprintf(stderr, "Incomplete fru data file. Size:[%zd]\n", data_len);
661         return rc;
662     }
663 
664     // Verify the crc and size
665     rc = verify_fru_data(common_hdr, sizeof(common_hdr));
666     if(rc < 0)
667     {
668         fprintf(stderr, "Failed to validate common header\n");
669         return rc;
670     }
671 
672     return EXIT_SUCCESS;
673 }
674 
675 //------------------------------------------------------------
676 // Cleanup routine
677 //------------------------------------------------------------
678 int cleanup_error(FILE *fru_fp, fru_area_vec_t & fru_area_vec)
679 {
680     if(fru_fp != NULL)
681     {
682         fclose(fru_fp);
683         fru_fp = NULL;
684     }
685 
686     if(!(fru_area_vec.empty()))
687     {
688         fru_area_vec.clear();
689     }
690 
691     return  -1;
692 }
693 
694 
695 ///-----------------------------------------------------
696 // Get the fru area names defined in BMC for a given @fruid.
697 //----------------------------------------------------
698 int get_defined_fru_area(sd_bus *bus_type, const uint8_t fruid,
699                          std::vector<std::string> &defined_fru_area)
700 {
701     // Need this to get respective DBUS objects
702     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
703     sd_bus_message *response = NULL;
704     int rc = 0;
705     const char *areas = NULL;
706     char *sys_bus_name = NULL;
707 
708 #ifdef __IPMI_DEBUG__
709     printf("Getting fru areas defined in Skeleton for :[%d]\n", fruid);
710 #endif
711 
712     rc = mapper_get_service(bus_type, sys_object_name, &sys_bus_name);
713     if(rc < 0)
714     {
715         fprintf(stderr, "Failed to get system manager service:[%s]\n",
716                 strerror(-rc));
717         goto exit;
718     }
719 
720     // We want to call a method "getFRUArea" on System Bus that is
721     // made available over OpenBmc system services.
722     rc = sd_bus_call_method(bus_type,                   // On the System Bus
723                             sys_bus_name,               // Service to contact
724                             sys_object_name,            // Object path
725                             sys_intf_name,              // Interface name
726                             "getFRUArea",               // Method to be called
727                             &bus_error,                 // object to return error
728                             &response,                  // Response message on success
729                             "y",                        // input message (integer)
730                             fruid);                     // Argument
731 
732     if(rc < 0)
733     {
734         fprintf(stderr, "Failed to get fru area for fruid:[%d] to dbus: [%s]\n",
735                     fruid, bus_error.message);
736     }
737     else
738     {
739         // if several fru area names are defined, the names are combined to
740         // a string seperated by ','
741         rc = sd_bus_message_read(response, "s", &areas);
742         if(rc < 0)
743         {
744             fprintf(stderr, "Failed to parse response message from getFRUArea:[%s]\n",
745                         strerror(-rc));
746         }
747         else
748         {
749 #ifdef __IPMI_DEBUG__
750             printf("get defined fru area: id: %d, areas: %s\n", fruid, areas);
751 #endif
752             std::string area_name;
753             std::stringstream ss(areas);
754             // fru area names string is seperated by ',', parse it into tokens
755             while (std::getline(ss, area_name, ','))
756             {
757                 if (!area_name.empty())
758                     defined_fru_area.emplace_back(area_name);
759             }
760         }
761     }
762 
763 exit:
764 
765     free(sys_bus_name);
766     sd_bus_error_free(&bus_error);
767     sd_bus_message_unref(response);
768 
769     return rc;
770 }
771 
772 
773 ///-----------------------------------------------------
774 // Accepts the filename and validates per IPMI FRU spec
775 //----------------------------------------------------
776 int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name,
777                            sd_bus *bus_type, const bool bmc_fru)
778 {
779     size_t data_len = 0;
780     size_t bytes_read = 0;
781     int rc = -1;
782 
783     // Vector that holds individual IPMI FRU AREAs. Although MULTI and INTERNAL
784     // are not used, keeping it here for completeness.
785     fru_area_vec_t fru_area_vec;
786     std::vector<std::string> defined_fru_area;
787 
788     // BMC defines fru areas that should be present in Skeleton
789     rc = get_defined_fru_area(bus_type, fruid, defined_fru_area);
790     if(rc < 0)
791     {
792         fprintf(stderr, "ERROR: cannot get defined fru area\n");
793         return rc;
794     }
795     for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
796         fru_entry < (sizeof(struct common_header) -2); fru_entry++)
797     {
798         // Create an object and push onto a vector.
799         std::unique_ptr<ipmi_fru> fru_area = std::make_unique<ipmi_fru>
800                          (fruid, get_fru_area_type(fru_entry), bus_type, bmc_fru);
801 
802         // Physically being present
803         bool present = access(fru_file_name, F_OK) == 0;
804         fru_area->set_present(present);
805 
806         // Only setup dbus path for areas defined in BMC.
807         // Otherwise Skeleton will report 'not found' error
808         std::string fru_area_name = fru_area->get_name() + std::to_string(fruid);
809         auto iter = std::find(defined_fru_area.begin(), defined_fru_area.end(),
810                                   fru_area_name);
811         if (iter != defined_fru_area.end())
812         {
813             fru_area->setup_sd_bus_paths();
814         }
815         fru_area_vec.emplace_back(std::move(fru_area));
816     }
817 
818     FILE *fru_fp = fopen(fru_file_name,"rb");
819     if(fru_fp == NULL)
820     {
821         fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
822         perror("Error:");
823         return cleanup_error(fru_fp, fru_area_vec);
824     }
825 
826     // Get the size of the file to see if it meets minimum requirement
827     if(fseek(fru_fp, 0, SEEK_END))
828     {
829         perror("Error:");
830         return cleanup_error(fru_fp, fru_area_vec);
831     }
832 
833     // Allocate a buffer to hold entire file content
834     data_len = ftell(fru_fp);
835     uint8_t fru_data[data_len] = {0};
836 
837     rewind(fru_fp);
838     bytes_read = fread(fru_data, data_len, 1, fru_fp);
839     if(bytes_read != 1)
840     {
841         fprintf(stderr, "Failed reading fru data. Bytes_read=[%zd]\n",bytes_read);
842         perror("Error:");
843         return cleanup_error(fru_fp, fru_area_vec);
844     }
845 
846     // We are done reading.
847     fclose(fru_fp);
848     fru_fp = NULL;
849 
850     rc = ipmi_validate_common_hdr(fru_data, data_len);
851     if(rc < 0)
852     {
853         return cleanup_error(fru_fp, fru_area_vec);
854     }
855 
856     // Now that we validated the common header, populate various fru sections if we have them here.
857     rc = ipmi_populate_fru_areas(fru_data, data_len, fru_area_vec);
858     if(rc < 0)
859     {
860         fprintf(stderr,"Populating FRU areas failed for:[%d]\n",fruid);
861         return cleanup_error(fru_fp, fru_area_vec);
862     }
863     else
864     {
865         printf("SUCCESS: Populated FRU areas for:[%s]\n",fru_file_name);
866     }
867 
868 #ifdef __IPMI_DEBUG__
869     for(auto& iter : fru_area_vec)
870     {
871         printf("FRU ID : [%d]\n",(iter)->get_fruid());
872         printf("AREA NAME : [%s]\n",(iter)->get_name());
873         printf("TYPE : [%d]\n",(iter)->get_type());
874         printf("LEN : [%d]\n",(iter)->get_len());
875         printf("BUS NAME : [%s]\n", (iter)->get_bus_name());
876         printf("OBJ PATH : [%s]\n", (iter)->get_obj_path());
877         printf("INTF NAME :[%s]\n", (iter)->get_intf_name());
878     }
879 #endif
880 
881     // If the vector is populated with everything, then go ahead and update the
882     // inventory.
883     if(!(fru_area_vec.empty()))
884     {
885 
886 #ifdef __IPMI_DEBUG__
887         printf("\n SIZE of vector is : [%d] \n",fru_area_vec.size());
888 #endif
889         rc = ipmi_update_inventory(fru_area_vec);
890         if(rc <0)
891         {
892             fprintf(stderr, "Error updating inventory\n");
893         }
894     }
895 
896     // we are done with all that we wanted to do. This will do the job of
897     // calling any destructors too.
898     fru_area_vec.clear();
899 
900     return rc;
901 }
902