1OpenBMC & Systemd
2===================
3OpenBMC uses [systemd](https://www.freedesktop.org/wiki/Software/systemd/) to
4manage all processes.  It has its own set of target and service units to
5control which processes are started.  There is a lot of documentation on systemd
6and to do OpenBMC state work, you're going to have to read some of it.  Here's
7the highlights:
8
9[Unit](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#) -
10Units are the basic framework of all systemd work.
11[Service](https://www.freedesktop.org/software/systemd/man/systemd.service.html) -
12Services are a type of unit, that define the processes to be run and execute.
13[Target](https://www.freedesktop.org/software/systemd/man/systemd.target.html) -
14Targets are another type of unit, they have two purposes:
15
161. Define synchronization points among services.
172. Define the services that get run for a given target.
18
19On an OpenBMC system, you can go to /lib/systemd/system/ and see all of the
20systemd units on the system.  You can easily cat these files to start looking at
21the relationships among them.  Service files can also be found in
22/etc/systemd/system and /run/systemd/system as well.
23
24[systemctl](https://www.freedesktop.org/software/systemd/man/systemctl.html) is
25the main tool you use to interact with systemd and its units.
26
27----------
28## Initial Power
29When an OpenBMC system first has power applied, it starts the "default.target"
30unless an alternate target is specified on the kernel command line.  In
31Phosphor OpenBMC, there is a link from `default.target` to `multi-user.target`.
32
33You'll find all the phosphor services associated with `multi-user.target`.
34
35## Server Power On
36When OpenBMC is used within a server, the [obmc-host-start@.target](https://github.com/openbmc/phosphor-state-manager/blob/master/target_files/obmc-host-start%40.target)
37is what drives the boot of the system.
38
39To start it you would run `systemctl start obmc-host-start@0.target`.
40
41If you dig into its .requires relationship, you'll see the following in the file
42system
43
44```
45ls -1 /lib/systemd/system/obmc-host-start@0.target.requires/
46obmc-host-startmin@0.target
47phosphor-reset-host-reboot-attempts@0.service
48```
49The [obmc-host-startmin@.target](https://github.com/openbmc/phosphor-state-manager/blob/master/target_files/obmc-host-startmin%40.target)
50represents the bare minimum of services and targets required to start the host.
51This target is also utilized in host reboot scenarios. This distinction of a
52host-start and a host-startmin target allows the user to put services in the
53`obmc-host-start@.target` that should only be run on an initial host boot (and
54not run on host reboots). For example, in the output above you can see the user
55only wants to run the `phosphor-reset-host-reboot-attempts@0.service` on a fresh
56host boot attempt.
57
58Next if we look at the `obmc-host-startmin@0.target`, we see this:
59```
60ls -1 /lib/systemd/system/obmc-host-startmin@0.target.requires/
61obmc-chassis-poweron@0.target
62start_host@0.service
63```
64
65You can see within `obmc-host-startmin@0.target` that we have another target in
66there, `obmc-chassis-poweron@0.target`, along with a service aptly named
67`start_host@0.service`.
68
69The `obmc-chassis-poweron@0.target` has corresponding services associated with
70it:
71```
72ls -1 /lib/systemd/system/obmc-chassis-poweron@0.target.requires/
73op-power-start@0.service
74op-wait-power-on@0.service
75```
76
77If you run `systemctl start obmc-host-start@0.target` then systemd will start
78execution of all of the above associated target and services.
79
80The services have dependencies within them that control the execution of each
81service (for example, the op-power-start.service will run prior to the
82op-wait-power-on.service).  These dependencies are set using targets and the
83Wants,Before,After keywords.
84
85## Server Power Off (Soft)
86The soft server power off function is encapsulated in the
87`obmc-host-shutdown@.target`.  This target is soft in that it notifies the host
88of the power off request and gives it a certain amount of time to shut itself
89down.
90
91## Server Power Off (Hard)
92The hard server power off is encapsulated in the
93`obmc-chassis-hard-poweroff@.target`. This target will force the stopping
94of the soft power off service if running, and immediately cut power to the
95system.
96
97## Server Reboot
98The reboot of the server is encapsulated in the `obmc-host-reboot@.target`.
99This target will utilize the soft power off target and then, once that
100completes, start the host power on target.
101
102## Systemd Control in OpenBMC
103There are a collection of services within OpenBMC that interact with systemd and
104its unit files, providing somewhat of an abstraction layer from the user of the
105OpenBMC system and systemd.  See the [state](https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/xyz/openbmc_project/State)
106interfaces for more information on this function.
107
108For example, if you wanted to execute the server power on function, you would do
109the following:
110
111> busctl set-property xyz.openbmc_project.State.Host /xyz/openbmc_project/state/host0
112xyz.openbmc_project.State.Host RequestedHostTransition s
113xyz.openbmc_project.State.Host.Transition.On
114
115Underneath the covers, this is calling systemd with the server power on target.
116
117## Systemd Services or Monitoring Applications
118A common question when creating new OpenBMC applications which need to
119execute some logic in the context of systemd targets is whether they should
120be triggered by systemd services or by monitoring for the appropriate
121D-Bus signal indicating the start/stop of the target they are interested in.
122
123The basic guidelines for when to create a systemd service are the following:
124- If your application logic depends on other systemd based services then
125  make it a systemd service and utilize the Wants/After/Before service
126  capabilities.
127- If other applications depend on your application logic then it should be a
128  systemd service.
129- If your application failing during the target start could impact targets or
130  services that run after it, then it should be a systemd service. This ensures
131  dependent targets are not started if your application fails.
132
133## Error Handling of Systemd
134With great numbers of targets and services, come great chances for failures.
135To make OpenBMC a robust and productive system, it needs to be sure to have an
136error handling policy for when services and their targets fail.
137
138When a failure occurs, the OpenBMC software needs to notify the users of the
139system and provide mechanisms for either the system to automatically retry the
140failed operation (i.e. reboot the system) or to stay in a quiesced state so that
141error data can be collected and the failure can be investigated.
142
143There are two main failure scenarios when it comes to OpenBMC and systemd usage:
144
1451. A service within a target fails
146- If the service is a "oneshot" type, and the service is required
147(not wanted) by the target then the target will fail if the service
148fails
149    - Define a behavior for when the target fails using the
150    "OnFailure" option (i.e. go to a new failure target if any required
151    service fails)
152- If the service is not a "oneshot", then it can not fail the target
153(the target only knows that it started successfully)
154    - Define a behavior for when the service fails (OnFailure)
155    option.
156    - The service can not have "RemainAfterExit=yes" otherwise, the OnFailure
157    action does not occur until the service is stopped (instead of when it
158    fails)
159        - *See more information below on [RemainAfterExit](#RemainAfterExit)
160
1612. A failure outside of a normal systemd target/service (host watchdog expires,
162host checkstop detected)
163- The service which detects this failure is responsible for logging the
164appropriate error, and instructing systemd to go to the appropriate target
165
166Within OpenBMC, there is a host quiesce target.  This is the target that other
167host related targets should go to when they hit a failure. Other software within
168OpenBMC can then monitor for the entry into this quiesce target and will handle
169the halt vs. automatic reboot functionality.
170
171Targets which are not host related, will need special thought in regards to
172their error handling.  For example, the target responsible for applying chassis
173power, `obmc-chassis-poweron@0.target`, will have a
174`OnFailure=obmc-chassis-poweroff@%i.target` error path.  That is, if the
175chassis power on target fails then power off the chassis.
176
177The above info sets up some general **guidelines** for our host related
178targets and services:
179
180- All targets should have a `OnFailure=obmc-quiesce-host@.target`
181- All services which are required for a target to achieve its function should
182be RequiredBy that target (not WantedBy)
183- All services should first try to be "Type=oneshot" so that we can just rely on
184the target failure path
185- If a service can not be "Type=oneshot", then it needs to have a
186`OnFailure=obmc-quiesce-host@.target` and ideally set "RemainAfterExit=no"
187(but see caveats on this below)
188- If a service can not be any of these then it's up to the service application
189to call systemd with the `obmc-quiesce-host@.target` on failures
190
191### RemainAfterExit
192This is set to "yes" for most OpenBMC services to handle the situation where
193someone starts the same target twice.   If the associated service with that
194target is not running (i.e. RemainAfterExit=no), then the service will be
195executed again.  Think about someone accidentally running the
196`obmc-chassis-poweron@.target` twice.  If you execute it when the operating system
197is up and running, and the service which toggles the pgood pin is re-executed,
198you're going to crash your system.  Given this info, the goal should always be
199to write "oneshot" services that have RemainAfterExit set to yes.
200
201## Target and Service Dependency Details
202There are some tools available out there to visualize systemd service and
203target dependencies (systemd-analyze) but due to the complexity of our design,
204they do not generate anything very useful.
205
206For now, document the current dependencies on a witherspoon system for
207reference.
208
209```
210R = Requires
211W = Wants
212A = After
213B = Before
214S = Start (runs a command to start another target or service)
215(S) = Synchronization Target
216```
217
218### Soft Power Off
219```
220obmc-host-shutdown.target
221  R: xyz.openbmc_project.Ipmi.Internal.SoftPowerOff.service
222     W: obmc-host-stopping.target (S)
223     B: obmc-host-stopping.target (S)
224  R: obmc-chassis-poweroff.target
225     R: obmc-host-stop.target
226        R: op-occ-disable.service
227           B: obmc-host-stop-pre.target
228     R: op-power-stop.service
229        W: obmc-power-stop.target (S)
230        B: obmc-power-stop.target (S)
231        W: obmc-power-stop-pre.target (S)
232        A: obmc-power-stop-pre.target (S)
233        W: mapper-wait@-org-openbmc-control-power.service
234        A: mapper-wait@-org-openbmc-control-power.service
235     R: op-wait-power-off.service
236        B: obmc-power-off.target (S)
237        W: obmc-power-stop.target (S)
238        B: obmc-power-stop.target (S)
239        W: obmc-power-stop-pre.target (S)
240        A: obmc-power-stop-pre.target (S)
241        W: mapper-wait@-org-openbmc-control-power.service
242        A: mapper-wait@-org-openbmc-control-power.service
243     R: op-powered-off.service
244        A: op-wait-power-off.service
245        R: op-wait-power-off.service
246        S: obmc-chassis-powered-off.target
247     W: pcie-poweroff.service
248        B: op-power-stop.service
249        A: obmc-power-stop-pre@.target
250```
251
252#### Synchronization Target Dependencies
253```
254obmc-power-stop.target
255  W: obmc-power-stop-pre.target
256  A: obmc-power-stop-pre.target
257
258obmc-power-stop-pre.target
259  W: obmc-host-stopped.target
260  A: obmc-host-stopped.target
261
262obmc-host-stopped.target
263  W: obmc-host-stopping.target
264  A: obmc-host-stopping.target
265  B: obmc-power-stop-pre.target
266
267obmc-host-stopping.target
268  W: obmc-host-stop-pre.target
269  A: obmc-host-stop-pre.target
270  B: obmc-host-stopped.target
271
272obmc-host-stop-pre.target
273  B: obmc-host-stopping.target
274```
275