xref: /openbmc/phosphor-logging/extensions/openpower-pels/json_utils.cpp (revision 075c79237505ea3b810a461f5f514e4d520a0c44)
1  /**
2   * Copyright © 2019 IBM 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  #include "json_utils.hpp"
17  
18  #include "paths.hpp"
19  
20  #include <stdio.h>
21  
22  #include <nlohmann/json.hpp>
23  
24  #include <cstring>
25  #include <filesystem>
26  #include <optional>
27  #include <sstream>
28  #include <string>
29  
30  namespace openpower
31  {
32  namespace pels
33  {
34  
35  std::string escapeJSON(const std::string& input)
36  {
37      std::string output;
38      output.reserve(input.length());
39  
40      for (const auto c : input)
41      {
42          switch (c)
43          {
44              case '"':
45                  output += "\\\"";
46                  break;
47              case '/':
48                  output += "\\/";
49                  break;
50              case '\b':
51                  output += "\\b";
52                  break;
53              case '\f':
54                  output += "\\f";
55                  break;
56              case '\n':
57                  output += "\\n";
58                  break;
59              case '\r':
60                  output += "\\r";
61                  break;
62              case '\t':
63                  output += "\\t";
64                  break;
65              case '\\':
66                  output += "\\\\";
67                  break;
68              default:
69                  output += c;
70                  break;
71          }
72      }
73  
74      return output;
75  }
76  std::unique_ptr<char[]> dumpHex(const void* data, size_t size,
77                                  size_t indentCount, bool toJson)
78  {
79      const int symbolSize = 100;
80      std::string jsonIndent(indentLevel * indentCount, 0x20);
81      if (toJson)
82      {
83          jsonIndent.append("\"");
84      }
85      std::unique_ptr<char[]> buffer{new char[std::max(70, 10 * (int)size)]()};
86      char* symbol = (char*)calloc(symbolSize, sizeof(char));
87      char* byteCount = (char*)calloc(11, sizeof(char));
88      char ascii[17];
89      size_t i, j;
90      ascii[16] = '\0';
91      for (i = 0; i < size; ++i)
92      {
93          if (i % 16 == 0)
94          {
95              if (!toJson)
96              {
97                  snprintf(byteCount, 11, "%08X  ", static_cast<uint32_t>(i));
98                  strcat(buffer.get(), byteCount);
99              }
100              strcat(buffer.get(), jsonIndent.c_str());
101          }
102          snprintf(symbol, symbolSize, "%02X ", ((unsigned char*)data)[i]);
103          strcat(buffer.get(), symbol);
104          memset(symbol, 0, strlen(symbol));
105          if (((unsigned char*)data)[i] >= ' ' &&
106              ((unsigned char*)data)[i] <= '~')
107          {
108              ascii[i % 16] = ((unsigned char*)data)[i];
109          }
110          else
111          {
112              ascii[i % 16] = '.';
113          }
114          if ((i + 1) % 8 == 0 || i + 1 == size)
115          {
116              std::string asciiString(ascii);
117              if (toJson)
118              {
119                  asciiString = escapeJSON(asciiString);
120              }
121              strcat(buffer.get(), " ");
122              if ((i + 1) % 16 == 0)
123              {
124                  if (i + 1 != size && toJson)
125                  {
126                      snprintf(symbol, symbolSize, "|  %s\",\n",
127                               asciiString.c_str());
128                  }
129                  else if (toJson)
130                  {
131                      snprintf(symbol, symbolSize, "|  %s\"\n",
132                               asciiString.c_str());
133                  }
134                  else
135                  {
136                      snprintf(symbol, symbolSize, "|  %s\n",
137                               asciiString.c_str());
138                  }
139                  strcat(buffer.get(), symbol);
140                  memset(symbol, 0, strlen(symbol));
141              }
142              else if (i + 1 == size)
143              {
144                  ascii[(i + 1) % 16] = '\0';
145                  if ((i + 1) % 16 <= 8)
146                  {
147                      strcat(buffer.get(), " ");
148                  }
149                  for (j = (i + 1) % 16; j < 16; ++j)
150                  {
151                      strcat(buffer.get(), "   ");
152                  }
153                  std::string asciiString2(ascii);
154                  if (toJson)
155                  {
156                      asciiString2 = escapeJSON(asciiString2);
157                      snprintf(symbol, symbolSize, "|  %s\"\n",
158                               asciiString2.c_str());
159                  }
160                  else
161                  {
162                      snprintf(symbol, symbolSize, "|  %s\n",
163                               asciiString2.c_str());
164                  }
165  
166                  strcat(buffer.get(), symbol);
167                  memset(symbol, 0, strlen(symbol));
168              }
169          }
170      }
171      free(byteCount);
172      free(symbol);
173      return buffer;
174  }
175  
176  void jsonInsert(std::string& jsonStr, const std::string& fieldName,
177                  const std::string& fieldValue, uint8_t indentCount)
178  {
179      const int8_t spacesToAppend =
180          colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
181      const std::string jsonIndent(indentCount * indentLevel, 0x20);
182      jsonStr.append(jsonIndent + "\"" + fieldName + "\":");
183      if (spacesToAppend >= 0)
184      {
185          jsonStr.append(spacesToAppend, 0x20);
186      }
187      else
188      {
189          jsonStr.append(1, 0x20);
190      }
191      jsonStr.append("\"" + fieldValue + "\",\n");
192  }
193  
194  void jsonInsertArray(std::string& jsonStr, const std::string& fieldName,
195                       const std::vector<std::string>& values,
196                       uint8_t indentCount)
197  {
198      const std::string jsonIndent(indentCount * indentLevel, 0x20);
199      if (!values.empty())
200      {
201          jsonStr.append(jsonIndent + "\"" + fieldName + "\": [\n");
202          for (size_t i = 0; i < values.size(); i++)
203          {
204              jsonStr.append(colAlign, 0x20);
205              if (i == values.size() - 1)
206              {
207                  jsonStr.append("\"" + values[i] + "\"\n");
208              }
209              else
210              {
211                  jsonStr.append("\"" + values[i] + "\",\n");
212              }
213          }
214          jsonStr.append(jsonIndent + "],\n");
215      }
216      else
217      {
218          const int8_t spacesToAppend =
219              colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
220          jsonStr.append(jsonIndent + "\"" + fieldName + "\":");
221          if (spacesToAppend > 0)
222          {
223              jsonStr.append(spacesToAppend, 0x20);
224          }
225          else
226          {
227              jsonStr.append(1, 0x20);
228          }
229          jsonStr.append("[],\n");
230      }
231  }
232  
233  std::string trimEnd(std::string s)
234  {
235      const char* t = " \t\n\r\f\v";
236      if (s.find_last_not_of(t) != std::string::npos)
237      {
238          s.erase(s.find_last_not_of(t) + 1);
239      }
240      return s;
241  }
242  
243  /**
244   * @brief Lookup the component ID in a JSON file named
245   *        after the creator ID.
246   *
247   * Keeps a cache of the JSON it reads to live throughout
248   * the peltool call as the JSON can be reused across
249   * PEL sections or even across PELs.
250   *
251   * @param[in] compID - The component ID
252   * @param[in] creatorID - The creator ID for the PEL
253   * @return optional<string> - The comp name, or std::nullopt
254   */
255  static std::optional<std::string>
256      lookupComponentName(uint16_t compID, char creatorID)
257  {
258      static std::map<char, nlohmann::json> jsonCache;
259      nlohmann::json jsonData;
260      nlohmann::json* jsonPtr = &jsonData;
261      std::filesystem::path filename{
262          std::string{creatorID} + "_component_ids.json"};
263      filename = getPELReadOnlyDataPath() / filename;
264  
265      auto jsonIt = jsonCache.find(creatorID);
266      if (jsonIt != jsonCache.end())
267      {
268          jsonPtr = &(jsonIt->second);
269      }
270      else
271      {
272          std::error_code ec;
273          if (!std::filesystem::exists(filename, ec))
274          {
275              return std::nullopt;
276          }
277  
278          std::ifstream file{filename};
279          if (!file)
280          {
281              return std::nullopt;
282          }
283  
284          jsonData = nlohmann::json::parse(file, nullptr, false);
285          if (jsonData.is_discarded())
286          {
287              return std::nullopt;
288          }
289  
290          jsonCache.emplace(creatorID, jsonData);
291      }
292  
293      auto id = getNumberString("%04X", compID);
294  
295      auto it = jsonPtr->find(id);
296      if (it == jsonPtr->end())
297      {
298          return std::nullopt;
299      }
300  
301      return it->get<std::string>();
302  }
303  
304  /**
305   * @brief Convert the component ID to a 2 character string
306   *        if both bytes are nonzero
307   *
308   * e.g. 0x4552 -> "ER"
309   *
310   * @param[in] compID - The component ID
311   * @return optional<string> - The two character string, or std::nullopt.
312   */
313  static std::optional<std::string> convertCompIDToChars(uint16_t compID)
314  {
315      uint8_t first = (compID >> 8) & 0xFF;
316      uint8_t second = compID & 0xFF;
317      if ((first != 0) && (second != 0))
318      {
319          std::string id{static_cast<char>(first)};
320          id += static_cast<char>(second);
321          return id;
322      }
323  
324      return std::nullopt;
325  }
326  
327  std::string getComponentName(uint16_t compID, uint8_t creatorID)
328  {
329      // See if there's a JSON file with the names
330      auto name = lookupComponentName(compID, creatorID);
331  
332      // If PHYP, convert to ASCII
333      if (!name && ('H' == creatorID))
334      {
335          name = convertCompIDToChars(compID);
336      }
337  
338      if (!name)
339      {
340          name = getNumberString("0x%04X", compID);
341      }
342  
343      return *name;
344  }
345  
346  } // namespace pels
347  } // namespace openpower
348