/* * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once //! \file //! The runlevel system provides a global switch for changing the system's //! operating mode, roughly analogous to runlevels in UNIX SysVinit. For each //! runlevel, a service can be in either an enabled or disabled state. When a //! service is disabled, it is "off" or "suppressed"; it is non-operational. //! When a service is enabled, it operates normally. //! //! The exact semantics of the enabled state is left up to the individual //! service. For some services, it may even make sense that in certain //! situations the enabled state is functionally indistinguishable from the //! disabled state. A service's enabled and disabled states are analogous to a //! circuit breaker for a light switch: when the circuit breaker is off, the //! light is off regardless of the position of the light switch. When the //! circuit breaker is turned on, the light switch determines whether the light //! is on or off. Following this analogy further; for many services it makes //! sense to allow the light switch to be flipped while the circuit breaker is //! off. While this state change is not reflected immediately, it takes effect //! as soon as the circuit breaker is enabled. //! //! Services need to be modified to be controllable through the runlevel system. //! The service needs to have a single `service_set_enabled(bool)` function //! which enables or disables the service as described above. This function //! should never be called outside of the runlevel system. This function must be //! idempotent: calling it any number of times with the same argument must //! have the same effect as calling it once. And the service must start in the //! disabled state when first initialized. //! //! The final requirement, that services must start up disabled, is in place for //! two reasons. The system may boot into a runlevel for which the service is //! disabled, and rapidly switching from uninitialized -> enabled -> disabled is //! undesirable. And requiring that each service be explicitly enabled as a //! separate step from initialization ensures that the disabled -> enabled state //! change code path is exercised on every normal boot, making it more likely //! that bugs in the service's state change code are noticed sooner. //! //! More details on the design of the runlevel system can be found in the design //! doc at //! https://docs.google.com/a/pulse-dev.net/document/d/1jz1xRvItR228ufFS8Fcf70lhwoZ7DI7G1MBD2hymVAs/edit //! //! \par Why is there no `services_get_runlevel()` function? //! Having such a function would make it possible for code to alter its //! behaviour based on runlevel without properly integrating into the runlevel //! system. This would subvert the property that all services controlled by the //! runlevel system are centrally listed and controlled by the //! `services_set_runlevel()` function. Anything that could be accomplished with //! `services_get_runlevel()` can be better accomplished by making that service //! controllable by the runlevel system in the usual way. // Runlevels are defined in runlevel.def. DO NOT ADD NEW RUNLEVELS TO THIS FILE! typedef enum RunLevel { #define RUNLEVEL(number, name) RunLevel_##name = number, #include "runlevel.def" #undef RUNLEVEL RunLevel_COUNT } RunLevel; void services_set_runlevel(RunLevel runlevel);