blob: 19a08a1382fad2844c72b725b9ab3951be1b8f2c [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#include "prepare_systemd.hpp"
22#include "update_systemd.hpp"
Patrick Venture298930a2019-07-03 11:44:52 -070023
Patrick Venturea6b4abd2019-07-19 10:58:55 -070024#include <algorithm>
Patrick Venture298930a2019-07-03 11:44:52 -070025#include <cstdio>
26#include <exception>
Patrick Venturea6b4abd2019-07-19 10:58:55 -070027#include <fstream>
Patrick Venture298930a2019-07-03 11:44:52 -070028#include <nlohmann/json.hpp>
Patrick Venturea6b4abd2019-07-19 10:58:55 -070029#include <phosphor-logging/log.hpp>
Patrick Venture097435f2019-08-15 07:39:48 -070030#include <regex>
Patrick Venture298930a2019-07-03 11:44:52 -070031#include <sdbusplus/bus.hpp>
32#include <string>
33#include <vector>
34
35namespace ipmi_flash
36{
37
Patrick Venture9cce5a22019-08-06 09:24:46 -070038std::unique_ptr<TriggerableActionInterface>
39 buildFileSystemd(const nlohmann::json& data)
40{
41 /* This type of action requires a path and unit, and optionally a mode. */
42 const auto& path = data.at("path");
43 const auto& unit = data.at("unit");
44
45 /* the mode parameter is optional. */
46 std::string systemdMode = "replace";
47 const auto& mode = data.find("mode");
48 if (mode != data.end())
49 {
50 systemdMode = data.at("mode").get<std::string>();
51 }
52
53 return SystemdWithStatusFile::CreateSystemdWithStatusFile(
54 sdbusplus::bus::new_default(), path, unit, systemdMode);
55}
56
Patrick Venture298930a2019-07-03 11:44:52 -070057std::vector<HandlerConfig> buildHandlerFromJson(const nlohmann::json& data)
58{
59 std::vector<HandlerConfig> handlers;
60
61 for (const auto& item : data)
62 {
63 try
64 {
65 HandlerConfig output;
66
67 /* at() throws an exception when the key is not present. */
68 item.at("blob").get_to(output.blobId);
69
Patrick Venture097435f2019-08-15 07:39:48 -070070 /* name must be: /flash/... */
71 if (!std::regex_match(output.blobId, std::regex("^\\/flash\\/.+")))
72 {
73 throw std::runtime_error("Invalid blob name: '" +
74 output.blobId +
75 "' must start with /flash/");
76 }
77
Patrick Venture298930a2019-07-03 11:44:52 -070078 /* handler is required. */
79 const auto& h = item.at("handler");
80 const std::string handlerType = h.at("type");
81 if (handlerType == "file")
82 {
83 const auto& path = h.at("path");
84 output.handler = std::make_unique<FileHandler>(path);
85 }
86 else
87 {
88 throw std::runtime_error("Invalid handler type: " +
89 handlerType);
90 }
91
92 /* actions are required (presently). */
93 const auto& a = item.at("actions");
94 std::unique_ptr<ActionPack> pack = std::make_unique<ActionPack>();
95
96 /* It hasn't been fully determined if any action being optional is
97 * useful, so for now they will be required.
98 * TODO: Evaluate how the behaviors change if some actions are
99 * missing, does the code just assume it was successful? I would
100 * think not.
101 */
102 const auto& prep = a.at("preparation");
103 const std::string prepareType = prep.at("type");
104 if (prepareType == "systemd")
105 {
106 const auto& unit = prep.at("unit");
107 pack->preparation = SystemdPreparation::CreatePreparation(
108 sdbusplus::bus::new_default(), unit);
109 }
110 else
111 {
112 throw std::runtime_error("Invalid preparation type: " +
113 prepareType);
114 }
115
116 const auto& verify = a.at("verification");
117 const std::string verifyType = verify.at("type");
118 if (verifyType == "fileSystemdVerify")
119 {
Patrick Venture9cce5a22019-08-06 09:24:46 -0700120 pack->verification = std::move(buildFileSystemd(verify));
Patrick Venture298930a2019-07-03 11:44:52 -0700121 }
122 else
123 {
124 throw std::runtime_error("Invalid verification type:" +
125 verifyType);
126 }
127
128 const auto& update = a.at("update");
129 const std::string updateType = update.at("type");
130 if (updateType == "reboot")
131 {
132 static constexpr auto rebootTarget = "reboot.target";
133 static constexpr auto rebootMode = "replace-irreversibly";
134 pack->update = SystemdUpdateMechanism::CreateSystemdUpdate(
135 sdbusplus::bus::new_default(), rebootTarget, rebootMode);
136 }
Patrick Venturec2baac92019-08-05 13:30:38 -0700137 else if (updateType == "fileSystemdUpdate")
138 {
Patrick Venture9cce5a22019-08-06 09:24:46 -0700139 pack->update = std::move(buildFileSystemd(update));
Patrick Venturec2baac92019-08-05 13:30:38 -0700140 }
Patrick Venture298930a2019-07-03 11:44:52 -0700141 else if (updateType == "systemd")
142 {
143 const auto& unit = update.at("unit");
Patrick Venture0caec992019-08-05 09:58:27 -0700144
145 /* the mode parameter is optional. */
146 std::string systemdMode = "replace";
147 const auto& mode = update.find("mode");
148 if (mode != update.end())
149 {
150 systemdMode = update.at("mode").get<std::string>();
151 }
152
Patrick Venture298930a2019-07-03 11:44:52 -0700153 pack->update = SystemdUpdateMechanism::CreateSystemdUpdate(
Patrick Venture0caec992019-08-05 09:58:27 -0700154 sdbusplus::bus::new_default(), unit, systemdMode);
Patrick Venture298930a2019-07-03 11:44:52 -0700155 }
156 else
157 {
158 throw std::runtime_error("Invalid update type: " + updateType);
159 }
160
161 output.actions = std::move(pack);
162 handlers.push_back(std::move(output));
163 }
164 catch (const std::exception& e)
165 {
166 /* TODO: Once phosphor-logging supports unit-test injection, fix
167 * this to log.
168 */
169 std::fprintf(stderr,
170 "Excepted building HandlerConfig from json: %s\n",
171 e.what());
172 }
173 }
174
175 return handlers;
176}
177
Patrick Venturea6b4abd2019-07-19 10:58:55 -0700178std::vector<HandlerConfig> BuildHandlerConfigs(const std::string& directory)
179{
180 using namespace phosphor::logging;
181
182 std::vector<HandlerConfig> output;
183
184 std::vector<std::string> jsonPaths = GetJsonList(directory);
185
186 for (const auto& path : jsonPaths)
187 {
188 std::ifstream jsonFile(path);
189 if (!jsonFile.is_open())
190 {
191 log<level::ERR>("Unable to open json file",
192 entry("PATH=%s", path.c_str()));
193 continue;
194 }
195
196 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
197 if (data.is_discarded())
198 {
199 log<level::ERR>("Parsing json failed",
200 entry("PATH=%s", path.c_str()));
201 continue;
202 }
203
204 std::vector<HandlerConfig> configs = buildHandlerFromJson(data);
205 std::move(configs.begin(), configs.end(), std::back_inserter(output));
206 }
207
208 return output;
209}
210
Patrick Venture298930a2019-07-03 11:44:52 -0700211} // namespace ipmi_flash