The fix in 8f1751a111 made me wonder if we could automatically detect
when pointers are accessed but when this might not be safe. Systemd
is already using a lot of `assert(dst)` and this change now forces
us to use them.
So this commit (ab)uses coccinelle to flag any pointer parameter
dereference not preceded by assert(param), ASSERT_PTR(param), or an
explicit NULL check. It adds integration into meson as a new "coccinelle"
test suite (just like clang-tidy) and is run in CI. The check is not
perfect but seems a reasonable heuristic.
For this RFC commit it is scoped to a subset, it excludes 25 dirs right
now and includes around 100. About 300 warnings left. Busywork that I am
happy to do if there is agreement that it is worth it.
With this in place we would have caught the bug from 8f1751a111 in CI:
```
FAIL: check-pointer-deref.cocci found issues in systemd/src/boot:
diff -u -p systemd/src/boot/measure.c /tmp/nothing/measure.c
--- systemd/src/boot/measure.c
+++ /tmp/nothing/measure.c
@@ -312,7 +312,6 @@ EFI_STATUS tpm_log_tagged_event(
if (err != EFI_SUCCESS)
return err;
- *ret_measured = true;
return EFI_SUCCESS;
}
```
This also adds a new POINTER_MAY_BE_NULL() for the cases when the
called function will do the NULL check (like `iovec_is_set()`).
211 lines
6.3 KiB
C
211 lines
6.3 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "ansi-color.h"
|
|
#include "bus-unit-util.h"
|
|
#include "glyph-util.h"
|
|
#include "log.h"
|
|
#include "pager.h"
|
|
#include "sort-util.h"
|
|
#include "special.h"
|
|
#include "string-util.h"
|
|
#include "strv.h"
|
|
#include "systemctl.h"
|
|
#include "systemctl-list-dependencies.h"
|
|
#include "systemctl-util.h"
|
|
#include "terminal-util.h"
|
|
#include "unit-def.h"
|
|
#include "unit-name.h"
|
|
|
|
static int list_dependencies_print(const char *name, UnitActiveState state, int level, unsigned branches, bool last) {
|
|
_cleanup_free_ char *n = NULL;
|
|
size_t max_len = MAX(columns(),20u);
|
|
size_t len = 0;
|
|
|
|
if (arg_plain || state == _UNIT_ACTIVE_STATE_INVALID)
|
|
printf(" ");
|
|
else {
|
|
const char *on;
|
|
|
|
switch (state) {
|
|
case UNIT_ACTIVE:
|
|
case UNIT_RELOADING:
|
|
case UNIT_REFRESHING:
|
|
case UNIT_ACTIVATING:
|
|
on = ansi_highlight_green();
|
|
break;
|
|
|
|
case UNIT_INACTIVE:
|
|
case UNIT_DEACTIVATING:
|
|
on = ansi_normal();
|
|
break;
|
|
|
|
default:
|
|
on = ansi_highlight_red();
|
|
}
|
|
|
|
printf("%s%s%s ", on, glyph(unit_active_state_to_glyph(state)), ansi_normal());
|
|
}
|
|
|
|
if (!arg_plain) {
|
|
for (int i = level - 1; i >= 0; i--) {
|
|
len += 2;
|
|
if (len > max_len - 3 && !arg_full) {
|
|
printf("%s...\n",max_len % 2 ? "" : " ");
|
|
return 0;
|
|
}
|
|
printf("%s", glyph(branches & (1 << i) ? GLYPH_TREE_VERTICAL : GLYPH_TREE_SPACE));
|
|
}
|
|
len += 2;
|
|
|
|
if (len > max_len - 3 && !arg_full) {
|
|
printf("%s...\n",max_len % 2 ? "" : " ");
|
|
return 0;
|
|
}
|
|
|
|
printf("%s", glyph(last ? GLYPH_TREE_RIGHT : GLYPH_TREE_BRANCH));
|
|
}
|
|
|
|
if (arg_full) {
|
|
printf("%s\n", name);
|
|
return 0;
|
|
}
|
|
|
|
n = ellipsize(name, max_len-len, 100);
|
|
if (!n)
|
|
return log_oom();
|
|
|
|
printf("%s\n", n);
|
|
return 0;
|
|
}
|
|
|
|
static int list_dependencies_compare(char * const *a, char * const *b) {
|
|
assert(a);
|
|
assert(b);
|
|
|
|
if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET)
|
|
return 1;
|
|
if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET)
|
|
return -1;
|
|
|
|
return strcasecmp(*a, *b);
|
|
}
|
|
|
|
static int list_dependencies_one(
|
|
sd_bus *bus,
|
|
const char *name,
|
|
int level,
|
|
char ***units,
|
|
unsigned branches) {
|
|
|
|
_cleanup_strv_free_ char **deps = NULL;
|
|
int r;
|
|
bool circular = false;
|
|
|
|
assert(bus);
|
|
assert(name);
|
|
assert(units);
|
|
|
|
r = strv_extend(units, name);
|
|
if (r < 0)
|
|
return log_oom();
|
|
|
|
r = unit_get_dependencies(bus, name, &deps);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
|
|
|
|
STRV_FOREACH(c, deps) {
|
|
_cleanup_free_ char *load_state = NULL, *sub_state = NULL;
|
|
UnitActiveState active_state;
|
|
|
|
if (strv_contains(*units, *c)) {
|
|
circular = true;
|
|
continue;
|
|
}
|
|
|
|
if (arg_types && !strv_contains(arg_types, unit_type_suffix(*c)))
|
|
continue;
|
|
|
|
r = get_state_one_unit(bus, *c, &active_state);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (arg_states) {
|
|
r = unit_load_state(bus, *c, &load_state);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = get_sub_state_one_unit(bus, *c, &sub_state);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!strv_overlap(arg_states, STRV_MAKE(unit_active_state_to_string(active_state), load_state, sub_state)))
|
|
continue;
|
|
}
|
|
|
|
r = list_dependencies_print(*c, active_state, level, branches, /* last= */ c[1] == NULL && !circular);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) {
|
|
r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (circular && !arg_plain) {
|
|
r = list_dependencies_print("...", _UNIT_ACTIVE_STATE_INVALID, level, branches, /* last= */ true);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if (!arg_plain)
|
|
strv_remove(*units, name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int verb_list_dependencies(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
|
_cleanup_strv_free_ char **units = NULL, **done = NULL;
|
|
char **patterns;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
/* We won't be able to preserve the tree structure if --type= or --state= is used */
|
|
arg_plain = arg_plain || arg_types || arg_states;
|
|
|
|
r = acquire_bus(BUS_MANAGER, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
patterns = strv_skip(argv, 1);
|
|
if (patterns) {
|
|
r = expand_unit_names(bus, patterns, NULL, &units, NULL);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to expand names: %m");
|
|
} else {
|
|
units = strv_new(SPECIAL_DEFAULT_TARGET);
|
|
if (!units)
|
|
return log_oom();
|
|
}
|
|
|
|
pager_open(arg_pager_flags);
|
|
|
|
STRV_FOREACH(u, units) {
|
|
if (u != units)
|
|
puts("");
|
|
|
|
puts(*u);
|
|
r = list_dependencies_one(bus, *u, 0, &done, 0);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|