blob: cca4899823c30b67731fee0601601c9f99b54da6 [file] [log] [blame]
AppaRao Puli00840472018-10-03 19:37:46 +05301/*
2// Copyright (c) 2018 Intel 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 "srvcfg_manager.hpp"
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053017#include <boost/algorithm/string/replace.hpp>
18#include <sdbusplus/bus/match.hpp>
19#include <filesystem>
20#include <cereal/archives/json.hpp>
21#include <cereal/types/tuple.hpp>
22#include <cereal/types/unordered_map.hpp>
23#include <fstream>
AppaRao Puli00840472018-10-03 19:37:46 +053024
25std::shared_ptr<boost::asio::deadline_timer> timer = nullptr;
26std::map<std::string, std::shared_ptr<phosphor::service::ServiceConfig>>
27 srvMgrObjects;
28
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +053029static constexpr const char* srvCfgMgrFile = "/etc/srvcfg-mgr.json";
30
31// Base service name list. All instance of these services and
32// units(service/socket) will be managed by this daemon.
33static std::vector<std::string> serviceNames = {
34 "phosphor-ipmi-net", "bmcweb", "phosphor-ipmi-kcs", "start-ipkvm"};
35
36using ListUnitsType =
37 std::tuple<std::string, std::string, std::string, std::string, std::string,
38 std::string, sdbusplus::message::object_path, uint32_t,
39 std::string, sdbusplus::message::object_path>;
40
41enum class ListUnitElements
42{
43 name,
44 descriptionString,
45 loadState,
46 activeState,
47 subState,
48 followedUnit,
49 objectPath,
50 queuedJobType,
51 jobType,
52 jobObject
53};
54
55enum class UnitType
56{
57 service,
58 socket,
59 target,
60 device,
61 invalid
62};
63
64using MonitorListMap =
65 std::unordered_map<std::string, std::tuple<std::string, std::string,
66 std::string, std::string>>;
67MonitorListMap unitsToMonitor;
68
69enum class monitorElement
70{
71 unitName,
72 instanceName,
73 serviceObjPath,
74 socketObjPath
75};
76
77std::tuple<std::string, UnitType, std::string>
78 getUnitNameTypeAndInstance(const std::string& fullUnitName)
79{
80 UnitType type = UnitType::invalid;
81 std::string instanceName;
82 std::string unitName;
83 // get service type
84 auto typePos = fullUnitName.rfind(".");
85 if (typePos != std::string::npos)
86 {
87 const auto& typeStr = fullUnitName.substr(typePos + 1);
88 // Ignore types other than service and socket
89 if (typeStr == "service")
90 {
91 type = UnitType::service;
92 }
93 else if (typeStr == "socket")
94 {
95 type = UnitType::socket;
96 }
97 // get instance name if available
98 auto instancePos = fullUnitName.rfind("@");
99 if (instancePos != std::string::npos)
100 {
101 instanceName =
102 fullUnitName.substr(instancePos + 1, typePos - instancePos - 1);
103 unitName = fullUnitName.substr(0, instancePos);
104 }
105 else
106 {
107 unitName = fullUnitName.substr(0, typePos);
108 }
109 }
110 return std::make_tuple(unitName, type, instanceName);
111}
112
113static inline void
114 handleListUnitsResponse(sdbusplus::asio::object_server& server,
115 std::shared_ptr<sdbusplus::asio::connection>& conn,
116 boost::system::error_code ec,
117 const std::vector<ListUnitsType>& listUnits)
118{
119 // Loop through all units, and mark all units, which has to be
120 // managed, irrespective of instance name.
121 for (const auto& unit : listUnits)
122 {
123 const auto& fullUnitName =
124 std::get<static_cast<int>(ListUnitElements::name)>(unit);
125 auto [unitName, type, instanceName] =
126 getUnitNameTypeAndInstance(fullUnitName);
127 if (std::find(serviceNames.begin(), serviceNames.end(), unitName) !=
128 serviceNames.end())
129 {
130 std::string instantiatedUnitName =
131 unitName + addInstanceName(instanceName, "_40");
132 boost::replace_all(instantiatedUnitName, "-", "_2d");
133 const sdbusplus::message::object_path& objectPath =
134 std::get<static_cast<int>(ListUnitElements::objectPath)>(unit);
135 // Group the service & socket units togther.. Same services
136 // are managed together.
137 auto it = unitsToMonitor.find(instantiatedUnitName);
138 if (it != unitsToMonitor.end())
139 {
140 auto& value = it->second;
141 if (type == UnitType::service)
142 {
143 std::get<static_cast<int>(monitorElement::unitName)>(
144 value) = unitName;
145 std::get<static_cast<int>(monitorElement::instanceName)>(
146 value) = instanceName;
147 std::get<static_cast<int>(monitorElement::serviceObjPath)>(
148 value) = objectPath;
149 }
150 else if (type == UnitType::socket)
151 {
152 std::get<static_cast<int>(monitorElement::socketObjPath)>(
153 value) = objectPath;
154 }
155 }
156 if (type == UnitType::service)
157 {
158 unitsToMonitor.emplace(instantiatedUnitName,
159 std::make_tuple(unitName, instanceName,
160 objectPath.str, ""));
161 }
162 else if (type == UnitType::socket)
163 {
164 unitsToMonitor.emplace(
165 instantiatedUnitName,
166 std::make_tuple("", "", "", objectPath.str));
167 }
168 }
169 }
170
171 bool updateRequired = false;
172 bool jsonExist = std::filesystem::exists(srvCfgMgrFile);
173 if (jsonExist)
174 {
175 std::ifstream file(srvCfgMgrFile);
176 cereal::JSONInputArchive archive(file);
177 MonitorListMap savedMonitorList;
178 archive(savedMonitorList);
179
180 // compare the unit list read from systemd1 and the save list.
181 MonitorListMap diffMap;
182 std::set_difference(begin(unitsToMonitor), end(unitsToMonitor),
183 begin(savedMonitorList), end(savedMonitorList),
184 std::inserter(diffMap, begin(diffMap)));
185 for (auto& unitIt : diffMap)
186 {
187 auto it = savedMonitorList.find(unitIt.first);
188 if (it == savedMonitorList.end())
189 {
190 savedMonitorList.insert(unitIt);
191 updateRequired = true;
192 }
193 }
194 unitsToMonitor = savedMonitorList;
195 }
196 if (!jsonExist || updateRequired)
197 {
198 std::ofstream file(srvCfgMgrFile);
199 cereal::JSONOutputArchive archive(file);
200 archive(CEREAL_NVP(unitsToMonitor));
201 }
202
203 // create objects for needed services
204 for (auto& it : unitsToMonitor)
205 {
206 std::string objPath(std::string(phosphor::service::srcCfgMgrBasePath) +
207 "/" + it.first);
208 std::string instanciatedUnitName =
209 std::get<static_cast<int>(monitorElement::unitName)>(it.second) +
210 addInstanceName(
211 std::get<static_cast<int>(monitorElement::instanceName)>(
212 it.second),
213 "@");
214 auto srvCfgObj = std::make_unique<phosphor::service::ServiceConfig>(
215 server, conn, objPath,
216 std::get<static_cast<int>(monitorElement::unitName)>(it.second),
217 std::get<static_cast<int>(monitorElement::instanceName)>(it.second),
218 std::get<static_cast<int>(monitorElement::serviceObjPath)>(
219 it.second),
220 std::get<static_cast<int>(monitorElement::socketObjPath)>(
221 it.second));
222 srvMgrObjects.emplace(
223 std::make_pair(std::move(objPath), std::move(srvCfgObj)));
224 }
225}
226
227void init(sdbusplus::asio::object_server& server,
228 std::shared_ptr<sdbusplus::asio::connection>& conn)
229{
230 // Go through all systemd units, and dynamically detect and manage
231 // the service daemons
232 conn->async_method_call(
233 [&server, &conn](boost::system::error_code ec,
234 const std::vector<ListUnitsType>& listUnits) {
235 if (ec)
236 {
237 phosphor::logging::log<phosphor::logging::level::ERR>(
238 "async_method_call error: ListUnits failed");
239 return;
240 }
241 handleListUnitsResponse(server, conn, ec, listUnits);
242 },
243 sysdService, sysdObjPath, sysdMgrIntf, "ListUnits");
244}
245
246void checkAndInit(sdbusplus::asio::object_server& server,
247 std::shared_ptr<sdbusplus::asio::connection>& conn)
248{
249 // Check whether systemd completed all the loading before initializing
250 conn->async_method_call(
251 [&server, &conn](boost::system::error_code ec,
252 const std::variant<uint64_t>& value) {
253 if (ec)
254 {
255 phosphor::logging::log<phosphor::logging::level::ERR>(
256 "async_method_call error: ListUnits failed");
257 return;
258 }
259 if (std::get<uint64_t>(value))
260 {
261 if (!srvMgrObjects.size())
262 {
263 init(server, conn);
264 }
265 }
266 },
267 sysdService, sysdObjPath, dBusPropIntf, dBusGetMethod, sysdMgrIntf,
268 "FinishTimestamp");
269}
AppaRao Puli00840472018-10-03 19:37:46 +0530270
271int main()
272{
AppaRao Puli00840472018-10-03 19:37:46 +0530273 boost::asio::io_service io;
274 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
275 timer = std::make_shared<boost::asio::deadline_timer>(io);
276 conn->request_name(phosphor::service::serviceConfigSrvName);
Richard Marian Thomaiyar4ae87cd2019-01-28 20:55:37 +0530277 auto server = sdbusplus::asio::object_server(conn, true);
278 auto mgrInterface =
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530279 server.add_interface(phosphor::service::srcCfgMgrBasePath, "");
Richard Marian Thomaiyar4ae87cd2019-01-28 20:55:37 +0530280 mgrInterface->initialize();
Richard Marian Thomaiyar90f2da32019-05-23 05:37:50 +0530281 server.add_manager(phosphor::service::srcCfgMgrBasePath);
282 // Initialize the objects after systemd indicated startup finished.
283 auto userUpdatedSignal = std::make_unique<sdbusplus::bus::match::match>(
284 static_cast<sdbusplus::bus::bus&>(*conn),
285 "type='signal',"
286 "member='StartupFinished',path='/org/freedesktop/systemd1',"
287 "interface='org.freedesktop.systemd1.Manager'",
288 [&server, &conn](sdbusplus::message::message& msg) {
289 if (!srvMgrObjects.size())
290 {
291 init(server, conn);
292 }
293 });
294 // this will make sure to initialize the objects, when daemon is
295 // restarted.
296 checkAndInit(server, conn);
297
AppaRao Puli00840472018-10-03 19:37:46 +0530298 io.run();
299
300 return 0;
301}