138 const auto& platform = *unit.
platform;
140 auto service_name = [&](
const std::string& name)
143 auto instance = platform.name.substr(std::min(platform.base_name.size(), platform.name.size()));
146 std::string gobyd_service;
147 for (
const auto& line : lines)
150 gobyd_service = service_name(line.name);
153 std::vector<SystemdUnitFile> units;
154 std::vector<std::string> services;
156 std::vector<std::string> dropped;
157 std::string previous;
158 for (
const auto& line : lines)
160 const auto& e = *line.entry;
161 auto settings =
detail::merged(platform.systemd.defaults, e.systemd);
162 if (!e.enabled || settings.skip)
170 e.name +
": when: '" + *e.when +
171 "' cannot be decided before launch, so no unit is written; set its "
172 "values with -D, or give systemd: { condition: ... }",
175 if (!decided || !*decided)
177 dropped.push_back(line.name);
182 auto service = service_name(line.name);
184 line.kill.empty() ? (line.goby_application ?
"goby_terminate" :
"SIGTERM") : line.kill;
185 bool needs_gobyd = line.goby_application && !
detail::is_gobyd(e) && !gobyd_service.empty();
186 auto description = settings.description.value_or(line.name +
" (" + unit.
name() +
" of " +
187 deployment.
name +
")");
189 std::string environment;
190 for (
const auto& kv : line.env)
191 environment +=
"Environment=\"" + kv.first +
"=" + kv.second +
"\"\n";
194 if (settings.unit_template)
196 auto path = std::filesystem::path(*settings.unit_template);
197 if (path.is_relative())
198 path = std::filesystem::path(deployment.
file).parent_path() / path;
199 std::ifstream in(path);
203 e.name +
": cannot open template " + path.string(), e.location);
206 std::stringstream ss;
209 auto config = line.goby_application && !e.config
210 ? platform.launch_settings.config.value_or(
"")
211 : e.config.value_or(
"");
212 auto args = line.goby_application && !e.args
213 ? platform.launch_settings.args.value_or(
"")
214 : e.args.value_or(
"");
215 std::map<std::string, std::string> vars{
217 {
"unit_name", service},
218 {
"application", e.application},
219 {
"platform", platform.name},
220 {
"instance", instance},
221 {
"deployment", deployment.
name},
222 {
"description", description},
223 {
"exec", e.exec.value_or(e.process ? e.name : e.application)},
224 {
"command", line.unconditional_command},
228 {
"env", environment},
230 {
"user", settings.user.value_or(
"")},
231 {
"group", settings.group.value_or(
"")},
232 {
"wanted_by", settings.wanted_by.value_or(target)},
234 {
"unit", settings.unit.value_or(
"")},
235 {
"service", settings.service.value_or(
"")},
236 {
"install", settings.install.value_or(
"")}};
237 for (
const auto& kv : settings.vars) vars[kv.first] = kv.second;
242 std::stringstream ss;
243 ss <<
"# Generated by 'goby interface systemd' from " << deployment.
file
244 <<
": edit that file rather than this one.\n";
246 ss <<
"Description=" << description <<
"\n";
247 ss <<
"PartOf=" << target <<
"\n";
249 ss <<
"After=" << gobyd_service <<
"\nBindsTo=" << gobyd_service <<
"\n";
250 if (platform.systemd.ordered && !previous.empty() &&
251 !(needs_gobyd && previous == gobyd_service))
252 ss <<
"After=" << previous <<
"\n";
253 if (settings.condition)
254 ss << *settings.condition <<
"\n";
256 ss << *settings.unit <<
"\n";
258 ss <<
"\n[Service]\n";
260 ss <<
"User=" << *settings.user <<
"\n";
262 ss <<
"Group=" << *settings.group <<
"\n";
265 if (line.goby_application && kill ==
"goby_terminate")
266 ss <<
"ExecStop=goby_terminate --target_pid $MAINPID\n";
268 ss <<
"KillSignal=" << (kill ==
"goby_terminate" ?
"SIGTERM" : kill) <<
"\n";
269 ss <<
"Restart=on-failure\n";
270 if (settings.service)
271 ss << *settings.service <<
"\n";
273 ss <<
"\n[Install]\n";
274 ss <<
"WantedBy=" << settings.wanted_by.value_or(target) <<
"\n";
275 if (settings.install)
276 ss << *settings.install <<
"\n";
280 if (text.find(
"${") != std::string::npos)
282 service +
" still has a ${...} that systemd will read as its own variable",
284 units.push_back({service, text});
285 services.push_back(service);
290 for (
const auto& line : lines)
292 const auto& e = *line.entry;
293 std::vector<std::string> waits_for;
299 waits_for.insert(waits_for.end(), expected.begin(), expected.end());
301 for (
const auto& name : dropped)
303 if (std::find(waits_for.begin(), waits_for.end(), name) != waits_for.end())
305 line.name +
" waits for " + name +
", which has no unit", e.location);
309 if (!platform.systemd.target)
311 std::stringstream ss;
312 ss <<
"# Generated by 'goby interface systemd' from " << deployment.
file
313 <<
": edit that file rather than this one.\n";
315 ss <<
"Description=" << unit.
name() <<
" of " << deployment.
name <<
"\n";
316 if (!services.empty())
319 for (std::size_t i = 0; i < services.size(); ++i) ss << (i ?
" " :
"") << services[i];
322 ss <<
"\n[Install]\nWantedBy=multi-user.target\n";
323 units.insert(units.begin(), {target, ss.str()});