docs: use collections to structure the data
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: systemd Repository Architecture
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# The systemd Repository Architecture
|
||||
|
||||
## Code Map
|
||||
|
||||
This document provides a high-level overview of the various components of the
|
||||
systemd repository.
|
||||
|
||||
## Source Code
|
||||
|
||||
Directories in `src/` provide the implementation of all daemons, libraries and
|
||||
command-line tools shipped by the project. There are many, and more are
|
||||
constantly added, so we will not enumerate them all here — the directory
|
||||
names are self-explanatory.
|
||||
|
||||
### Shared Code
|
||||
|
||||
The code that is shared between components is split into a few directories,
|
||||
each with a different purpose:
|
||||
|
||||
- `src/basic/` and `src/fundamental/` — those directories contain code
|
||||
primitives that are used by all other code. `src/fundamental/` is stricter,
|
||||
because it used for EFI and user-space code, while `src/basic/` is only used
|
||||
for user-space code. The code in `src/fundamental/` cannot depend on any
|
||||
other code in the tree, and `src/basic/` can depend only on itself and
|
||||
`src/fundamental/`. For user-space, a static library is built from this code
|
||||
and linked statically in various places.
|
||||
|
||||
- `src/libsystemd/` implements the `libsystemd.so` shared library (also
|
||||
available as static `libsystemd.a`). This code may use anything in
|
||||
`src/basic/` or `src/fundamental/`.
|
||||
|
||||
- `src/shared/` provides various utilities and code shared between other
|
||||
components that is exposed as the `libsystemd-shared-<nnn>.so` shared library.
|
||||
|
||||
The other subdirectories implement individual components. They may depend only
|
||||
on `src/fundamental/` + `src/basic/`, or also on `src/libsystemd/`, or also on
|
||||
`src/shared/`.
|
||||
|
||||
You might wonder what kind of code belongs where. In general, the rule is that
|
||||
code should be linked as few times as possible, ideally only once. Thus code that
|
||||
is used by "higher-level" components (e.g. our binaries which are linked to
|
||||
`libsystemd-shared-<nnn>.so`), would go to a subdirectory specific to that
|
||||
component if it is only used there. If the code is to be shared between
|
||||
components, it'd go to `src/shared/`. Shared code that is used by multiple
|
||||
components that do not link to `libsystemd-shared-<nnn>.so` may live either in
|
||||
`src/libsystemd/`, `src/basic/`, or `src/fundamental/`. Any code that is used
|
||||
only for EFI goes under `src/boot/efi/`, and `src/fundamental/` if is shared
|
||||
with non-EFI compoenents.
|
||||
|
||||
To summarize:
|
||||
|
||||
`src/fundamental/`
|
||||
- may be used by all code in the tree
|
||||
- may not use any code outside of `src/fundamental/`
|
||||
|
||||
`src/basic/`
|
||||
- may be used by all code in the tree
|
||||
- may not use any code outside of `src/fundamental/` and `src/basic/`
|
||||
|
||||
`src/libsystemd/`
|
||||
- may be used by all code in the tree that links to `libsystem.so`
|
||||
- may not use any code outside of `src/fundamental/`, `src/basic/`, and
|
||||
`src/libsystemd/`
|
||||
|
||||
`src/shared/`
|
||||
- may be used by all code in the tree, except for code in `src/basic/`,
|
||||
`src/libsystemd/`, `src/nss-*`, `src/login/pam_systemd.*`, and files under
|
||||
`src/journal/` that end up in `libjournal-client.a` convenience library.
|
||||
- may not use any code outside of `src/fundamental/`, `src/basic/`,
|
||||
`src/libsystemd/`, `src/shared/`
|
||||
|
||||
### PID 1
|
||||
|
||||
Code located in `src/core/` implements the main logic of the systemd system (and user)
|
||||
service manager.
|
||||
|
||||
BPF helpers written in C and used by PID 1 can be found under `src/core/bpf/`.
|
||||
|
||||
#### Implementing Unit Settings
|
||||
|
||||
The system and session manager supports a large number of unit settings. These can generally
|
||||
be configured in three ways:
|
||||
|
||||
1. Via textual, INI-style configuration files called *unit* *files*
|
||||
2. Via D-Bus messages to the manager
|
||||
3. Via the `systemd-run` and `systemctl set-property` commands
|
||||
|
||||
From a user's perspective, the third is a wrapper for the second. To implement a new unit
|
||||
setting, it is necessary to support all three input methods:
|
||||
|
||||
1. *unit* *files* are parsed in `src/core/load-fragment.c`, with many simple and fixed-type
|
||||
unit settings being parsed by common helpers, with the definition in the generator file
|
||||
`src/core/load-fragment-gperf.gperf.in`
|
||||
2. D-Bus messages are defined and parsed in `src/core/dbus-*.c`
|
||||
3. `systemd-run` and `systemctl set-property` do client-side parsing and translation into
|
||||
D-Bus messages in `src/shared/bus-unit-util.c`
|
||||
|
||||
So that they are exercised by the fuzzing CI, new unit settings should also be listed in the
|
||||
text files under `test/fuzz/fuzz-unit-file/`.
|
||||
|
||||
### systemd-udev
|
||||
|
||||
Sources for the udev daemon and command-line tool (single binary) can be found under
|
||||
`src/udev/`.
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Source files found under `src/test/` implement unit-level testing, mostly for
|
||||
modules found in `src/basic/` and `src/shared/`, but not exclusively. Each test
|
||||
file is compiled in a standalone binary that can be run to exercise the
|
||||
corresponding module. While most of the tests can be run by any user, some
|
||||
require privileges, and will attempt to clearly log about what they need
|
||||
(mostly in the form of effective capabilities). These tests are self-contained,
|
||||
and generally safe to run on the host without side effects.
|
||||
|
||||
Ideally, every module in `src/basic/` and `src/shared/` should have a
|
||||
corresponding unit test under `src/test/`, exercising every helper function.
|
||||
|
||||
### Fuzzing
|
||||
|
||||
Fuzzers are a type of unit tests that execute code on an externally-supplied
|
||||
input sample. Fuzzers are called `fuzz-*`. Fuzzers for `src/basic/` and
|
||||
`src/shared` live under `src/fuzz/`, and those for other parts of the codebase
|
||||
should be located next to the code they test.
|
||||
|
||||
Files under `test/fuzz/` contain input data for fuzzers, one subdirectory for
|
||||
each fuzzer. Some of the files are "seed corpora", i.e. files that contain
|
||||
lists of settings and input values intended to generate initial coverage, and
|
||||
other files are samples saved by the fuzzing engines when they find an issue.
|
||||
|
||||
When adding new input samples under `test/fuzz/*/`, please use some
|
||||
short-but-meaningful names. Names of meson tests include the input file name
|
||||
and output looks awkward if they are too long.
|
||||
|
||||
Fuzzers are invoked primarily in three ways: firstly, each fuzzer is compiled
|
||||
as a normal executable and executed for each of the input samples under
|
||||
`test/fuzz/` as part of the test suite. Secondly, fuzzers may be instrumented
|
||||
with sanitizers and invoked as part of the test suite (if `-Dfuzz-tests=true`
|
||||
is configured). Thirdly, fuzzers are executed through fuzzing engines that try
|
||||
to find new "interesting" inputs through coverage feedback and massive
|
||||
parallelization; see the links for oss-fuzz in [Code quality](CODE_QUALITY).
|
||||
For testing and debugging, fuzzers can be executed as any other program,
|
||||
including under `valgrind` or `gdb`.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Sources in `test/TEST-*` implement system-level testing for executables,
|
||||
libraries and daemons that are shipped by the project. They require privileges
|
||||
to run, and are not safe to execute directly on a host. By default they will
|
||||
build an image and run the test under it via `qemu` or `systemd-nspawn`.
|
||||
|
||||
Most of those tests should be able to run via `systemd-nspawn`, which is
|
||||
orders-of-magnitude faster than `qemu`, but some tests require privileged
|
||||
operations like using `dm-crypt` or `loopdev`. They are clearly marked if that
|
||||
is the case.
|
||||
|
||||
See `test/README.testsuite` for more specific details.
|
||||
|
||||
## hwdb
|
||||
|
||||
Rules built in the static hardware database shipped by the project can be found
|
||||
under `hwdb.d/`. Some of these files are updated automatically, some are filled
|
||||
by contributors.
|
||||
|
||||
## Documentation
|
||||
|
||||
### systemd.io
|
||||
|
||||
Markdown files found under `docs/` are automatically published on the
|
||||
[systemd.io](https://systemd.io) website using Github Pages. A minimal unit test
|
||||
to ensure the formatting doesn't have errors is included in the
|
||||
`meson test -C build/ github-pages` run as part of the CI.
|
||||
|
||||
### Man pages
|
||||
|
||||
Manpages for binaries and libraries, and the DBUS interfaces, can be found under
|
||||
`man/` and should ideally be kept in sync with changes to the corresponding
|
||||
binaries and libraries.
|
||||
|
||||
### Translations
|
||||
|
||||
Translations files for binaries and daemons, provided by volunteers, can be found
|
||||
under `po/` in the usual format. They are kept up to date by contributors and by
|
||||
automated tools.
|
||||
|
||||
## System Configuration files and presets
|
||||
|
||||
Presets (or templates from which they are generated) for various daemons and tools
|
||||
can be found under various directories such as `factory/`, `modprobe.d/`, `network/`,
|
||||
`presets/`, `rules.d/`, `shell-completion/`, `sysctl.d/`, `sysusers.d/`, `tmpfiles.d/`.
|
||||
|
||||
## Utilities for Developers
|
||||
|
||||
`tools/`, `coccinelle/`, `.github/`, `.semaphore/`, `.mkosi/` host various
|
||||
utilities and scripts that are used by maintainers and developers. They are not
|
||||
shipped or installed.
|
||||
|
||||
# Service Manager Overview
|
||||
|
||||
The Service Manager takes configuration in the form of unit files, credentials,
|
||||
kernel command line options and D-Bus commands, and based on those manages the
|
||||
system and spawns other processes. It runs in system mode as PID1, and in user
|
||||
mode with one instance per user session.
|
||||
|
||||
When starting a unit requires forking a new process, configuration for the new
|
||||
process will be serialized and passed over to the new process, created via a
|
||||
posix_spawn() call. This is done in order to avoid excessive processing after
|
||||
a fork() but before an exec(), which is against glibc's best practices and can
|
||||
also result in a copy-on-write trap. The new process will start as the
|
||||
`systemd-executor` binary, which will deserialize the configuration and apply
|
||||
all the options (sandboxing, namespacing, cgroup, etc.) before exec'ing the
|
||||
configured executable.
|
||||
|
||||
```
|
||||
┌──────┐posix_spawn() ┌───────────┐execve() ┌────────┐
|
||||
│ PID1 ├─────────────►│sd-executor├────────►│program │
|
||||
└──────┘ (memfd) └───────────┘ └────────┘
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: systemd Community Conduct Guidelines
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# The systemd Community Conduct Guidelines
|
||||
|
||||
This document provides community guidelines for a safe, respectful, productive, and collaborative place for any person who is willing to contribute to systemd. It applies to all “collaborative spaces”, which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.).
|
||||
|
||||
- Participants will be tolerant of opposing views.
|
||||
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
||||
- When interpreting the words and actions of others, participants should always assume good intentions.
|
||||
- Behaviour which can be reasonably considered harassment will not be tolerated.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at systemd-conduct@googlegroups.com. This team currently consists of David Strauss <<systemd-conduct@davidstrauss.net>>, Ekaterina Gerasimova (Kat) <<Kittykat3756@gmail.com>>, and Zbigniew Jędrzejewski-Szmek <<zbyszek@in.waw.pl>>. In the unfortunate event that you wish to make a complaint against one of the members, you may instead contact any of the other members individually.
|
||||
|
||||
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Code Quality Tools
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Code Quality Tools
|
||||
|
||||
The systemd project has a number of code quality tools set up in the source
|
||||
tree and on the github infrastructure. Here's an incomprehensive list of the
|
||||
available functionality:
|
||||
|
||||
1. Use `meson test -C build` to run the unit tests. Some tests are skipped if
|
||||
no privileges are available, hence consider also running them with `sudo
|
||||
meson test -C build`. A couple of unit tests are considered "unsafe" (as
|
||||
they change system state); to run those too, build with `meson setup
|
||||
-Dtests=unsafe`. Finally, some unit tests are considered to be very slow,
|
||||
build them too with `meson setup -Dslow-tests=true`. (Note that there are a
|
||||
couple of manual tests in addition to these unit tests.) (Also note: you can
|
||||
change these flags for an already set up build tree, too, with "meson
|
||||
configure -C build -D…".)
|
||||
|
||||
2. Use `./test/run-integration-tests.sh` to run the full integration test
|
||||
suite. This will build OS images with a number of integration tests and run
|
||||
them using `systemd-nspawn` and `qemu`. Requires root.
|
||||
|
||||
3. Use `./coccinelle/run-coccinelle.sh` to run all
|
||||
[Coccinelle](http://coccinelle.lip6.fr/) semantic patch scripts we ship. The
|
||||
output will show false positives, hence take it with a pinch of salt.
|
||||
|
||||
4. Use `./tools/find-double-newline.sh recdiff` to find double newlines. Use
|
||||
`./tools/find-double-newline.sh recpatch` to fix them. Take this with a grain
|
||||
of salt, in particular as we generally leave foreign header files we include in
|
||||
our tree unmodified, if possible.
|
||||
|
||||
5. Similar use `./tools/find-tabs.sh recdiff` to find TABs, and
|
||||
`./tools/find-tabs.sh recpatch` to fix them. (Again, grain of salt, foreign
|
||||
headers should usually be left unmodified.)
|
||||
|
||||
6. Use `ninja -C build check-api-docs` to compare the list of exported symbols
|
||||
of `libsystemd.so` and `libudev.so` with the list of man pages. Symbols
|
||||
lacking documentation are highlighted.
|
||||
|
||||
7. Use `ninja -C build update-hwdb` and `ninja -C build update-hwdb-autosuspend`
|
||||
to automatically download and import the PCI, USB, and OUI databases and the
|
||||
autosuspend quirks into the hwdb.
|
||||
|
||||
8. Use `ninja -C build update-man-rules` to update the meson rules for building
|
||||
man pages automatically from the docbook XML files included in `man/`.
|
||||
|
||||
9. There are multiple CI systems in use that run on every github pull request
|
||||
submission or update.
|
||||
|
||||
10. [Coverity](https://scan.coverity.com/) is analyzing systemd `main` branch
|
||||
in regular intervals. The reports are available
|
||||
[online](https://scan.coverity.com/projects/systemd).
|
||||
|
||||
11. [OSS-Fuzz](https://github.com/google/oss-fuzz) is continuously fuzzing the
|
||||
codebase. Reports are available
|
||||
[online](https://oss-fuzz.com/testcases?project=systemd&open=yes).
|
||||
It also builds
|
||||
[coverage reports](https://oss-fuzz.com/coverage-report/job/libfuzzer_asan_systemd/latest)
|
||||
daily.
|
||||
|
||||
12. Our tree includes `.editorconfig`, `.dir-locals.el` and `.vimrc` files, to
|
||||
ensure that editors follow the right indentiation styles automatically.
|
||||
|
||||
13. When building systemd from a git checkout the build scripts will
|
||||
automatically enable a git commit hook that ensures whitespace cleanliness.
|
||||
|
||||
14. [CodeQL](https://codeql.github.com/) analyzes each PR and every commit
|
||||
pushed to `main`. The list of active alerts can be found
|
||||
[here](https://github.com/systemd/systemd/security/code-scanning).
|
||||
|
||||
15. Each PR is automatically tested with [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
|
||||
and [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
|
||||
See [Testing systemd using sanitizers](TESTING_WITH_SANITIZERS)
|
||||
for more information.
|
||||
|
||||
16. Fossies provides [source code misspelling reports](https://fossies.org/features.html#codespell).
|
||||
The systemd report can be found [here](https://fossies.org/linux/misc/systemd/codespell.html).
|
||||
|
||||
Access to Coverity and oss-fuzz reports is limited. Please reach out to the
|
||||
maintainers if you need access.
|
||||
@@ -0,0 +1,782 @@
|
||||
---
|
||||
title: Coding Style
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Coding Style
|
||||
|
||||
## Formatting
|
||||
|
||||
- 8ch indent, no tabs, except for files in `man/` which are 2ch indent, and
|
||||
still no tabs, and shell scripts, which are 4ch indent, and no tabs either.
|
||||
|
||||
- We prefer `/* comments */` over `// comments` in code you commit,
|
||||
please. This way `// comments` are left for developers to use for local,
|
||||
temporary commenting of code for debug purposes (i.e. uncommittable stuff),
|
||||
making such comments easily discernible from explanatory, documenting code
|
||||
comments (i.e. committable stuff).
|
||||
|
||||
- Don't break code lines too eagerly. We do **not** force line breaks at 80ch,
|
||||
all of today's screens should be much larger than that. But then again, don't
|
||||
overdo it, ~109ch should be enough really. The `.editorconfig`, `.vimrc` and
|
||||
`.dir-locals.el` files contained in the repository will set this limit up for
|
||||
you automatically, if you let them (as well as a few other things). Please
|
||||
note that emacs loads `.dir-locals.el` automatically, but vim needs to be
|
||||
configured to load `.vimrc`, see that file for instructions.
|
||||
|
||||
- If you break a function declaration over multiple lines, do it like this:
|
||||
|
||||
```c
|
||||
void some_function(
|
||||
int foo,
|
||||
bool bar,
|
||||
char baz) {
|
||||
|
||||
int a, b, c;
|
||||
```
|
||||
|
||||
(i.e. use double indentation — 16 spaces — for the parameter list.)
|
||||
|
||||
- Try to write this:
|
||||
|
||||
```c
|
||||
void foo() {
|
||||
}
|
||||
```
|
||||
|
||||
instead of this:
|
||||
|
||||
```c
|
||||
void foo()
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
- Single-line `if` blocks should not be enclosed in `{}`. Write this:
|
||||
|
||||
```c
|
||||
if (foobar)
|
||||
waldo();
|
||||
```
|
||||
|
||||
instead of this:
|
||||
|
||||
```c
|
||||
if (foobar) {
|
||||
waldo();
|
||||
}
|
||||
```
|
||||
|
||||
- Do not write `foo ()`, write `foo()`.
|
||||
|
||||
- `else` blocks should generally start on the same line as the closing `}`:
|
||||
```c
|
||||
if (foobar) {
|
||||
find();
|
||||
waldo();
|
||||
} else
|
||||
dont_find_waldo();
|
||||
```
|
||||
|
||||
- Please define flags types like this:
|
||||
|
||||
```c
|
||||
typedef enum FoobarFlags {
|
||||
FOOBAR_QUUX = 1 << 0,
|
||||
FOOBAR_WALDO = 1 << 1,
|
||||
FOOBAR_XOXO = 1 << 2,
|
||||
…
|
||||
} FoobarFlags;
|
||||
```
|
||||
|
||||
i.e. use an enum for it, if possible. Indicate bit values via `1 <<`
|
||||
expressions, and align them vertically. Define both an enum and a type for
|
||||
it.
|
||||
|
||||
- If you define (non-flags) enums, follow this template:
|
||||
|
||||
```c
|
||||
typedef enum FoobarMode {
|
||||
FOOBAR_AAA,
|
||||
FOOBAR_BBB,
|
||||
FOOBAR_CCC,
|
||||
…
|
||||
_FOOBAR_MAX,
|
||||
_FOOBAR_INVALID = -EINVAL,
|
||||
} FoobarMode;
|
||||
```
|
||||
|
||||
i.e. define a `_MAX` enum for the largest defined enum value, plus one. Since
|
||||
this is not a regular enum value, prefix it with `_`. Also, define a special
|
||||
"invalid" enum value, and set it to `-EINVAL`. That way the enum type can
|
||||
safely be used to propagate conversion errors.
|
||||
|
||||
- If you define an enum in a public API, be extra careful, as the size of the
|
||||
enum might change when new values are added, which would break ABI
|
||||
compatibility. Since we typically want to allow adding new enum values to an
|
||||
existing enum type with later API versions, please use the
|
||||
`_SD_ENUM_FORCE_S64()` macro in the enum definition, which forces the size of
|
||||
the enum to be signed 64-bit wide.
|
||||
|
||||
- Empty lines to separate code blocks are a good thing, please add them
|
||||
abundantly. However, please stick to one at a time, i.e. multiple empty lines
|
||||
immediately following each other are not OK. Also, we try to keep function
|
||||
calls and their immediate error handling together. Hence:
|
||||
|
||||
```c
|
||||
/* → empty line here is good */
|
||||
r = some_function(…);
|
||||
/* → empty line here would be bad */
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Some function failed: %m");
|
||||
/* → empty line here is good */
|
||||
```
|
||||
|
||||
- In shell scripts, do not use whitespace after the redirection operator
|
||||
(`>some/file` instead of `> some/file`, `<<EOF` instead of `<< EOF`).
|
||||
|
||||
## Code Organization and Semantics
|
||||
|
||||
- For our codebase we intend to use ISO C11 *with* GNU extensions (aka
|
||||
"gnu11"). Public APIs (i.e. those we expose via `libsystemd.so`
|
||||
i.e. `systemd/sd-*.h`) should only use ISO C89 however (with a very limited
|
||||
set of conservative and common extensions, such as fixed size integer types
|
||||
from `<inttypes.h>`), so that we don't force consuming programs into C11
|
||||
mode. (This discrepancy in particular means one thing: internally we use C99
|
||||
`bool` booleans, externally C89-compatible `int` booleans which generally
|
||||
have different size in memory and slightly different semantics, also see
|
||||
below.) Both for internal and external code it's OK to use even newer
|
||||
features and GCC extension than "gnu11", as long as there's reasonable
|
||||
fallback #ifdeffery in place to ensure compatibility is retained with older
|
||||
compilers.
|
||||
|
||||
- Please name structures in `PascalCase` (with exceptions, such as public API
|
||||
structs), variables and functions in `snake_case`.
|
||||
|
||||
- Avoid static variables, except for caches and very few other cases. Think
|
||||
about thread-safety! While most of our code is never used in threaded
|
||||
environments, at least the library code should make sure it works correctly
|
||||
in them. Instead of doing a lot of locking for that, we tend to prefer using
|
||||
TLS to do per-thread caching (which only works for small, fixed-size cache
|
||||
objects), or we disable caching for any thread that is not the main
|
||||
thread. Use `is_main_thread()` to detect whether the calling thread is the
|
||||
main thread.
|
||||
|
||||
- Do not write functions that clobber call-by-reference variables on
|
||||
failure. Use temporary variables for these cases and change the passed in
|
||||
variables only on success. The rule is: never clobber return parameters on
|
||||
failure, always initialize return parameters on success.
|
||||
|
||||
- Typically, function parameters fit into three categories: input parameters,
|
||||
mutable objects, and call-by-reference return parameters. Input parameters
|
||||
should always carry suitable "const" declarators if they are pointers, to
|
||||
indicate they are input-only and not changed by the function. Return
|
||||
parameters are best prefixed with "ret_", to clarify they are return
|
||||
parameters. (Conversely, please do not prefix parameters that aren't
|
||||
output-only with "ret_", in particular not mutable parameters that are both
|
||||
input as well as output). Example:
|
||||
|
||||
```c
|
||||
static int foobar_frobnicate(
|
||||
Foobar* object, /* the associated mutable object */
|
||||
const char *input, /* immutable input parameter */
|
||||
char **ret_frobnicated) { /* return parameter */
|
||||
…
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
- The order in which header files are included doesn't matter too
|
||||
much. systemd-internal headers must not rely on an include order, so it is
|
||||
safe to include them in any order possible. However, to not clutter global
|
||||
includes, and to make sure internal definitions will not affect global
|
||||
headers, please always include the headers of external components first
|
||||
(these are all headers enclosed in <>), followed by our own exported headers
|
||||
(usually everything that's prefixed by `sd-`), and then followed by internal
|
||||
headers. Furthermore, in all three groups, order all includes alphabetically
|
||||
so duplicate includes can easily be detected.
|
||||
|
||||
- Please avoid using global variables as much as you can. And if you do use
|
||||
them make sure they are static at least, instead of exported. Especially in
|
||||
library-like code it is important to avoid global variables. Why are global
|
||||
variables bad? They usually hinder generic reusability of code (since they
|
||||
break in threaded programs, and usually would require locking there), and as
|
||||
the code using them has side-effects make programs non-transparent. That
|
||||
said, there are many cases where they explicitly make a lot of sense, and are
|
||||
OK to use. For example, the log level and target in `log.c` is stored in a
|
||||
global variable, and that's OK and probably expected by most. Also in many
|
||||
cases we cache data in global variables. If you add more caches like this,
|
||||
please be careful however, and think about threading. Only use static
|
||||
variables if you are sure that thread-safety doesn't matter in your
|
||||
case. Alternatively, consider using TLS, which is pretty easy to use with
|
||||
gcc's `thread_local` concept. It's also OK to store data that is inherently
|
||||
global in global variables, for example, data parsed from command lines, see
|
||||
below.
|
||||
|
||||
- Our focus is on the GNU libc (glibc), not any other libcs. If other libcs are
|
||||
incompatible with glibc it's on them. However, if there are equivalent POSIX
|
||||
and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there
|
||||
aren't, we are happy to use GNU or Linux APIs, and expect non-GNU
|
||||
implementations of libc to catch up with glibc.
|
||||
|
||||
## Using C Constructs
|
||||
|
||||
- Allocate local variables where it makes sense: at the top of the block, or at
|
||||
the point where they can be initialized. Avoid huge variable declaration
|
||||
lists at the top of the function.
|
||||
|
||||
As an exception, `int r` is typically used for a local state variable, but
|
||||
should almost always be declared as the last variable at the top of the
|
||||
function.
|
||||
|
||||
```c
|
||||
{
|
||||
uint64_t a;
|
||||
int r;
|
||||
|
||||
r = frobnicate(&a);
|
||||
if (r < 0)
|
||||
…
|
||||
|
||||
uint64_t b = a + 1, c;
|
||||
|
||||
r = foobarify(a, b, &c);
|
||||
if (r < 0)
|
||||
…
|
||||
|
||||
const char *pretty = prettify(a, b, c);
|
||||
…
|
||||
}
|
||||
```
|
||||
|
||||
- Do not mix multiple variable definitions with function invocations or
|
||||
complicated expressions:
|
||||
|
||||
```c
|
||||
{
|
||||
uint64_t x = 7;
|
||||
int a;
|
||||
|
||||
a = foobar();
|
||||
}
|
||||
```
|
||||
|
||||
instead of:
|
||||
|
||||
```c
|
||||
{
|
||||
int a = foobar();
|
||||
uint64_t x = 7;
|
||||
}
|
||||
```
|
||||
|
||||
- Use `goto` for cleaning up, and only use it for that. I.e. you may only jump
|
||||
to the end of a function, and little else. Never jump backwards!
|
||||
|
||||
- To minimize strict aliasing violations, we prefer unions over casting.
|
||||
|
||||
- Instead of using `memzero()`/`memset()` to initialize structs allocated on
|
||||
the stack, please try to use c99 structure initializers. It's short, prettier
|
||||
and actually even faster at execution. Hence:
|
||||
|
||||
```c
|
||||
struct foobar t = {
|
||||
.foo = 7,
|
||||
.bar = "bazz",
|
||||
};
|
||||
```
|
||||
|
||||
instead of:
|
||||
|
||||
```c
|
||||
struct foobar t;
|
||||
zero(t);
|
||||
t.foo = 7;
|
||||
t.bar = "bazz";
|
||||
```
|
||||
|
||||
- To implement an endless loop, use `for (;;)` rather than `while (1)`. The
|
||||
latter is a bit ugly anyway, since you probably really meant `while
|
||||
(true)`. To avoid the discussion what the right always-true expression for an
|
||||
infinite while loop is, our recommendation is to simply write it without any
|
||||
such expression by using `for (;;)`.
|
||||
|
||||
- To determine the length of a constant string `"foo"`, don't bother with
|
||||
`sizeof("foo")-1`, please use `strlen()` instead (both gcc and clang optimize
|
||||
the call away for fixed strings). The only exception is when declaring an
|
||||
array. In that case use `STRLEN()`, which evaluates to a static constant and
|
||||
doesn't force the compiler to create a VLA.
|
||||
|
||||
- Please use C's downgrade-to-bool feature only for expressions that are
|
||||
actually booleans (or "boolean-like"), and not for variables that are really
|
||||
numeric. Specifically, if you have an `int b` and it's only used in a boolean
|
||||
sense, by all means check its state with `if (b) …` — but if `b` can actually
|
||||
have more than two semantic values, and you want to compare for non-zero,
|
||||
then please write that explicitly with `if (b != 0) …`. This helps readability
|
||||
as the value range and semantical behaviour is directly clear from the
|
||||
condition check. As a special addition: when dealing with pointers which you
|
||||
want to check for non-NULL-ness, you may also use downgrade-to-bool feature.
|
||||
|
||||
- Please do not use yoda comparisons, i.e. please prefer the more readable `if
|
||||
(a == 7)` over the less readable `if (7 == a)`.
|
||||
|
||||
## Destructors
|
||||
|
||||
- The destructors always deregister the object from the next bigger object, not
|
||||
the other way around.
|
||||
|
||||
- For robustness reasons, destructors should be able to destruct
|
||||
half-initialized objects, too.
|
||||
|
||||
- When you define a destructor or `unref()` call for an object, please accept a
|
||||
`NULL` object and simply treat this as NOP. This is similar to how libc
|
||||
`free()` works, which accepts `NULL` pointers and becomes a NOP for them. By
|
||||
following this scheme a lot of `if` checks can be removed before invoking
|
||||
your destructor, which makes the code substantially more readable and robust.
|
||||
|
||||
- Related to this: when you define a destructor or `unref()` call for an
|
||||
object, please make it return the same type it takes and always return `NULL`
|
||||
from it. This allows writing code like this:
|
||||
|
||||
```c
|
||||
p = foobar_unref(p);
|
||||
```
|
||||
|
||||
which will always work regardless if `p` is initialized or not, and
|
||||
guarantees that `p` is `NULL` afterwards, all in just one line.
|
||||
|
||||
## Common Function Naming
|
||||
|
||||
- Name destructor functions that destroy an object in full freeing all its
|
||||
memory and associated resources (and thus invalidating the pointer to it)
|
||||
`xyz_free()`. Example: `strv_free()`.
|
||||
|
||||
- Name destructor functions that destroy only the referenced content of an
|
||||
object but leave the object itself allocated `xyz_done()`. If it resets all
|
||||
fields so that the object can be reused later call it `xyz_clear()`.
|
||||
|
||||
- Functions that decrease the reference counter of an object by one should be
|
||||
called `xyz_unref()`. Example: `json_variant_unref()`. Functions that
|
||||
increase the reference counter by one should be called `xyz_ref()`. Example:
|
||||
`json_variant_ref()`
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Error codes are returned as negative `Exxx`. e.g. `return -EINVAL`. There are
|
||||
some exceptions: for constructors, it is OK to return `NULL` on OOM. For
|
||||
lookup functions, `NULL` is fine too for "not found".
|
||||
|
||||
Be strict with this. When you write a function that can fail due to more than
|
||||
one cause, it *really* should have an `int` as the return value for the error
|
||||
code.
|
||||
|
||||
- libc system calls typically return -1 on error (with the error code in
|
||||
`errno`), and >= 0 on success. Use the RET_NERRNO() helper if you are looking
|
||||
for a simple way to convert this libc style error returning into systemd
|
||||
style error returning. e.g.
|
||||
|
||||
```c
|
||||
…
|
||||
r = RET_NERRNO(unlink(t));
|
||||
…
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```c
|
||||
…
|
||||
r = RET_NERRNO(open("/some/file", O_RDONLY|O_CLOEXEC));
|
||||
…
|
||||
```
|
||||
|
||||
- Do not bother with error checking whether writing to stdout/stderr worked.
|
||||
|
||||
- Do not log errors from "library" code, only do so from "main program"
|
||||
code. (With one exception: it is OK to log with DEBUG level from any code,
|
||||
with the exception of maybe inner loops).
|
||||
|
||||
- In public API calls, you **must** validate all your input arguments for
|
||||
programming error with `assert_return()` and return a sensible return
|
||||
code. In all other calls, it is recommended to check for programming errors
|
||||
with a more brutal `assert()`. We are more forgiving to public users than for
|
||||
ourselves! Note that `assert()` and `assert_return()` really only should be
|
||||
used for detecting programming errors, not for runtime errors. `assert()` and
|
||||
`assert_return()` by usage of `_likely_()` inform the compiler that it should
|
||||
not expect these checks to fail, and they inform fellow programmers about the
|
||||
expected validity and range of parameters.
|
||||
|
||||
- When you invoke certain calls like `unlink()`, or `mkdir_p()` and you know it
|
||||
is safe to ignore the error it might return (because a later call would
|
||||
detect the failure anyway, or because the error is in an error path and you
|
||||
thus couldn't do anything about it anyway), then make this clear by casting
|
||||
the invocation explicitly to `(void)`. Code checks like Coverity understand
|
||||
that, and will not complain about ignored error codes. Hence, please use
|
||||
this:
|
||||
|
||||
```c
|
||||
(void) unlink("/foo/bar/baz");
|
||||
```
|
||||
|
||||
instead of just this:
|
||||
|
||||
```c
|
||||
unlink("/foo/bar/baz");
|
||||
```
|
||||
|
||||
When returning from a `void` function, you may also want to shorten the error
|
||||
path boilerplate by returning a function invocation cast to `(void)` like so:
|
||||
|
||||
```c
|
||||
if (condition_not_met)
|
||||
return (void) log_tests_skipped("Cannot run ...");
|
||||
```
|
||||
|
||||
Don't cast function calls to `(void)` that return no error
|
||||
conditions. Specifically, the various `xyz_unref()` calls that return a
|
||||
`NULL` object shouldn't be cast to `(void)`, since not using the return value
|
||||
does not hide any errors.
|
||||
|
||||
- When returning a return code from `main()`, please preferably use
|
||||
`EXIT_FAILURE` and `EXIT_SUCCESS` as defined by libc.
|
||||
|
||||
## Logging
|
||||
|
||||
- For every function you add, think about whether it is a "logging" function or
|
||||
a "non-logging" function. "Logging" functions do (non-debug) logging on their
|
||||
own, "non-logging" functions never log on their own (except at debug level)
|
||||
and expect their callers to log. All functions in "library" code, i.e. in
|
||||
`src/shared/` and suchlike must be "non-logging". Every time a "logging"
|
||||
function calls a "non-logging" function, it should log about the resulting
|
||||
errors. If a "logging" function calls another "logging" function, then it
|
||||
should not generate log messages, so that log messages are not generated
|
||||
twice for the same errors. (Note that debug level logging — at syslog level
|
||||
`LOG_DEBUG` — is not considered logging in this context, debug logging is
|
||||
generally always fine and welcome.)
|
||||
|
||||
- If possible, do a combined log & return operation:
|
||||
|
||||
```c
|
||||
r = operation(...);
|
||||
if (r < 0)
|
||||
return log_(error|warning|notice|...)_errno(r, "Failed to ...: %m");
|
||||
```
|
||||
|
||||
If the error value is "synthetic", i.e. it was not received from
|
||||
the called function, use `SYNTHETIC_ERRNO` wrapper to tell the logging
|
||||
system to not log the errno value, but still return it:
|
||||
|
||||
```c
|
||||
n = read(..., s, sizeof s);
|
||||
if (n != sizeof s)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read ...");
|
||||
```
|
||||
|
||||
## Memory Allocation
|
||||
|
||||
- Always check OOM. There is no excuse. In program code, you can use
|
||||
`log_oom()` for then printing a short message, but not in "library" code.
|
||||
|
||||
- Avoid fixed-size string buffers, unless you really know the maximum size and
|
||||
that maximum size is small. It is often nicer to use dynamic memory,
|
||||
`alloca_safe()` or VLAs. If you do allocate fixed-size strings on the stack,
|
||||
then it is probably only OK if you either use a maximum size such as
|
||||
`LINE_MAX`, or count in detail the maximum size a string can
|
||||
have. (`DECIMAL_STR_MAX` and `DECIMAL_STR_WIDTH` macros are your friends for
|
||||
this!)
|
||||
|
||||
Or in other words, if you use `char buf[256]` then you are likely doing
|
||||
something wrong!
|
||||
|
||||
- Make use of `_cleanup_free_` and friends. It makes your code much nicer to
|
||||
read (and shorter)!
|
||||
|
||||
- Do not use `alloca()`, `strdupa()` or `strndupa()` directly. Use
|
||||
`alloca_safe()`, `strdupa_safe()` or `strndupa_safe()` instead. (The
|
||||
difference is that the latter include an assertion that the specified size is
|
||||
below a safety threshold, so that the program rather aborts than runs into
|
||||
possible stack overruns.)
|
||||
|
||||
- Use `alloca_safe()`, but never forget that it is not OK to invoke
|
||||
`alloca_safe()` within a loop or within function call
|
||||
parameters. `alloca_safe()` memory is released at the end of a function, and
|
||||
not at the end of a `{}` block. Thus, if you invoke it in a loop, you keep
|
||||
increasing the stack pointer without ever releasing memory again. (VLAs have
|
||||
better behavior in this case, so consider using them as an alternative.)
|
||||
Regarding not using `alloca_safe()` within function parameters, see the BUGS
|
||||
section of the `alloca(3)` man page.
|
||||
|
||||
- If you want to concatenate two or more strings, consider using `strjoina()`
|
||||
or `strjoin()` rather than `asprintf()`, as the latter is a lot slower. This
|
||||
matters particularly in inner loops (but note that `strjoina()` cannot be
|
||||
used there).
|
||||
|
||||
## Runtime Behaviour
|
||||
|
||||
- Avoid leaving long-running child processes around, i.e. `fork()`s that are
|
||||
not followed quickly by an `execv()` in the child. Resource management is
|
||||
unclear in this case, and memory CoW will result in unexpected penalties in
|
||||
the parent much, much later on.
|
||||
|
||||
- Don't block execution for arbitrary amounts of time using `usleep()` or a
|
||||
similar call, unless you really know what you do. Just "giving something some
|
||||
time", or so is a lazy excuse. Always wait for the proper event, instead of
|
||||
doing time-based poll loops.
|
||||
|
||||
- Whenever installing a signal handler, make sure to set `SA_RESTART` for it,
|
||||
so that interrupted system calls are automatically restarted, and we minimize
|
||||
hassles with handling `EINTR` (in particular as `EINTR` handling is pretty
|
||||
broken on Linux).
|
||||
|
||||
- When applying C-style unescaping as well as specifier expansion on the same
|
||||
string, always apply the C-style unescaping first, followed by the specifier
|
||||
expansion. When doing the reverse, make sure to escape `%` in specifier-style
|
||||
first (i.e. `%` → `%%`), and then do C-style escaping where necessary.
|
||||
|
||||
- Be exceptionally careful when formatting and parsing floating point
|
||||
numbers. Their syntax is locale dependent (i.e. `5.000` in en_US is generally
|
||||
understood as 5, while in de_DE as 5000.).
|
||||
|
||||
- Make sure to enforce limits on every user controllable resource. If the user
|
||||
can allocate resources in your code, your code must enforce some form of
|
||||
limits after which it will refuse operation. It's fine if it is hard-coded
|
||||
(at least initially), but it needs to be there. This is particularly
|
||||
important for objects that unprivileged users may allocate, but also matters
|
||||
for everything else any user may allocate.
|
||||
|
||||
## Types
|
||||
|
||||
- Think about the types you use. If a value cannot sensibly be negative, do not
|
||||
use `int`, but use `unsigned`. We prefer `unsigned` form to `unsigned int`.
|
||||
|
||||
- Use `char` only for actual characters. Use `uint8_t` or `int8_t` when you
|
||||
actually mean a byte-sized signed or unsigned integers. When referring to a
|
||||
generic byte, we generally prefer the unsigned variant `uint8_t`. Do not use
|
||||
types based on `short`. They *never* make sense. Use `int`, `long`, `long
|
||||
long`, all in unsigned and signed fashion, and the fixed-size types
|
||||
`uint8_t`, `uint16_t`, `uint32_t`, `uint64_t`, `int8_t`, `int16_t`, `int32_t`
|
||||
and so on, as well as `size_t`, but nothing else. Do not use kernel types
|
||||
like `u32` and so on, leave that to the kernel.
|
||||
|
||||
- Stay uniform. For example, always use `usec_t` for time values. Do not mix
|
||||
`usec` and `msec`, and `usec` and whatnot.
|
||||
|
||||
- Never use the `off_t` type, and particularly avoid it in public APIs. It's
|
||||
really weirdly defined, as it usually is 64-bit and we don't support it any
|
||||
other way, but it could in theory also be 32-bit. Which one it is depends on
|
||||
a compiler switch chosen by the compiled program, which hence corrupts APIs
|
||||
using it unless they can also follow the program's choice. Moreover, in
|
||||
systemd we should parse values the same way on all architectures and cannot
|
||||
expose `off_t` values over D-Bus. To avoid any confusion regarding conversion
|
||||
and ABIs, always use simply `uint64_t` directly.
|
||||
|
||||
- Unless you allocate an array, `double` is always a better choice than
|
||||
`float`. Processors speak `double` natively anyway, so there is no speed
|
||||
benefit, and on calls like `printf()` `float`s get promoted to `double`s
|
||||
anyway, so there is no point.
|
||||
|
||||
- Use the bool type for booleans, not integers. One exception: in public
|
||||
headers (i.e those in `src/systemd/sd-*.h`) use integers after all, as `bool`
|
||||
is C99 and in our public APIs we try to stick to C89 (with a few extensions;
|
||||
also see above).
|
||||
|
||||
## Deadlocks
|
||||
|
||||
- Do not issue NSS requests (that includes user name and hostname lookups)
|
||||
from PID 1 as this might trigger deadlocks when those lookups involve
|
||||
synchronously talking to services that we would need to start up.
|
||||
|
||||
- Do not synchronously talk to any other service from PID 1, due to risk of
|
||||
deadlocks.
|
||||
|
||||
## File Descriptors
|
||||
|
||||
- When you allocate a file descriptor, it should be made `O_CLOEXEC` right from
|
||||
the beginning, as none of our files should leak to forked binaries by
|
||||
default. Hence, whenever you open a file, `O_CLOEXEC` must be specified,
|
||||
right from the beginning. This also applies to sockets. Effectively, this
|
||||
means that all invocations to:
|
||||
|
||||
- `open()` must get `O_CLOEXEC` passed,
|
||||
- `socket()` and `socketpair()` must get `SOCK_CLOEXEC` passed,
|
||||
- `recvmsg()` must get `MSG_CMSG_CLOEXEC` set,
|
||||
- `F_DUPFD_CLOEXEC` should be used instead of `F_DUPFD`, and so on,
|
||||
- invocations of `fopen()` should take `e`.
|
||||
|
||||
- It's a good idea to use `O_NONBLOCK` when opening 'foreign' regular files,
|
||||
i.e. file system objects that are supposed to be regular files whose paths
|
||||
were specified by the user and hence might actually refer to other types of
|
||||
file system objects. This is a good idea so that we don't end up blocking on
|
||||
'strange' file nodes, for example, if the user pointed us to a FIFO or device
|
||||
node which may block when opening. Moreover even for actual regular files
|
||||
`O_NONBLOCK` has a benefit: it bypasses any mandatory lock that might be in
|
||||
effect on the regular file. If in doubt consider turning off `O_NONBLOCK`
|
||||
again after opening.
|
||||
|
||||
- These days we generally prefer `openat()`-style file APIs, i.e. APIs that
|
||||
accept a combination of file descriptor and path string, and where the path
|
||||
(if not absolute) is considered relative to the specified file
|
||||
descriptor. When implementing library calls in similar style, please make
|
||||
sure to imply `AT_EMPTY_PATH` if an empty or `NULL` path argument is
|
||||
specified (and convert that latter to an empty string). This differs from the
|
||||
underlying kernel semantics, where `AT_EMPTY_PATH` must always be specified
|
||||
explicitly, and `NULL` is not accepted as path.
|
||||
|
||||
## Command Line
|
||||
|
||||
- If you parse a command line, and want to store the parsed parameters in
|
||||
global variables, please consider prefixing their names with `arg_`. We have
|
||||
been following this naming rule in most of our tools, and we should continue
|
||||
to do so, as it makes it easy to identify command line parameter variables,
|
||||
and makes it clear why it is OK that they are global variables.
|
||||
|
||||
- Command line option parsing:
|
||||
- Do not print full `help()` on error, be specific about the error.
|
||||
- Do not print messages to stdout on error.
|
||||
- Do not POSIX_ME_HARDER unless necessary, i.e. avoid `+` in option string.
|
||||
|
||||
## Exporting Symbols
|
||||
|
||||
- Variables and functions **must** be static, unless they have a prototype, and
|
||||
are supposed to be exported.
|
||||
|
||||
- Public API calls (i.e. functions exported by our shared libraries)
|
||||
must be marked `_public_` and need to be prefixed with `sd_`. No
|
||||
other functions should be prefixed like that.
|
||||
|
||||
- When exposing public C APIs, be careful what function parameters you make
|
||||
`const`. For example, a parameter taking a context object should probably not
|
||||
be `const`, even if you are writing an otherwise read-only accessor function
|
||||
for it. The reason is that making it `const` fixates the contract that your
|
||||
call won't alter the object ever, as part of the API. However, that's often
|
||||
quite a promise, given that this even prohibits object-internal caching or
|
||||
lazy initialization of object variables. Moreover, it's usually not too
|
||||
useful for client applications. Hence, please be careful and avoid `const` on
|
||||
object parameters, unless you are very sure `const` is appropriate.
|
||||
|
||||
## Referencing Concepts
|
||||
|
||||
- When referring to a configuration file option in the documentation and such,
|
||||
please always suffix it with `=`, to indicate that it is a configuration file
|
||||
setting.
|
||||
|
||||
- When referring to a command line option in the documentation and such, please
|
||||
always prefix with `--` or `-` (as appropriate), to indicate that it is a
|
||||
command line option.
|
||||
|
||||
- When referring to a file system path that is a directory, please always
|
||||
suffix it with `/`, to indicate that it is a directory, not a regular file
|
||||
(or other file system object).
|
||||
|
||||
## Functions to Avoid
|
||||
|
||||
- Use `memzero()` or even better `zero()` instead of `memset(..., 0, ...)`
|
||||
|
||||
- Please use `streq()` and `strneq()` instead of `strcmp()`, `strncmp()` where
|
||||
applicable (i.e. wherever you just care about equality/inequality, not about
|
||||
the sorting order).
|
||||
|
||||
- Never use `strtol()`, `atoi()` and similar calls. Use `safe_atoli()`,
|
||||
`safe_atou32()` and suchlike instead. They are much nicer to use in most
|
||||
cases and correctly check for parsing errors.
|
||||
|
||||
- `htonl()`/`ntohl()` and `htons()`/`ntohs()` are weird. Please use `htobe32()`
|
||||
and `htobe16()` instead, it's much more descriptive, and actually says what
|
||||
really is happening, after all `htonl()` and `htons()` don't operate on
|
||||
`long`s and `short`s as their name would suggest, but on `uint32_t` and
|
||||
`uint16_t`. Also, "network byte order" is just a weird name for "big endian",
|
||||
hence we might want to call it "big endian" right-away.
|
||||
|
||||
- Use `typesafe_inet_ntop()`, `typesafe_inet_ntop4()`, and
|
||||
`typesafe_inet_ntop6()` instead of `inet_ntop()`. But better yet, use the
|
||||
`IN_ADDR_TO_STRING()`, `IN4_ADDR_TO_STRING()`, and `IN6_ADDR_TO_STRING()`
|
||||
macros which allocate an anonymous buffer internally.
|
||||
|
||||
- Please never use `dup()`. Use `fcntl(fd, F_DUPFD_CLOEXEC, 3)` instead. For
|
||||
two reasons: first, you want `O_CLOEXEC` set on the new `fd` (see
|
||||
above). Second, `dup()` will happily duplicate your `fd` as 0, 1, 2,
|
||||
i.e. stdin, stdout, stderr, should those `fd`s be closed. Given the special
|
||||
semantics of those `fd`s, it's probably a good idea to avoid
|
||||
them. `F_DUPFD_CLOEXEC` with `3` as parameter avoids them.
|
||||
|
||||
- Don't use `fgets()`, it's too hard to properly handle errors such as overly
|
||||
long lines. Use `read_line()` instead, which is our own function that handles
|
||||
this much more nicely.
|
||||
|
||||
- Don't invoke `exit()`, ever. It is not replacement for proper error
|
||||
handling. Please escalate errors up your call chain, and use normal `return`
|
||||
to exit from the main function of a process. If you `fork()`ed off a child
|
||||
process, please use `_exit()` instead of `exit()`, so that the exit handlers
|
||||
are not run.
|
||||
|
||||
- Do not use `basename()` or `dirname()`. The semantics in corner cases are
|
||||
full of pitfalls, and the fact that there are two quite different versions of
|
||||
`basename()` (one POSIX and one GNU, of which the latter is much more useful)
|
||||
doesn't make it better either. Use path_extract_filename() and
|
||||
path_extract_directory() instead.
|
||||
|
||||
- Never use `FILENAME_MAX`. Use `PATH_MAX` instead (for checking maximum size
|
||||
of paths) and `NAME_MAX` (for checking maximum size of filenames).
|
||||
`FILENAME_MAX` is not POSIX, and is a confusingly named alias for `PATH_MAX`
|
||||
on Linux. Note that `NAME_MAX` does not include space for a trailing `NUL`,
|
||||
but `PATH_MAX` does. UNIX FTW!
|
||||
|
||||
## Committing to git
|
||||
|
||||
- Commit message subject lines should be prefixed with an appropriate component
|
||||
name of some kind. For example, "journal: ", "nspawn: " and so on.
|
||||
|
||||
- Do not use "Signed-Off-By:" in your commit messages. That's a kernel thing we
|
||||
don't do in the systemd project.
|
||||
|
||||
## Commenting
|
||||
|
||||
- The best place for code comments and explanations is in the code itself. Only
|
||||
the second best is in git commit messages. The worst place is in the GitHub
|
||||
PR cover letter. Hence, whenever you type a commit message consider for a
|
||||
moment if what you are typing there wouldn't be a better fit for an in-code
|
||||
comment. And if you type the cover letter of a PR, think hard if this
|
||||
wouldn't be better as a commit message or even code comment. Comments are
|
||||
supposed to be useful for somebody who reviews the code, and hence hiding
|
||||
comments in git commits or PR cover letters makes reviews unnecessarily
|
||||
hard. Moreover, while we rely heavily on GitHub's project management
|
||||
infrastructure we'd like to keep everything that can reasonably be kept in
|
||||
the git repository itself in the git repository, so that we can theoretically
|
||||
move things elsewhere with the least effort possible.
|
||||
|
||||
- It's OK to reference GitHub PRs, GitHub issues and git commits from code
|
||||
comments. Cross-referencing code, issues, and documentation is a good thing.
|
||||
|
||||
- Reasonable use of non-ASCII Unicode UTF-8 characters in code comments is
|
||||
welcome. If your code comment contains an emoji or two this will certainly
|
||||
brighten the day of the occasional reviewer of your code. Really! 😊
|
||||
|
||||
## Threading
|
||||
|
||||
- We generally avoid using threads, to the level this is possible. In
|
||||
particular in the service manager/PID 1 threads are not OK to use. This is
|
||||
because you cannot mix memory allocation in threads with use of glibc's
|
||||
`clone()` call, or manual `clone()`/`clone3()` system call wrappers. Only
|
||||
glibc's own `fork()` call will properly synchronize the memory allocation
|
||||
locks around the process clone operation. This means that if a process is
|
||||
cloned via `clone()`/`clone3()` and another thread currently has the
|
||||
`malloc()` lock taken, it will be cloned in locked state to the child, and
|
||||
thus can never be acquired in the child, leading to deadlocks. Hence, when
|
||||
using `clone()`/`clone3()` there are only two ways out: never use threads in the
|
||||
parent, or never do memory allocation in the child. For our uses we need
|
||||
`clone()`/`clone3()` and hence decided to avoid threads. Of course, sometimes the
|
||||
concurrency threads allow is beneficial, however we suggest forking off
|
||||
worker *processes* rather than worker *threads* for this purpose, ideally
|
||||
even with an `execve()` to remove the CoW trap situation `fork()` easily
|
||||
triggers.
|
||||
|
||||
- A corollary of the above is: never use `clone()` where a `fork()` would do
|
||||
too. Also consider using `posix_spawn()` which combines `clone()` +
|
||||
`execve()` into one and has nice properties since it avoids becoming a CoW
|
||||
trap by using `CLONE_VORK` and `CLONE_VM` together.
|
||||
|
||||
- While we avoid forking off threads on our own, writing thread-safe code is a
|
||||
good idea where it might end up running inside of libsystemd.so or
|
||||
similar. Hence, use TLS (i.e. `thread_local`) where appropriate, and maybe
|
||||
the occasional `pthread_once()`.
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: Contributing
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Contributing
|
||||
|
||||
We welcome contributions from everyone. However, please follow the following guidelines when posting a GitHub Pull Request or filing a GitHub Issue on the systemd project:
|
||||
|
||||
## Filing Issues
|
||||
|
||||
* We use [GitHub Issues](https://github.com/systemd/systemd/issues) **exclusively** for tracking **bugs** and **feature** **requests** (RFEs) of systemd. If you are looking for help, please try the forums of your distribution first, or [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) for general questions about systemd.
|
||||
* We only track bugs in the **two** **most** **recently** **released** (non-rc) **versions** of systemd in the GitHub Issue tracker. If you are using an older version of systemd, please contact your distribution's bug tracker instead (see below). See [GitHub Release Page](https://github.com/systemd/systemd/releases) for the list of most recent releases.
|
||||
* When filing a feature request issue (RFE), please always check first if the newest upstream version of systemd already implements the feature, and whether there's already an issue filed for your feature by someone else.
|
||||
* When filing an issue, specify the **systemd** **version** you are experiencing the issue with. Also, indicate which **distribution** you are using.
|
||||
* Please include an explanation how to reproduce the issue you are pointing out.
|
||||
|
||||
Following these guidelines makes it easier for us to process your issue, and ensures we won't close your issue right-away for being misfiled.
|
||||
|
||||
### Older downstream versions
|
||||
|
||||
For older versions that are still supported by your distribution please use respective downstream tracker:
|
||||
|
||||
* **Fedora** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=systemd)
|
||||
* **RHEL/CentOS stream** - [Jira](https://issues.redhat.com/secure/CreateIssueDetails!init.jspa?pid=12332745&issuetype=1&components=12380515&priority=10300) or [contribute to systemd-rhel @GitHub](https://github.com/redhat-plumbers#systemd)
|
||||
* **Debian** - [bugs.debian.org](https://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=systemd)
|
||||
|
||||
## Security vulnerability reports
|
||||
|
||||
See [reporting of security vulnerabilities](SECURITY).
|
||||
|
||||
## Posting Pull Requests
|
||||
|
||||
* Make sure to post PRs only relative to a recent tip of the `main` branch.
|
||||
* Follow our [Coding Style](CODING_STYLE) when contributing code. This is a requirement for all code we merge.
|
||||
* Please make sure to test your change before submitting the PR. See the [Hacking guide](HACKING) for details on how to do this.
|
||||
* Make sure to run the test suite locally, before posting your PR. We use a CI system, meaning we don't even look at your PR if the build and tests don't pass.
|
||||
* If you need to update the code in an existing PR, force-push into the same branch, overriding old commits with new versions.
|
||||
* After you have pushed a new version, add a comment explaining the latest changes. If you are a member of the systemd project on GitHub, remove the `reviewed/needs-rework`/`ci-fails/needs-rework`/`needs-rebase` labels.
|
||||
* If you are copying existing code from another source (eg: a compat header), please make sure the license is compatible with `LGPL-2.1-or-later`. If the license is not `LGPL-2.1-or-later`, please add a note to [`LICENSES/README.md`](https://github.com/systemd/systemd/blob/main/LICENSES/README.md).
|
||||
* If the pull request stalls without review, post a ping in a comment after some time has passed. We are always short on reviewer time, and pull requests which haven't seen any recent activity can be easily forgotten.
|
||||
* Github will automatically add the `please-review` label when a pull request is opened or updated. If you need
|
||||
more information after a review, you can comment `/please-review` on the pull request to have Github add the
|
||||
`please-review` label to the pull request.
|
||||
|
||||
## Reviewing Pull Requests
|
||||
|
||||
* See [filtered list of pull requests](https://github.com/systemd/systemd/pulls?q=is%3Aopen+is%3Apr+-label%3A%22reviewed%2Fneeds-rework+%F0%9F%94%A8%22+-label%3Aneeds-rebase+-label%3Agood-to-merge%2Fwith-minor-suggestions+-label%3A%22good-to-merge%2Fwaiting-for-ci+%F0%9F%91%8D%22+-label%3Apostponed+-label%3A%22needs-reporter-feedback+%E2%9D%93%22+-label%3A%22dont-merge+%F0%9F%92%A3%22+-label%3A%22ci-fails%2Fneeds-rework+%F0%9F%94%A5%22+sort%3Aupdated-desc) for requests that are ready for review.
|
||||
* After performing a review, set
|
||||
|
||||
* `reviewed/needs-rework` if the pull request needs significant changes
|
||||
* `ci-fails/needs-rework` if the automatic tests fail and the failure is relevant to the pull request
|
||||
* `ci-failure-appears-unrelated` if the test failures seem irrelevant
|
||||
* `needs-rebase` if the pull request needs a rebase because of conflicts
|
||||
* `good-to-merge/waiting-for-ci` if the pull request should be merged without further review
|
||||
* `good-to-merge/with-minor-suggestions` if the pull request should be merged after an update without going through another round of reviews
|
||||
|
||||
Unfortunately only members of the `systemd` organization on github can change labels.
|
||||
If your pull request is mislabeled, make a comment in the pull request and somebody will fix it.
|
||||
Reviews from non-members are still welcome.
|
||||
|
||||
## Final Words
|
||||
|
||||
We'd like to apologize in advance if we are not able to process and reply to your issue or PR right-away. We have a lot of work to do, but we are trying our best!
|
||||
|
||||
Thank you very much for your contributions!
|
||||
|
||||
# Backward Compatibility And External Dependencies
|
||||
|
||||
We strive to keep backward compatibility where possible and reasonable. The following are general guidelines, not hard
|
||||
rules, and case-by-case exceptions might be applied at the discretion of the maintainers. The current set of build-time
|
||||
and runtime dependencies are documented in the [README](https://github.com/systemd/systemd/blob/main/README).
|
||||
|
||||
## New features
|
||||
|
||||
It is fine for new features/functionality/tools/daemons to require bleeding edge external dependencies, provided there
|
||||
are runtime and build-time graceful fallbacks (e.g.: a daemon will not be built, runtime functionality will be skipped with a clear log message).
|
||||
In case a new feature is added to both `systemd` and one of its dependencies, we expect the corresponding feature code to
|
||||
be merged upstream in the dependency before accepting our side of the implementation.
|
||||
Making use of new kernel syscalls can be achieved through compat wrappers in our tree (see: `src/basic/missing_syscall_def.h`),
|
||||
and does not need to wait for glibc support.
|
||||
|
||||
## External Build/Runtime Dependencies
|
||||
|
||||
It is often tempting to bump external dependencies' minimum versions to cut cruft, and in general it's an essential part
|
||||
of the maintenance process. But as a general rule, existing dependencies should not be bumped without strong
|
||||
reasons. When possible, we try to keep compatibility with the most recent LTS releases of each mainstream distribution
|
||||
for optional components, and with all currently maintained (i.e.: not EOL) LTS releases for core components. When in
|
||||
doubt, ask before committing time to work on contributions if it's not clear that cutting support would be obviously
|
||||
acceptable.
|
||||
|
||||
## Kernel Requirements
|
||||
|
||||
Same principles as with other dependencies should be applied. It is fine to require newer kernel versions for additional
|
||||
functionality or optional features, but very strong reasons should be required for breaking compatibility for existing
|
||||
functionality, especially for core components. It is not uncommon, for example, for embedded systems to be stuck on older
|
||||
kernel versions due to hardware requirements, so do not assume everybody is running with latest and greatest at all times.
|
||||
In general, [currently maintained LTS branches](https://www.kernel.org/category/releases.html) should keep being supported
|
||||
for existing functionality.
|
||||
|
||||
## `libsystemd.so`
|
||||
|
||||
`libsystemd.so` is a shared public library, so breaking ABI/API compatibility would create lot of work for everyone, and is not allowed. Instead, always add a new interface instead of modifying
|
||||
the signature of an existing function. It is fine to mark an interface as deprecated to gently nudge users toward a newer one,
|
||||
but support for the old one must be maintained.
|
||||
Symbol versioning and the compiler's deprecated attribute should be used when managing the lifetime of a public interface.
|
||||
|
||||
## `libudev.so`
|
||||
|
||||
`libudev.so` is a shared public library, and is still maintained, but should not gain new symbols at this point.
|
||||
@@ -0,0 +1,356 @@
|
||||
---
|
||||
title: Hacking on systemd
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Hacking on systemd
|
||||
|
||||
We welcome all contributions to systemd. If you notice a bug or a missing
|
||||
feature, please feel invited to fix it, and submit your work as a
|
||||
[GitHub Pull Request (PR)](https://github.com/systemd/systemd/pull/new).
|
||||
|
||||
Please make sure to follow our [Coding Style](CODING_STYLE) when submitting
|
||||
patches. Also have a look at our [Contribution Guidelines](CONTRIBUTING).
|
||||
|
||||
When adding new functionality, tests should be added. For shared functionality
|
||||
(in `src/basic/` and `src/shared/`) unit tests should be sufficient. The general
|
||||
policy is to keep tests in matching files underneath `src/test/`,
|
||||
e.g. `src/test/test-path-util.c` contains tests for any functions in
|
||||
`src/basic/path-util.c`. If adding a new source file, consider adding a matching
|
||||
test executable. For features at a higher level, tests in `src/test/` are very
|
||||
strongly recommended. If that is not possible, integration tests in `test/` are
|
||||
encouraged.
|
||||
|
||||
Please also have a look at our list of [code quality tools](CODE_QUALITY) we
|
||||
have setup for systemd, to ensure our codebase stays in good shape.
|
||||
|
||||
Please always test your work before submitting a PR. For many of the components
|
||||
of systemd testing is straightforward as you can simply compile systemd and
|
||||
run the relevant tool from the build directory.
|
||||
|
||||
For some components (most importantly, systemd/PID 1 itself) this is not
|
||||
possible, however. In order to simplify testing for cases like this we provide
|
||||
a set of `mkosi` build files directly in the source tree.
|
||||
[mkosi](https://github.com/systemd/mkosi) is a tool for building clean OS images
|
||||
from an upstream distribution in combination with a fresh build of the project
|
||||
in the local working directory. To make use of this, please install `mkosi` v19
|
||||
or newer using your distribution's package manager or from the
|
||||
[GitHub repository](https://github.com/systemd/mkosi). `mkosi` will build an
|
||||
image for the host distro by default. First, run `mkosi genkey` to generate a key
|
||||
and certificate to be used for secure boot and verity signing. After that is done,
|
||||
it is sufficient to type `mkosi` in the systemd project directory to generate a disk
|
||||
image you can boot either in `systemd-nspawn` or in a UEFI-capable VM:
|
||||
|
||||
```sh
|
||||
$ sudo mkosi boot # nspawn still needs sudo for now
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```sh
|
||||
$ mkosi qemu
|
||||
```
|
||||
|
||||
Every time you rerun the `mkosi` command a fresh image is built, incorporating
|
||||
all current changes you made to the project tree.
|
||||
|
||||
By default a directory image is built. This requires `virtiofsd` to be installed
|
||||
on the host. To build a disk image instead which does not require `virtiofsd`,
|
||||
add the following to `mkosi.local.conf`:
|
||||
|
||||
```conf
|
||||
[Output]
|
||||
Format=disk
|
||||
```
|
||||
|
||||
To boot in UEFI mode instead of using QEMU's direct kernel boot, add the following
|
||||
to `mkosi.local.conf`:
|
||||
|
||||
```conf
|
||||
[Host]
|
||||
QemuFirmware=uefi
|
||||
```
|
||||
|
||||
Putting this all together, here's a series of commands for preparing a patch
|
||||
for systemd:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/systemd/mkosi.git # If mkosi v19 or newer is not packaged by your distribution
|
||||
$ ln -s $PWD/mkosi/bin/mkosi /usr/local/bin/mkosi # If mkosi v19 or newer is not packaged by your distribution
|
||||
$ git clone https://github.com/systemd/systemd.git
|
||||
$ cd systemd
|
||||
$ git checkout -b <BRANCH> # where BRANCH is the name of the branch
|
||||
$ vim src/core/main.c # or wherever you'd like to make your changes
|
||||
$ mkosi -f qemu # (re-)build and boot up the test image in qemu
|
||||
$ git add -p # interactively put together your patch
|
||||
$ git commit # commit it
|
||||
$ git push -u <REMOTE> # where REMOTE is your "fork" on GitHub
|
||||
```
|
||||
|
||||
And after that, head over to your repo on GitHub and click "Compare & pull request"
|
||||
|
||||
If you want to do a local build without mkosi, most distributions also provide
|
||||
very simple and convenient ways to install most development packages necessary
|
||||
to build systemd:
|
||||
|
||||
```sh
|
||||
# Fedora
|
||||
$ sudo dnf builddep systemd
|
||||
# Debian/Ubuntu
|
||||
$ sudo apt-get build-dep systemd
|
||||
# Arch
|
||||
$ sudo pacman -S devtools
|
||||
$ pkgctl repo clone --protocol=https systemd
|
||||
$ cd systemd
|
||||
$ makepkg -seoc
|
||||
```
|
||||
|
||||
After installing the development packages, systemd can be built from source as follows:
|
||||
|
||||
```sh
|
||||
$ meson setup build <options>
|
||||
$ ninja -C build
|
||||
$ meson test -C build
|
||||
```
|
||||
|
||||
Happy hacking!
|
||||
|
||||
## Templating engines in .in files
|
||||
|
||||
Some source files are generated during build. We use two templating engines:
|
||||
* meson's `configure_file()` directive uses syntax with `@VARIABLE@`.
|
||||
|
||||
See the
|
||||
[Meson docs for `configure_file()`](https://mesonbuild.com/Reference-manual.html#configure_file)
|
||||
for details.
|
||||
|
||||
{% raw %}
|
||||
* most files are rendered using jinja2, with `{{VARIABLE}}` and `{% if … %}`,
|
||||
`{% elif … %}`, `{% else … %}`, `{% endif … %}` blocks. `{# … #}` is a
|
||||
jinja2 comment, i.e. that block will not be visible in the rendered
|
||||
output. `{% raw %} … `{% endraw %}`{{ '{' }}{{ '% endraw %' }}}` creates a block
|
||||
where jinja2 syntax is not interpreted.
|
||||
|
||||
See the
|
||||
[Jinja Template Designer Documentation](https://jinja2docs.readthedocs.io/en/stable/templates.html#synopsis)
|
||||
for details.
|
||||
|
||||
Please note that files for both template engines use the `.in` extension.
|
||||
|
||||
## Developer and release modes
|
||||
|
||||
In the default meson configuration (`-Dmode=developer`), certain checks are
|
||||
enabled that are suitable when hacking on systemd (such as internal
|
||||
documentation consistency checks). Those are not useful when compiling for
|
||||
distribution and can be disabled by setting `-Dmode=release`.
|
||||
|
||||
## Sanitizers in mkosi
|
||||
|
||||
See [Testing systemd using sanitizers](TESTING_WITH_SANITIZERS) for more information
|
||||
on how to build with sanitizers enabled in mkosi.
|
||||
|
||||
## Fuzzers
|
||||
|
||||
systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
|
||||
run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers.
|
||||
To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput`
|
||||
function and add it to the list in `src/fuzz/meson.build`.
|
||||
|
||||
Whenever possible, a seed corpus and a dictionary should also be added with new
|
||||
fuzz targets. The dictionary should be named `src/fuzz/fuzz-foo.dict` and the seed
|
||||
corpus should be built and exported as `$OUT/fuzz-foo_seed_corpus.zip` in
|
||||
`tools/oss-fuzz.sh`.
|
||||
|
||||
The fuzzers can be built locally if you have libFuzzer installed by running
|
||||
`tools/oss-fuzz.sh`, or by running:
|
||||
|
||||
```
|
||||
CC=clang CXX=clang++ \
|
||||
meson setup build-libfuzz -Dllvm-fuzz=true -Db_sanitize=address,undefined -Db_lundef=false \
|
||||
-Dc_args='-fno-omit-frame-pointer -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'
|
||||
ninja -C build-libfuzz fuzzers
|
||||
```
|
||||
|
||||
Each fuzzer then can be then run manually together with a directory containing
|
||||
the initial corpus:
|
||||
|
||||
```
|
||||
export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
|
||||
build-libfuzz/fuzz-varlink-idl test/fuzz/fuzz-varlink-idl/
|
||||
```
|
||||
|
||||
Note: the `halt_on_error=1` UBSan option is especially important, otherwise
|
||||
the fuzzer won't crash when undefined behavior is triggered.
|
||||
|
||||
You should also confirm that the fuzzers can be built and run using
|
||||
[the OSS-Fuzz toolchain](https://google.github.io/oss-fuzz/advanced-topics/reproducing/#building-using-docker):
|
||||
|
||||
```
|
||||
path_to_systemd=...
|
||||
|
||||
git clone --depth=1 https://github.com/google/oss-fuzz
|
||||
cd oss-fuzz
|
||||
|
||||
for sanitizer in address undefined memory; do
|
||||
for engine in libfuzzer afl honggfuzz; do
|
||||
./infra/helper.py build_fuzzers --sanitizer "$sanitizer" --engine "$engine" \
|
||||
--clean systemd "$path_to_systemd"
|
||||
|
||||
./infra/helper.py check_build --sanitizer "$sanitizer" --engine "$engine" \
|
||||
-e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd
|
||||
done
|
||||
done
|
||||
|
||||
./infra/helper.py build_fuzzers --clean --architecture i386 systemd "$path_to_systemd"
|
||||
./infra/helper.py check_build --architecture i386 -e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd
|
||||
|
||||
./infra/helper.py build_fuzzers --clean --sanitizer coverage systemd "$path_to_systemd"
|
||||
./infra/helper.py coverage --no-corpus-download systemd
|
||||
```
|
||||
|
||||
If you find a bug that impacts the security of systemd, please follow the
|
||||
guidance in [CONTRIBUTING.md](CONTRIBUTING) on how to report a security vulnerability.
|
||||
|
||||
For more details on building fuzzers and integrating with OSS-Fuzz, visit:
|
||||
|
||||
- [Setting up a new project - OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/new-project-guide/)
|
||||
- [Tutorials - OSS-Fuzz](https://google.github.io/oss-fuzz/reference/useful-links/#tutorials)
|
||||
|
||||
## Debugging binaries that need to run as root in vscode
|
||||
|
||||
When trying to debug binaries that need to run as root, we need to do some custom configuration in vscode to
|
||||
have it try to run the applications as root and to ask the user for the root password when trying to start
|
||||
the binary. To achieve this, we'll use a custom debugger path which points to a script that starts `gdb` as
|
||||
root using `pkexec`. pkexec will prompt the user for their root password via a graphical interface. This
|
||||
guide assumes the C/C++ extension is used for debugging.
|
||||
|
||||
First, create a file `sgdb` in the root of the systemd repository with the following contents and make it
|
||||
executable:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
exec pkexec gdb "$@"
|
||||
```
|
||||
|
||||
Then, open launch.json in vscode, and set `miDebuggerPath` to `${workspaceFolder}/sgdb` for the corresponding
|
||||
debug configuration. Now, whenever you try to debug the application, vscode will try to start gdb as root via
|
||||
pkexec which will prompt you for your password via a graphical interface. After entering your password,
|
||||
vscode should be able to start debugging the application.
|
||||
|
||||
For more information on how to set up a debug configuration for C binaries, please refer to the official
|
||||
vscode documentation [here](https://code.visualstudio.com/docs/cpp/launch-json-reference)
|
||||
|
||||
## Debugging systemd with mkosi + vscode
|
||||
|
||||
To simplify debugging systemd when testing changes using mkosi, we're going to show how to attach
|
||||
[VSCode](https://code.visualstudio.com/)'s debugger to an instance of systemd running in a mkosi image using
|
||||
QEMU.
|
||||
|
||||
To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access
|
||||
the virtual machine spawned by mkosi where systemd is running. mkosi makes this possible via a handy SSH
|
||||
option that makes the generated image accessible via SSH when booted. Thus you must build the image with
|
||||
`mkosi --ssh`. The easiest way to set the option is to create a file `mkosi.local.conf` in the root of the
|
||||
repository and add the following contents:
|
||||
|
||||
```
|
||||
[Host]
|
||||
Ssh=yes
|
||||
RuntimeTrees=.
|
||||
```
|
||||
|
||||
Also make sure that the SSH agent is running on your system and that you've added your SSH key to it with
|
||||
`ssh-add`. Also make sure that `virtiofsd` is installed.
|
||||
|
||||
After rebuilding the image and booting it with `mkosi qemu`, you should now be able to connect to it by
|
||||
running `mkosi ssh` from the same directory in another terminal window.
|
||||
|
||||
Now we need to configure VSCode. First, make sure the C/C++ extension is installed. If you're already using
|
||||
a different extension for code completion and other IDE features for C in VSCode, make sure to disable the
|
||||
corresponding parts of the C/C++ extension in your VSCode user settings by adding the following entries:
|
||||
|
||||
```json
|
||||
"C_Cpp.formatting": "Disabled",
|
||||
"C_Cpp.intelliSenseEngine": "Disabled",
|
||||
"C_Cpp.enhancedColorization": "Disabled",
|
||||
"C_Cpp.suggestSnippets": false,
|
||||
```
|
||||
|
||||
With the extension set up, we can create the launch.json file in the .vscode/ directory to tell the VSCode
|
||||
debugger how to attach to the systemd instance running in our mkosi container/VM. Create the file, and possibly
|
||||
the directory, and add the following contents:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cppdbg",
|
||||
"program": "/usr/lib/systemd/systemd",
|
||||
"processId": "${command:pickRemoteProcess}",
|
||||
"request": "attach",
|
||||
"name": "systemd",
|
||||
"pipeTransport": {
|
||||
"pipeProgram": "mkosi",
|
||||
"pipeArgs": [
|
||||
"-C",
|
||||
"/path/to/systemd/repo/directory/on/host/system/",
|
||||
"ssh"
|
||||
],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"MIMode": "gdb",
|
||||
"sourceFileMap": {
|
||||
"/root/src/systemd": {
|
||||
"editorPath": "${workspaceFolder}",
|
||||
"useForBreakpoints": false
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Now that the debugger knows how to connect to our process in the container/VM and we've set up the necessary
|
||||
source mappings, go to the "Run and Debug" window and run the "systemd" debug configuration. If everything
|
||||
goes well, the debugger should now be attached to the systemd instance running in the container/VM. You can
|
||||
attach breakpoints from the editor and enjoy all the other features of VSCode's debugger.
|
||||
|
||||
To debug systemd components other than PID 1, set "program" to the full path of the component you want to
|
||||
debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
|
||||
the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
|
||||
container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
|
||||
|
||||
## Debugging systemd-boot
|
||||
|
||||
During boot, systemd-boot and the stub loader will output messages like
|
||||
`systemd-boot@0x0A` and `systemd-stub@0x0B`, providing the base of the loaded
|
||||
code. This location can then be used to attach to a QEMU session (provided it
|
||||
was run with `-s`). See `debug-sd-boot.sh` script in the tools folder which
|
||||
automates this processes.
|
||||
|
||||
If the debugger is too slow to attach to examine an early boot code passage,
|
||||
the call to `DEFINE_EFI_MAIN_FUNCTION()` can be modified to enable waiting. As
|
||||
soon as the debugger has control, we can then run `set variable wait = 0` or
|
||||
`return` to continue. Once the debugger has attached, setting breakpoints will
|
||||
work like usual.
|
||||
|
||||
To debug systemd-boot in an IDE such as VSCode we can use a launch configuration like this:
|
||||
```json
|
||||
{
|
||||
"name": "systemd-boot",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerServerAddress": ":1234",
|
||||
"setupCommands": [
|
||||
{ "text": "shell mkfifo /tmp/sdboot.{in,out}" },
|
||||
{ "text": "shell qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot" },
|
||||
{ "text": "shell ${workspaceFolder}/tools/debug-sd-boot.sh ${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi /tmp/sdboot.out systemd-boot.gdb" },
|
||||
{ "text": "source /tmp/systemd-boot.gdb" },
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Porting to New Architectures
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Porting systemd to New Architectures
|
||||
|
||||
Here's a brief checklist of things to implement when porting systemd to a new
|
||||
architecture.
|
||||
|
||||
1. Patch
|
||||
[src/basic/architecture.h](https://github.com/systemd/systemd/blob/main/src/basic/architecture.h)
|
||||
and
|
||||
[src/basic/architecture.c](https://github.com/systemd/systemd/blob/main/src/basic/architecture.c)
|
||||
to make your architecture known to systemd. Besides an `ARCHITECTURE_XYZ`
|
||||
enumeration entry you need to provide an implementation of
|
||||
`native_architecture()` and `uname_architecture()`.
|
||||
|
||||
2. Patch
|
||||
[src/shared/gpt.h](https://github.com/systemd/systemd/blob/main/src/shared/gpt.h)
|
||||
and
|
||||
[src/shared/gpt.c](https://github.com/systemd/systemd/blob/main/src/shared/gpt.c)
|
||||
and define a new set of GPT partition type UUIDs for the root file system,
|
||||
`/usr/` file system, and the matching Verity and Verity signature
|
||||
partitions. Use `systemd-id128 new -p` to generate new suitable UUIDs you
|
||||
can use for this. Make sure to register your new types in the various
|
||||
functions in `gpt.c`. Also make sure to update the tables in
|
||||
[Discoverable Partitions Specification](https://uapi-group.org/specifications/specs/discoverable_partitions_specification)
|
||||
and `man/systemd-gpt-auto-generator.xml` accordingly.
|
||||
|
||||
3. If your architecture supports UEFI, make sure to update the `efi_arch`
|
||||
variable logic in `meson.build` to be set to the right architecture string
|
||||
as defined by the UEFI specification. (This ensures that `systemd-boot` will
|
||||
be built as the appropriately named `BOOT<arch>.EFI` binary.) Also, if your
|
||||
architecture uses a special boot protocol for the Linux kernel, make sure to
|
||||
implement it in `src/boot/efi/linux*.c`, so that the `systemd-stub` EFI stub
|
||||
can work.
|
||||
|
||||
4. Make sure to register the right system call numbers for your architecture in
|
||||
`src/basic/missing_syscall_def.h`. systemd uses various system calls the
|
||||
Linux kernel provides that are currently not wrapped by glibc (or are only
|
||||
in very new glibc), and we need to know the right numbers for them. It might
|
||||
also be necessary to tweak `src/basic/raw-clone.h`.
|
||||
|
||||
5. Make sure the code in `src/shared/seccomp-util.c` properly understands the
|
||||
local architecture and its system call quirks.
|
||||
|
||||
6. If your architecture uses a `/lib64/` library directory, then make sure that
|
||||
the `BaseFilesystem` table in `src/shared/base-filesystem.c` has an entry
|
||||
for it so that it can be set up automatically if missing. This is useful to
|
||||
support booting into OS trees that have an empty root directory with only
|
||||
`/usr/` mounted in.
|
||||
|
||||
7. If your architecture supports VM virtualization and provides CPU opcodes
|
||||
similar to x86' CPUID, consider adding native support for detecting VMs this
|
||||
way to `src/basic/virt.c`.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Steps to a Successful Release
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Steps to a Successful Release
|
||||
|
||||
1. Add all items to NEWS
|
||||
2. Update the contributors list in NEWS (`ninja -C build git-contrib`)
|
||||
3. Update the time and place in NEWS
|
||||
4. Update hwdb (`ninja -C build update-hwdb`, `ninja -C build update-hwdb-autosuspend`, commit separately).
|
||||
5. Update syscall numbers (`ninja -C build update-syscall-tables update-syscall-header`).
|
||||
6. [RC1] Update library numbers in `meson.build`
|
||||
7. Update version number in `meson.version` (e.g. from `v256~devel` to `v256~rc1` or from `v256~rc3` to `v256`)
|
||||
8. Check dbus docs with `ninja -C build update-dbus-docs`
|
||||
9. Update translation strings (`cd build`, `meson compile systemd-pot`, `meson compile systemd-update-po`) - drop the header comments from `systemd.pot` + re-add SPDX before committing. If the only change in a file is the 'POT-Creation-Date' field, then ignore that file.
|
||||
10. Tag the release: `version=vXXX~rcY && git tag -s "${version}" -m "systemd ${version}"`. Note that this uses a tilde (\~) instead of a hyphen (-) because tildes sort lower in version comparisons according to the [version format specification](https://uapi-group.org/specifications/specs/version_format_specification/), and we want `v255~rc1` to sort lower than `v255`.
|
||||
11. Do `ninja -C build`
|
||||
12. Make sure that the version string and package string match: `build/systemctl --version`
|
||||
13. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
|
||||
14. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
|
||||
15. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
|
||||
16. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released | Online resources https://systemd.io/`)
|
||||
17. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`.
|
||||
18. [FINAL] Build and upload the documentation (on the -stable branch): `ninja -C build doc-sync`
|
||||
19. [FINAL] Change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
|
||||
20. [FINAL] Change the Github Pages branch in the stable repository to the newly created branch (https://github.com/systemd/systemd-stable/settings/pages) and set the 'Custom domain' to 'systemd.io'
|
||||
21. [FINAL] Update version number in `meson.version` to the devel version of the next release (e.g. from `v256` to `v257~devel`)
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Reporting of Security Vulnerabilities
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Reporting of Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability, we'd appreciate a non-public disclosure. systemd developers can be contacted privately on the **[systemd-security@redhat.com](mailto:systemd-security@redhat.com) mailing list**. The disclosure will be coordinated with distributions.
|
||||
|
||||
(The [issue tracker](https://github.com/systemd/systemd/issues) and [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) are fully public.)
|
||||
|
||||
Subscription to the systemd-security mailing list is open to **regular systemd contributors and people working in the security teams of various distributions**. Those conditions should be backed by publicly accessible information (ideally, a track of posts and commits from the mail address in question). If you fall into one of those categories and wish to be subscribed, submit a **[subscription request](https://www.redhat.com/mailman/listinfo/systemd-security)**.
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Testing systemd Using Sanitizers
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Testing systemd Using Sanitizers
|
||||
|
||||
To catch the *nastier* kind of bugs, you can run your code with [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
|
||||
and [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
|
||||
This is mostly done automagically by various CI systems for each PR, but you may
|
||||
want to do it locally as well. The process slightly varies depending on the
|
||||
compiler you want to use and which part of the test suite you want to run.
|
||||
|
||||
## mkosi
|
||||
|
||||
To build with sanitizers in mkosi, create a file `mkosi.local.conf` and add the following contents:
|
||||
|
||||
```
|
||||
[Content]
|
||||
Environment=SANITIZERS=address,undefined
|
||||
```
|
||||
|
||||
The value of `SANITIZERS` is passed directly to meson's `b_sanitize` option, See
|
||||
https://mesonbuild.com/Builtin-options.html#base-options for the format expected by the option. Currently,
|
||||
only the sanitizers supported by gcc can be used, which are `address` and `undefined`.
|
||||
|
||||
Note that this will only work with a recent version of mkosi (>= 14 or by running mkosi directly from source).
|
||||
|
||||
## gcc
|
||||
gcc compiles in sanitizer libraries dynamically by default, so you need to get
|
||||
the shared libraries first - on Fedora these are shipped as separate packages
|
||||
(`libasan` for Address Sanitizer and `libubsan` for Undefined Behavior Sanitizer).
|
||||
|
||||
The compilation itself is then a matter of simply adding `-Db_sanitize=address,undefined`
|
||||
to `meson`. That's it - following executions of `meson test` and integration tests
|
||||
under `test/` subdirectory will run with sanitizers enabled. However, to get
|
||||
truly useful results, you should tweak the runtime configuration of respective
|
||||
sanitizers; e.g. in systemd we set the following environment variables:
|
||||
|
||||
```bash
|
||||
ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
|
||||
UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
|
||||
```
|
||||
## clang
|
||||
In case of clang things are somewhat different - the sanitizer libraries are
|
||||
compiled in statically by default. This is not an issue if you plan to run
|
||||
only the unit tests, but for integration tests you'll need to convince clang
|
||||
to use the dynamic versions of sanitizer libraries.
|
||||
|
||||
First of all, pass `-shared-libsan` to both `clang` and `clang++`:
|
||||
|
||||
```bash
|
||||
CFLAGS=-shared-libasan
|
||||
CXXFLAGS=-shared-libasan
|
||||
```
|
||||
|
||||
The `CXXFLAGS` are necessary for `src/libsystemd/sd-bus/test-bus-vtable-cc.c`. Compilation
|
||||
is then the same as in case of gcc, simply add `-Db_sanitize=address,undefined`
|
||||
to the `meson` call and use the same environment variables for runtime configuration.
|
||||
|
||||
```bash
|
||||
ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
|
||||
UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
|
||||
```
|
||||
|
||||
After this, you'll probably notice that all compiled binaries complain about
|
||||
missing `libclang_rt.asan*` library. To fix this, you have to install clang's
|
||||
runtime libraries, usually shipped in the `compiler-rt` package. As these libraries
|
||||
are installed in a non-standard location (non-standard for `ldconfig`), you'll
|
||||
need to manually direct binaries to the respective runtime libraries.
|
||||
|
||||
```
|
||||
# Optionally locate the respective runtime DSO
|
||||
$ ldd build/systemd | grep libclang_rt.asan
|
||||
libclang_rt.asan-x86_64.so => not found
|
||||
libclang_rt.asan-x86_64.so => not found
|
||||
$ find /usr/lib* /usr/local/lib* -type f -name libclang_rt.asan-x86_64.so 2>/dev/null
|
||||
/usr/lib64/clang/7.0.1/lib/libclang_rt.asan-x86_64.so
|
||||
|
||||
# Set the LD_LIBRARY_PATH accordingly
|
||||
export LD_LIBRARY_PATH=/usr/lib64/clang/7.0.1/lib/
|
||||
|
||||
# If the path is correct, the "not found" message should change to an actual path
|
||||
$ ldd build/systemd | grep libclang_rt.asan
|
||||
libclang_rt.asan-x86_64.so => /usr/lib64/clang/7.0.1/lib/libclang_rt.asan-x86_64.so (0x00007fa9752fc000)
|
||||
```
|
||||
|
||||
This should help binaries to correctly find necessary sanitizer DSOs.
|
||||
|
||||
Also, to make the reports useful, `llvm-symbolizer` tool is required (usually
|
||||
part of the `llvm` package).
|
||||
|
||||
## Background notes
|
||||
The reason why you need to force dynamic linking in case of `clang` is that some
|
||||
applications make use of `libsystemd`, which is compiled with sanitizers as well.
|
||||
However, if a *standard* (uninstrumented) application loads an instrumented library,
|
||||
it will immediately fail due to unresolved symbols. To fix/workaround this, you
|
||||
need to pre-load the ASan DSO using `LD_PRELOAD=/path/to/asan/dso`, which will
|
||||
make things work as expected in most cases. This will, obviously, not work with
|
||||
statically linked sanitizer libraries.
|
||||
|
||||
These shenanigans are performed automatically when running the integration test
|
||||
suite (i.e. `test/TEST-??-*`) and are located in `test/test-functions` (mainly,
|
||||
but not only, in the `create_asan_wrapper` function).
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Notes for Translators
|
||||
category: Contributing
|
||||
layout: default
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
---
|
||||
|
||||
# Notes for Translators
|
||||
|
||||
systemd depends on the `gettext` package for multilingual support.
|
||||
|
||||
You'll find the i18n files in the `po/` directory.
|
||||
|
||||
The build system (meson/ninja) can be used to generate a template (`*.pot`),
|
||||
which can be used to create new translations.
|
||||
|
||||
It can also merge the template into the existing translations (`*.po`), to pick
|
||||
up new strings in need of translation.
|
||||
|
||||
Finally, it is able to compile the translations (to `*.gmo` files), so that
|
||||
they can be used by systemd software. (This step is also useful to confirm the
|
||||
syntax of the `*.po` files is correct.)
|
||||
|
||||
## Creating a New Translation
|
||||
|
||||
To create a translation to a language not yet available, start by creating the
|
||||
initial template:
|
||||
|
||||
```
|
||||
$ ninja -C build/ systemd-pot
|
||||
```
|
||||
|
||||
This will generate file `po/systemd.pot` in the source tree.
|
||||
|
||||
Then simply copy it to a new `${lang_code}.po` file, where
|
||||
`${lang_code}` is the two-letter code for a language
|
||||
(possibly followed by a two-letter uppercase country code), according to the
|
||||
ISO 639 standard.
|
||||
|
||||
In short:
|
||||
|
||||
```
|
||||
$ cp po/systemd.pot po/${lang_code}.po
|
||||
```
|
||||
|
||||
Then edit the new `po/${lang_code}.po` file (for example,
|
||||
using the `poedit` GUI editor.)
|
||||
|
||||
## Updating an Existing Translation
|
||||
|
||||
Start by updating the `*.po` files from the latest template:
|
||||
|
||||
```
|
||||
$ ninja -C build/ systemd-update-po
|
||||
```
|
||||
|
||||
This will touch all the `*.po` files, so you'll want to pay attention when
|
||||
creating a git commit from this change, to only include the one translation
|
||||
you're actually updating.
|
||||
|
||||
Edit the `*.po` file, looking for empty translations and translations marked as
|
||||
"fuzzy" (which means the merger found a similar message that needs to be
|
||||
reviewed as it's expected not to match exactly.)
|
||||
|
||||
You can use any text editor to update the `*.po` files, but a good choice is
|
||||
the `poedit` editor, a graphical application specifically designed for this
|
||||
purpose.
|
||||
|
||||
Once you're done, create a git commit for the update of the `po/*.po` file you
|
||||
touched. Remember to undo the changes to the other `*.po` files (for instance,
|
||||
using `git checkout -- po/` after you commit the changes you do want to keep.)
|
||||
|
||||
## Recompiling Translations
|
||||
|
||||
You can recompile the `*.po` files using the following command:
|
||||
|
||||
```
|
||||
$ ninja -C build/ systemd-gmo
|
||||
```
|
||||
|
||||
The resulting files will be saved in the `build/po/` directory.
|
||||
Reference in New Issue
Block a user