1 /*
2  * Copyright 2019 Google Inc.
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 "firmware_handlers_builder.hpp"
17 
18 #include "file_handler.hpp"
19 #include "fs.hpp"
20 #include "skip_action.hpp"
21 
22 #include <nlohmann/json.hpp>
23 
24 #include <algorithm>
25 #include <cstdio>
26 #include <exception>
27 #include <fstream>
28 #include <regex>
29 #include <string>
30 #include <vector>
31 
32 namespace ipmi_flash
33 {
34 std::vector<HandlerConfig<ActionPack>>
35     FirmwareHandlersBuilder::buildHandlerFromJson(const nlohmann::json& data)
36 {
37     std::vector<HandlerConfig<ActionPack>> handlers;
38 
39     for (const auto& item : data)
40     {
41         try
42         {
43             HandlerConfig<ActionPack> output;
44 
45             /* at() throws an exception when the key is not present. */
46             item.at("blob").get_to(output.blobId);
47 
48             /* name must be: /flash/... */
49             if (!std::regex_match(output.blobId, std::regex("^\\/flash\\/.+")))
50             {
51                 throw std::runtime_error("Invalid blob name: '" +
52                                          output.blobId +
53                                          "' must start with /flash/");
54             }
55 
56             /* handler is required. */
57             const auto& h = item.at("handler");
58             const std::string handlerType = h.at("type");
59             if (handlerType == "file")
60             {
61                 const auto& path = h.at("path");
62                 output.handler = std::make_unique<FileHandler>(path);
63             }
64             else
65             {
66                 throw std::runtime_error("Invalid handler type: " +
67                                          handlerType);
68             }
69 
70             /* actions are required (presently). */
71             const auto& a = item.at("actions");
72             std::unique_ptr<ActionPack> pack = std::make_unique<ActionPack>();
73 
74             /* to make an action optional, assign type "skip" */
75             const auto& prep = a.at("preparation");
76             const std::string prepareType = prep.at("type");
77             if (prepareType == "systemd")
78             {
79                 pack->preparation = std::move(buildSystemd(prep));
80             }
81             else if (prepareType == "skip")
82             {
83                 pack->preparation = SkipAction::CreateSkipAction();
84             }
85             else
86             {
87                 throw std::runtime_error("Invalid preparation type: " +
88                                          prepareType);
89             }
90 
91             const auto& verify = a.at("verification");
92             const std::string verifyType = verify.at("type");
93             if (verifyType == "fileSystemdVerify")
94             {
95                 pack->verification = std::move(buildFileSystemd(verify));
96             }
97             else if (verifyType == "systemd")
98             {
99                 pack->verification = std::move(buildSystemd(verify));
100             }
101             else if (verifyType == "skip")
102             {
103                 pack->verification = SkipAction::CreateSkipAction();
104             }
105             else
106             {
107                 throw std::runtime_error("Invalid verification type:" +
108                                          verifyType);
109             }
110 
111             const auto& update = a.at("update");
112             const std::string updateType = update.at("type");
113             if (updateType == "reboot")
114             {
115                 pack->update = SystemdNoFile::CreateSystemdNoFile(
116                     sdbusplus::bus::new_default(), "reboot.target",
117                     "replace-irreversibly");
118             }
119             else if (updateType == "fileSystemdUpdate")
120             {
121                 pack->update = std::move(buildFileSystemd(update));
122             }
123             else if (updateType == "systemd")
124             {
125                 pack->update = std::move(buildSystemd(update));
126             }
127             else if (updateType == "skip")
128             {
129                 pack->update = SkipAction::CreateSkipAction();
130             }
131             else
132             {
133                 throw std::runtime_error("Invalid update type: " + updateType);
134             }
135 
136             output.actions = std::move(pack);
137             handlers.push_back(std::move(output));
138         }
139         catch (const std::exception& e)
140         {
141             /* TODO: Once phosphor-logging supports unit-test injection, fix
142              * this to log.
143              */
144             std::fprintf(stderr,
145                          "Excepted building HandlerConfig from json: %s\n",
146                          e.what());
147         }
148     }
149 
150     return handlers;
151 }
152 } // namespace ipmi_flash
153