DESIGN 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. Dinit Design Overview
  2. =====================
  3. NOTE: This document is intended to provide an overview. Specifics are generally included as
  4. comments/documentation within source files.
  5. Dinit's overall function is fairly straightforward but there are some considerations that cause
  6. its design to be more complicated than it might otherwise be. The heart of the issue is that
  7. Dinit should not stall on I/O and its essential operation should never fail, which means that many
  8. code paths cannot perform memory allocation for example, and that log messages all go through a
  9. buffer.
  10. The design is based around an event loop: events include process termination, signal reception,
  11. and I/O coming in from control sockets (used to issue service start/stop commands etc via
  12. dinitctl). The Dasynq library provides the backend event loop functionality; it is included
  13. with the Dinit source, but has a separate web page and documentation:
  14. http://davmac.org/projects/dasynq/
  15. Note that blocking actions are avoided: non-blocking I/O is used where possible; Dinit must
  16. remain responsive at all times.
  17. In Dinit, service actions are often performed by setting "propagation" flags, inserting the
  18. service in a queue, and then processing the queues. Thus a lot of service actions are performed
  19. by first calling a function on a service and then draining the propagation queues. More
  20. information can be found in comments at the beginning of the includes/service.h header.
  21. The logging subsystem is designed to log to two different sinks - typically, one is the console
  22. and one is the syslog facility, but other arrangements are possible. It uses a circular buffer
  23. for each output sink, and if the buffer becomes full it discards additional messages to avoid
  24. blocking (though it flags that this has occurred and later outputs a message to the sink).
  25. Services may acquire the console and in this case output is discarded (though it will of
  26. course continue to be logged to the other sink).
  27. Control protocol handling uses a circular receive buffer, but allocates storage for outgoing
  28. packets dynamically. If allocation fails an "out of memory" flag is set and the connection is
  29. closed after writing an "out of memory" information packet.
  30. Services
  31. --------
  32. There are presently four service types: internal, process, bgprocess and scripted. The latter
  33. three share the fact that they execute external processes and so naturally share some
  34. implementation. Specifically, the base service class is "service_record" - this directly
  35. supports "internal" services. The "base_process_service" class serves as a base class for
  36. the other three service types and provides common functionality.
  37. Various functions in the service_record / base_process_service classes are virtual, so that
  38. they can be overridden by the subclasses.
  39. All execution of child processes related to services currently goes through
  40. service_record::run_child_proc() (after forking).
  41. Source code organisation
  42. ------------------------
  43. Most function comments and general documentation can be found in header files rather than in the
  44. corresponding source files.
  45. dinit.cc - main event loop handling, command line parsing, general initialisation
  46. service.cc, proc-service.cc, baseproc-service.cc -
  47. service logic (see service.h, proc-service.h)
  48. run-child-proc.cc - contains service_record::run_child_proc(), used to run child processes.
  49. load-service.cc - service loading
  50. control.cc - control protocol handling
  51. dinit-log.cc - logging subsystem
  52. tests/ - various tests
  53. The utility sources are:
  54. dinitctl.cc - the control/status utility
  55. shutdown.cc - shutdown/halt/reboot utility
  56. Testing
  57. -------
  58. The unit tests work by mocking out parts of Dinit, and some system calls, in order to isolate
  59. functional units. In the src/tests/test-includes directory are three mock headers. When compiling
  60. tests, the src/tests/includes directory is populated with (linked) copies of the standard headers
  61. from src/include, but with mocked headers taken from src/tests/test-includes instead.
  62. Note that systems calls are not mocked directly, instead:
  63. - system calls are wrapped in the bp_sys namespace, as defined in the baseproc-sys.h header;
  64. - for testing, the header is replaced with a mock header.
  65. (This avoids problems that might arise from replacing important system calls, and in
  66. particular avoids interfering with the test harness itself).
  67. It is important when writing new code in Dinit to avoid calling system calls directly, and to
  68. instead call the wrapper in bp_sys.
  69. Exception safety, error handling
  70. --------------------------------
  71. In general operation Dinit methods should avoid throwing exceptions and be declared as 'noexcept',
  72. or otherwise be clearly documented as throwing exceptions. Errors should always be handled as
  73. gracefully as possible and should not prevent Dinit's continued operation. Particular care is
  74. needed for dynamic allocations: C++ style allocations (including adding elements to C++
  75. containers) will raise 'std::bad_alloc' if they cannot allocate memory, and this must be handled
  76. appropriately. Once it has started regular operation, Dinit must not terminate due to an error
  77. condition, even if the error is an allocation failure.