blob: 478fb7cbce8e1007d8e63b9ecb1808891487c733 [file] [log] [blame]
Patrick Venture298930a2019-07-03 11:44:52 -07001/*
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 "buildjson.hpp"
17
18#include "file_handler.hpp"
Patrick Venturea6b4abd2019-07-19 10:58:55 -070019#include "fs.hpp"
Patrick Venturecf066ac2019-08-06 09:03:47 -070020#include "general_systemd.hpp"
Patrick Venture298930a2019-07-03 11:44:52 -070021
Patrick Venturea6b4abd2019-07-19 10:58:55 -070022#include <algorithm>
Patrick Venture298930a2019-07-03 11:44:52 -070023#include <cstdio>
24#include <exception>
Patrick Venturea6b4abd2019-07-19 10:58:55 -070025#include <fstream>
Patrick Venture298930a2019-07-03 11:44:52 -070026#include <nlohmann/json.hpp>
Patrick Venturea6b4abd2019-07-19 10:58:55 -070027#include <phosphor-logging/log.hpp>
Patrick Venture097435f2019-08-15 07:39:48 -070028#include <regex>
Patrick Venture298930a2019-07-03 11:44:52 -070029#include <sdbusplus/bus.hpp>
30#include <string>
31#include <vector>
32
33namespace ipmi_flash
34{
35
Patrick Venture9cce5a22019-08-06 09:24:46 -070036std::unique_ptr<TriggerableActionInterface>
37 buildFileSystemd(const nlohmann::json& data)
38{
39 /* This type of action requires a path and unit, and optionally a mode. */
40 const auto& path = data.at("path");
41 const auto& unit = data.at("unit");
42
43 /* the mode parameter is optional. */
44 std::string systemdMode = "replace";
45 const auto& mode = data.find("mode");
46 if (mode != data.end())
47 {
48 systemdMode = data.at("mode").get<std::string>();
49 }
50
51 return SystemdWithStatusFile::CreateSystemdWithStatusFile(
52 sdbusplus::bus::new_default(), path, unit, systemdMode);
53}
54
Patrick Venturee0216d22019-08-21 10:17:39 -070055std::unique_ptr<TriggerableActionInterface>
56 buildSystemd(const nlohmann::json& data)
57{
58 /* This type of action requires a unit, and optionally a mode. */
59 const auto& unit = data.at("unit");
60
61 /* the mode parameter is optional. */
62 std::string systemdMode = "replace";
63 const auto& mode = data.find("mode");
64 if (mode != data.end())
65 {
66 systemdMode = data.at("mode").get<std::string>();
67 }
68
69 return SystemdNoFile::CreateSystemdNoFile(sdbusplus::bus::new_default(),
70 unit, systemdMode);
71}
72
Patrick Venture298930a2019-07-03 11:44:52 -070073std::vector<HandlerConfig> buildHandlerFromJson(const nlohmann::json& data)
74{
75 std::vector<HandlerConfig> handlers;
76
77 for (const auto& item : data)
78 {
79 try
80 {
81 HandlerConfig output;
82
83 /* at() throws an exception when the key is not present. */
84 item.at("blob").get_to(output.blobId);
85
Patrick Venture097435f2019-08-15 07:39:48 -070086 /* name must be: /flash/... */
87 if (!std::regex_match(output.blobId, std::regex("^\\/flash\\/.+")))
88 {
89 throw std::runtime_error("Invalid blob name: '" +
90 output.blobId +
91 "' must start with /flash/");
92 }
93
Patrick Venture298930a2019-07-03 11:44:52 -070094 /* handler is required. */
95 const auto& h = item.at("handler");
96 const std::string handlerType = h.at("type");
97 if (handlerType == "file")
98 {
99 const auto& path = h.at("path");
100 output.handler = std::make_unique<FileHandler>(path);
101 }
102 else
103 {
104 throw std::runtime_error("Invalid handler type: " +
105 handlerType);
106 }
107
108 /* actions are required (presently). */
109 const auto& a = item.at("actions");
110 std::unique_ptr<ActionPack> pack = std::make_unique<ActionPack>();
111
112 /* It hasn't been fully determined if any action being optional is
113 * useful, so for now they will be required.
114 * TODO: Evaluate how the behaviors change if some actions are
115 * missing, does the code just assume it was successful? I would
116 * think not.
117 */
118 const auto& prep = a.at("preparation");
119 const std::string prepareType = prep.at("type");
120 if (prepareType == "systemd")
121 {
Patrick Venturee0216d22019-08-21 10:17:39 -0700122 pack->preparation = std::move(buildSystemd(prep));
Patrick Venture298930a2019-07-03 11:44:52 -0700123 }
124 else
125 {
126 throw std::runtime_error("Invalid preparation type: " +
127 prepareType);
128 }
129
130 const auto& verify = a.at("verification");
131 const std::string verifyType = verify.at("type");
132 if (verifyType == "fileSystemdVerify")
133 {
Patrick Venture9cce5a22019-08-06 09:24:46 -0700134 pack->verification = std::move(buildFileSystemd(verify));
Patrick Venture298930a2019-07-03 11:44:52 -0700135 }
William A. Kennington III2d434c82019-11-22 18:42:35 -0800136 else if (verifyType == "systemd")
137 {
138 pack->verification = std::move(buildSystemd(verify));
139 }
Patrick Venture298930a2019-07-03 11:44:52 -0700140 else
141 {
142 throw std::runtime_error("Invalid verification type:" +
143 verifyType);
144 }
145
146 const auto& update = a.at("update");
147 const std::string updateType = update.at("type");
148 if (updateType == "reboot")
149 {
Patrick Venturee0216d22019-08-21 10:17:39 -0700150 pack->update = SystemdNoFile::CreateSystemdNoFile(
Patrick Venture44564902019-08-21 09:46:32 -0700151 sdbusplus::bus::new_default(), "reboot.target",
152 "replace-irreversibly");
Patrick Venture298930a2019-07-03 11:44:52 -0700153 }
Patrick Venturec2baac92019-08-05 13:30:38 -0700154 else if (updateType == "fileSystemdUpdate")
155 {
Patrick Venture9cce5a22019-08-06 09:24:46 -0700156 pack->update = std::move(buildFileSystemd(update));
Patrick Venturec2baac92019-08-05 13:30:38 -0700157 }
Patrick Venture298930a2019-07-03 11:44:52 -0700158 else if (updateType == "systemd")
159 {
Patrick Venturee0216d22019-08-21 10:17:39 -0700160 pack->update = std::move(buildSystemd(update));
Patrick Venture298930a2019-07-03 11:44:52 -0700161 }
162 else
163 {
164 throw std::runtime_error("Invalid update type: " + updateType);
165 }
166
167 output.actions = std::move(pack);
168 handlers.push_back(std::move(output));
169 }
170 catch (const std::exception& e)
171 {
172 /* TODO: Once phosphor-logging supports unit-test injection, fix
173 * this to log.
174 */
175 std::fprintf(stderr,
176 "Excepted building HandlerConfig from json: %s\n",
177 e.what());
178 }
179 }
180
181 return handlers;
182}
183
Patrick Venturea6b4abd2019-07-19 10:58:55 -0700184std::vector<HandlerConfig> BuildHandlerConfigs(const std::string& directory)
185{
186 using namespace phosphor::logging;
187
188 std::vector<HandlerConfig> output;
189
190 std::vector<std::string> jsonPaths = GetJsonList(directory);
191
192 for (const auto& path : jsonPaths)
193 {
194 std::ifstream jsonFile(path);
195 if (!jsonFile.is_open())
196 {
197 log<level::ERR>("Unable to open json file",
198 entry("PATH=%s", path.c_str()));
199 continue;
200 }
201
202 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
203 if (data.is_discarded())
204 {
205 log<level::ERR>("Parsing json failed",
206 entry("PATH=%s", path.c_str()));
207 continue;
208 }
209
210 std::vector<HandlerConfig> configs = buildHandlerFromJson(data);
211 std::move(configs.begin(), configs.end(), std::back_inserter(output));
212 }
213
214 return output;
215}
216
Patrick Venture298930a2019-07-03 11:44:52 -0700217} // namespace ipmi_flash