1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "ServiceManager.hpp"
4
5#include "common/ConfigurationManager.hpp"
6#include "global.hpp"
7#include "logging.hpp"
8#include "units/unit.hpp"
9
10#include <functional>
11#include <set>
12
13using namespace std::string_literals;
14
15void ServiceManagerImpl::OnProcessExit(pid_t pid, int status)
16{
17 // check if any service has exited
18 for (const auto &[id, unit] : ConfigurationManager->GetAllUnits())
19 {
20 if (unit->GetType() != UnitType::Service)
21 continue;
22 const auto service = std::dynamic_pointer_cast<Service>(r: unit);
23 if (!service)
24 continue;
25
26 const auto mainPid = service->GetMainPid();
27 if (mainPid == -1)
28 continue;
29
30 if (pid == mainPid)
31 {
32 service->OnExited(status);
33 return; // we found the service that exited
34 }
35 }
36
37 if (pid > 0)
38 {
39 if (WIFEXITED(status))
40 std::cout << "process " << pid << " exited with status " << WEXITSTATUS(status) << std::endl;
41 else if (WIFSIGNALED(status))
42 std::cout << "process " << pid << " killed by signal " << WTERMSIG(status) << std::endl;
43 }
44}
45
46std::vector<std::string> ServiceManagerImpl::GetStartupOrder(const std::string &id) const
47{
48 std::set<std::string> visited;
49 std::vector<std::string> order;
50
51 std::function<void(const std::string &)> visit = [&](const std::string &id)
52 {
53 if (visited.contains(x: id))
54 return;
55
56 visited.insert(x: id);
57 const auto unit = ConfigurationManager->GetUnit(id);
58 for (const auto &dep_id : unit->GetDependencies())
59 visit(dep_id);
60 order.push_back(x: id);
61 };
62
63 visit(id);
64 return order;
65}
66
67bool ServiceManagerImpl::StartUnit(const std::string &id) const
68{
69 if (!ConfigurationManager->HasUnit(id))
70 {
71 if (!ConfigurationManager->HasUnit(id: id + ".service"))
72 {
73 std::cerr << RED("unit not found") << " " << id << std::endl;
74 return false;
75 }
76
77 if (!StartUnit(id: id + ".service"))
78 {
79 std::cerr << RED("unit not found") << " " << id << std::endl;
80 return false;
81 }
82 return true;
83 }
84
85 const auto order = GetStartupOrder(id);
86 for (const auto &unit_id : order)
87 {
88 const auto unit = ConfigurationManager->GetUnit(id: unit_id);
89
90 Debug << STARTING() << "Starting " << unit->GetDescription() << " (" << unit->id << ")" << std::endl;
91 if (unit->GetStatus().status == UnitStatus::UnitStarted)
92 continue;
93 if (!unit->Start())
94 {
95 std::cerr << std::endl << FAILED() << " Failed to start " << unit->GetDescription() << ": " << *unit->GetFailReason() << std::endl;
96 return false;
97 }
98
99 // timed wait for the unit to start
100 for (int i = 0; i < 50; i++)
101 {
102 if (unit->GetStatus().status != UnitStatus::UnitStarting)
103 break;
104 Debug << "Waiting for " << unit->GetDescription() << " to start, n = " << i << std::endl;
105 std::this_thread::sleep_for(std::chrono::milliseconds(100));
106 }
107
108 if (unit->GetStatus().status != UnitStatus::UnitStarted)
109 {
110 std::cerr << FAILED() << " Failed to start " << unit->GetDescription() << ": " << unit->GetStatus().status << std::endl;
111 return false;
112 }
113 }
114 return true;
115}
116
117bool ServiceManagerImpl::StopUnit(const std::string &id) const
118{
119 Debug << "Stopping unit: " << id << std::endl;
120 const auto unit = ConfigurationManager->GetUnit(id);
121 if (!unit)
122 {
123 if (!ConfigurationManager->HasUnit(id: id + ".service"))
124 {
125 std::cerr << RED("unit not found") << " " << id << std::endl;
126 return false;
127 }
128
129 if (!StopUnit(id: id + ".service"))
130 {
131 std::cerr << RED("unit not found") << " " << id << std::endl;
132 return false;
133 }
134 return true;
135 }
136
137 // first stop all dependent units that depends on this unit
138 const auto units = ConfigurationManager->GetAllUnits();
139 for (const auto &[unitid, unit] : units)
140 {
141 const auto deps = unit->GetDependencies();
142 if (std::find(first: deps.begin(), last: deps.end(), val: id) != deps.end())
143 StopUnit(id: unitid);
144 }
145
146 Debug << STOPPING() << "Real Stopping " << unit->GetDescription() << " (" << unit->id << ")" << std::endl;
147 if (!unit->GetStatus().active)
148 return true;
149
150 if (unit->GetStatus().status == UnitStatus::UnitStopping || unit->GetStatus().status == UnitStatus::UnitStopped)
151 {
152 std::cerr << "Unit " << unit->GetDescription() << " is already stopping or stopped." << std::endl;
153 return true;
154 }
155
156 if (!unit->Stop())
157 {
158 std::cerr << FAILED() << " Failed to stop " << unit->GetDescription() << ": " << *unit->GetFailReason() << std::endl;
159 return false;
160 }
161 return true;
162}
163
164void ServiceManagerImpl::OnUnitStarted(Unit *unit)
165{
166 if (unit->GetType() == UnitType::Target)
167 std::cout << OK() << " Reached target " << unit->description << std::endl;
168 else
169 std::cout << OK() << " Started " << unit->description << std::endl;
170}
171
172void ServiceManagerImpl::OnUnitStopped(Unit *unit)
173{
174 if (unit->GetType() == UnitType::Target)
175 std::cout << OK() << " Stopped target " << unit->description << std::endl;
176 else if (unit->GetType() == UnitType::Service)
177 std::cout << OK() << " Stopped " << unit->description << std::endl;
178 else
179 std::cout << OK() << " Stopped " << unit->description << std::endl;
180}
181
182bool ServiceManagerImpl::StartDefaultTarget() const
183{
184 const auto defaultTarget = ConfigurationManager->GetDefaultTarget();
185 return StartUnit(id: defaultTarget);
186}
187