diff options
Diffstat (limited to 'src')
122 files changed, 3328 insertions, 3196 deletions
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index b83f559e7d..5fd3ee49eb 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -231,14 +231,12 @@ static int verify_unit(Unit *u, bool check_man) { return r; } -int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) { +int verify_units(char **filenames, UnitFileScope scope, bool check_man) { _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; + _cleanup_free_ char *var = NULL; Manager *m = NULL; FILE *serial = NULL; FDSet *fdset = NULL; - - _cleanup_free_ char *var = NULL; - char **filename; int r = 0, k; @@ -255,7 +253,7 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) assert_se(set_unit_path(var) >= 0); - r = manager_new(running_as, true, &m); + r = manager_new(scope, true, &m); if (r < 0) return log_error_errno(r, "Failed to initialize manager: %m"); diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h index 27c253a562..d8204dc69c 100644 --- a/src/analyze/analyze-verify.h +++ b/src/analyze/analyze-verify.h @@ -23,4 +23,4 @@ #include "path-lookup.h" -int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man); +int verify_units(char **filenames, UnitFileScope scope, bool check_man); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 42754a2741..5e03c0c5e0 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1443,7 +1443,7 @@ int main(int argc, char *argv[]) { if (streq_ptr(argv[optind], "verify")) r = verify_units(argv+optind+1, - arg_user ? MANAGER_USER : MANAGER_SYSTEM, + arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, arg_man); else { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; diff --git a/src/basic/c-rbtree.c b/src/basic/c-rbtree.c deleted file mode 100644 index cf5a7242df..0000000000 --- a/src/basic/c-rbtree.c +++ /dev/null @@ -1,674 +0,0 @@ -/*** - This file is part of systemd. See COPYING for details. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -/* - * RB-Tree Implementation - * This implements the insertion/removal of elements in RB-Trees. You're highly - * recommended to have an RB-Tree documentation at hand when reading this. Both - * insertion and removal can be split into a handful of situations that can - * occur. Those situations are enumerated as "Case 1" to "Case n" here, and - * follow closely the cases described in most RB-Tree documentations. This file - * does not explain why it is enough to handle just those cases, nor does it - * provide a proof of correctness. Dig out your algorithm 101 handbook if - * you're interested. - * - * This implementation is *not* straightforward. Usually, a handful of - * rotation, reparent, swap and link helpers can be used to implement the - * rebalance operations. However, those often perform unnecessary writes. - * Therefore, this implementation hard-codes all the operations. You're highly - * recommended to look at the two basic helpers before reading the code: - * c_rbtree_swap_child() - * c_rbtree_set_parent_and_color() - * Those are the only helpers used, hence, you should really know what they do - * before digging into the code. - * - * For a highlevel documentation of the API, see the header file and docbook - * comments. - */ - -#include <assert.h> -#include <stddef.h> -#include "c-rbtree.h" - -enum { - C_RBNODE_RED = 0, - C_RBNODE_BLACK = 1, -}; - -static inline unsigned long c_rbnode_color(CRBNode *n) { - return (unsigned long)n->__parent_and_color & 1UL; -} - -static inline _Bool c_rbnode_is_red(CRBNode *n) { - return c_rbnode_color(n) == C_RBNODE_RED; -} - -static inline _Bool c_rbnode_is_black(CRBNode *n) { - return c_rbnode_color(n) == C_RBNODE_BLACK; -} - -/** - * c_rbnode_leftmost() - return leftmost child - * @n: current node, or NULL - * - * This returns the leftmost child of @n. If @n is NULL, this will return NULL. - * In all other cases, this function returns a valid pointer. That is, if @n - * does not have any left children, this returns @n. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to leftmost child, or NULL. - */ -CRBNode *c_rbnode_leftmost(CRBNode *n) { - if (n) - while (n->left) - n = n->left; - return n; -} - -/** - * c_rbnode_rightmost() - return rightmost child - * @n: current node, or NULL - * - * This returns the rightmost child of @n. If @n is NULL, this will return - * NULL. In all other cases, this function returns a valid pointer. That is, if - * @n does not have any right children, this returns @n. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to rightmost child, or NULL. - */ -CRBNode *c_rbnode_rightmost(CRBNode *n) { - if (n) - while (n->right) - n = n->right; - return n; -} - -/** - * c_rbnode_next() - return next node - * @n: current node, or NULL - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically next node to @n. If @n is NULL, the last node or - * unlinked, this returns NULL. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to next node, or NULL. - */ -CRBNode *c_rbnode_next(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->right) - return c_rbnode_leftmost(n->right); - - while ((p = c_rbnode_parent(n)) && n == p->right) - n = p; - - return p; -} - -/** - * c_rbnode_prev() - return previous node - * @n: current node, or NULL - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically previous node to @n. If @n is NULL, the first node or - * unlinked, this returns NULL. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to previous node, or NULL. - */ -CRBNode *c_rbnode_prev(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->left) - return c_rbnode_rightmost(n->left); - - while ((p = c_rbnode_parent(n)) && n == p->left) - n = p; - - return p; -} - -/** - * c_rbtree_first() - return first node - * @t: tree to operate on - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically first node in @t. If @t is empty, NULL is returned. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to first node, or NULL. - */ -CRBNode *c_rbtree_first(CRBTree *t) { - assert(t); - return c_rbnode_leftmost(t->root); -} - -/** - * c_rbtree_last() - return last node - * @t: tree to operate on - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically last node in @t. If @t is empty, NULL is returned. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to last node, or NULL. - */ -CRBNode *c_rbtree_last(CRBTree *t) { - assert(t); - return c_rbnode_rightmost(t->root); -} - -/* - * Set the color and parent of a node. This should be treated as a simple - * assignment of the 'color' and 'parent' fields of the node. No other magic is - * applied. But since both fields share its backing memory, this helper - * function is provided. - */ -static inline void c_rbnode_set_parent_and_color(CRBNode *n, CRBNode *p, unsigned long c) { - assert(!((unsigned long)p & 1)); - assert(c < 2); - n->__parent_and_color = (CRBNode*)((unsigned long)p | c); -} - -/* same as c_rbnode_set_parent_and_color(), but keeps the current color */ -static inline void c_rbnode_set_parent(CRBNode *n, CRBNode *p) { - c_rbnode_set_parent_and_color(n, p, c_rbnode_color(n)); -} - -/* - * This function partially replaces an existing child pointer to a new one. The - * existing child must be given as @old, the new child as @new. @p must be the - * parent of @old (or NULL if it has no parent). - * This function ensures that the parent of @old now points to @new. However, - * it does *NOT* change the parent pointer of @new. The caller must ensure - * this. - * If @p is NULL, this function ensures that the root-pointer is adjusted - * instead (given as @t). - */ -static inline void c_rbtree_swap_child(CRBTree *t, CRBNode *p, CRBNode *old, CRBNode *new) { - if (p) { - if (p->left == old) - p->left = new; - else - p->right = new; - } else { - t->root = new; - } -} - -static inline CRBNode *c_rbtree_paint_one(CRBTree *t, CRBNode *n) { - CRBNode *p, *g, *gg, *u, *x; - - /* - * Paint a single node according to RB-Tree rules. The node must - * already be linked into the tree and painted red. - * We repaint the node or rotate the tree, if required. In case a - * recursive repaint is required, the next node to be re-painted - * is returned. - * p: parent - * g: grandparent - * gg: grandgrandparent - * u: uncle - * x: temporary - */ - - /* node is red, so we can access the parent directly */ - p = n->__parent_and_color; - - if (!p) { - /* Case 1: - * We reached the root. Mark it black and be done. As all - * leaf-paths share the root, the ratio of black nodes on each - * path stays the same. */ - c_rbnode_set_parent_and_color(n, p, C_RBNODE_BLACK); - n = NULL; - } else if (c_rbnode_is_black(p)) { - /* Case 2: - * The parent is already black. As our node is red, we did not - * change the number of black nodes on any path, nor do we have - * multiple consecutive red nodes. */ - n = NULL; - } else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */ - g = p->__parent_and_color; - gg = c_rbnode_parent(g); - u = g->right; - - if (u && c_rbnode_is_red(u)) { - /* Case 3: - * Parent and uncle are both red. We know the - * grandparent must be black then. Repaint parent and - * uncle black, the grandparent red and recurse into - * the grandparent. */ - c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED); - n = g; - } else { - /* parent is red, uncle is black */ - - if (n == p->right) { - /* Case 4: - * We're the right child. Rotate on parent to - * become left child, so we can handle it the - * same as case 5. */ - x = n->left; - p->right = n->left; - n->left = p; - if (x) - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED); - p = n; - } - - /* 'n' is invalid from here on! */ - n = NULL; - - /* Case 5: - * We're the red left child or a red parent, black - * grandparent and uncle. Rotate on grandparent and - * switch color with parent. Number of black nodes on - * each path stays the same, but we got rid of the - * double red path. As the grandparent is still black, - * we're done. */ - x = p->right; - g->left = x; - p->right = g; - if (x) - c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED); - c_rbtree_swap_child(t, gg, g, p); - } - } else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */ - g = p->__parent_and_color; - gg = c_rbnode_parent(g); - u = g->left; - - if (u && c_rbnode_is_red(u)) { - c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED); - n = g; - } else { - if (n == p->left) { - x = n->right; - p->left = n->right; - n->right = p; - if (x) - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED); - p = n; - } - - n = NULL; - - x = p->left; - g->right = x; - p->left = g; - if (x) - c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED); - c_rbtree_swap_child(t, gg, g, p); - } - } - - return n; -} - -static inline void c_rbtree_paint(CRBTree *t, CRBNode *n) { - assert(t); - assert(n); - - while (n) - n = c_rbtree_paint_one(t, n); -} - -/** - * c_rbtree_add() - add node to tree - * @t: tree to operate one - * @p: parent node to link under, or NULL - * @l: left/right slot of @p (or root) to link at - * @n: node to add - * - * This links @n into the tree given as @t. The caller must provide the exact - * spot where to link the node. That is, the caller must traverse the tree - * based on their search order. Once they hit a leaf where to insert the node, - * call this function to link it and rebalance the tree. - * - * A typical insertion would look like this (@t is your tree, @n is your node): - * - * CRBNode **i, *p; - * - * i = &t->root; - * p = NULL; - * while (*i) { - * p = *i; - * if (compare(n, *i) < 0) - * i = &(*i)->left; - * else - * i = &(*i)->right; - * } - * - * c_rbtree_add(t, p, i, n); - * - * Once the node is linked into the tree, a simple lookup on the same tree can - * be coded like this: - * - * CRBNode *i; - * - * i = t->root; - * while (i) { - * int v = compare(n, i); - * if (v < 0) - * i = (*i)->left; - * else if (v > 0) - * i = (*i)->right; - * else - * break; - * } - * - * When you add nodes to a tree, the memory contents of the node do not matter. - * That is, there is no need to initialize the node via c_rbnode_init(). - * However, if you relink nodes multiple times during their lifetime, it is - * usually very convenient to use c_rbnode_init() and c_rbtree_remove_init(). - * In those cases, you should validate that a node is unlinked before you call - * c_rbtree_add(). - */ -void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) { - assert(t); - assert(l); - assert(n); - assert(!p || l == &p->left || l == &p->right); - assert(p || l == &t->root); - - c_rbnode_set_parent_and_color(n, p, C_RBNODE_RED); - n->left = n->right = NULL; - *l = n; - - c_rbtree_paint(t, n); -} - -static inline CRBNode *c_rbtree_rebalance_one(CRBTree *t, CRBNode *p, CRBNode *n) { - CRBNode *s, *x, *y, *g; - - /* - * Rebalance tree after a node was removed. This happens only if you - * remove a black node and one path is now left with an unbalanced - * number or black nodes. - * This function assumes all paths through p and n have one black node - * less than all other paths. If recursive fixup is required, the - * current node is returned. - */ - - if (n == p->left) { - s = p->right; - if (c_rbnode_is_red(s)) { - /* Case 3: - * We have a red node as sibling. Rotate it onto our - * side so we can later on turn it black. This way, we - * gain the additional black node in our path. */ - g = c_rbnode_parent(p); - x = s->left; - p->right = x; - s->left = p; - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED); - c_rbtree_swap_child(t, g, p, s); - s = x; - } - - x = s->right; - if (!x || c_rbnode_is_black(x)) { - y = s->left; - if (!y || c_rbnode_is_black(y)) { - /* Case 4: - * Our sibling is black and has only black - * children. Flip it red and turn parent black. - * This way we gained a black node in our path, - * or we fix it recursively one layer up, which - * will rotate the red sibling as parent. */ - c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED); - if (c_rbnode_is_black(p)) - return p; - - c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK); - return NULL; - } - - /* Case 5: - * Left child of our sibling is red, right one is black. - * Rotate on parent so the right child of our sibling is - * now red, and we can fall through to case 6. */ - x = y->right; - s->left = y->right; - y->right = s; - p->right = y; - if (x) - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - x = s; - s = y; - } - - /* Case 6: - * The right child of our sibling is red. Rotate left and flip - * colors, which gains us an additional black node in our path, - * that was previously on our sibling. */ - g = c_rbnode_parent(p); - y = s->left; - p->right = y; - s->left = p; - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - if (y) - c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y)); - c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK); - c_rbtree_swap_child(t, g, p, s); - } else /* if (!n || n == p->right) */ { /* same as above, but mirrored */ - s = p->left; - if (c_rbnode_is_red(s)) { - g = c_rbnode_parent(p); - x = s->right; - p->left = x; - s->right = p; - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(s, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED); - c_rbtree_swap_child(t, g, p, s); - s = x; - } - - x = s->left; - if (!x || c_rbnode_is_black(x)) { - y = s->right; - if (!y || c_rbnode_is_black(y)) { - c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED); - if (c_rbnode_is_black(p)) - return p; - - c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK); - return NULL; - } - - x = y->left; - s->right = y->left; - y->left = s; - p->left = y; - if (x) - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - x = s; - s = y; - } - - g = c_rbnode_parent(p); - y = s->right; - p->left = y; - s->right = p; - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - if (y) - c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y)); - c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK); - c_rbtree_swap_child(t, g, p, s); - } - - return NULL; -} - -static inline void c_rbtree_rebalance(CRBTree *t, CRBNode *p) { - CRBNode *n = NULL; - - assert(t); - assert(p); - - do { - n = c_rbtree_rebalance_one(t, p, n); - p = n ? c_rbnode_parent(n) : NULL; - } while (p); -} - -/** - * c_rbtree_remove() - remove node from tree - * @t: tree to operate one - * @n: node to remove - * - * This removes the given node from its tree. Once unlinked, the tree is - * rebalanced. - * The caller *must* ensure that the given tree is actually the tree it is - * linked on. Otherwise, behavior is undefined. - * - * This does *NOT* reset @n to being unlinked (for performance reason, this - * function *never* modifies @n at all). If you need this, use - * c_rbtree_remove_init(). - */ -void c_rbtree_remove(CRBTree *t, CRBNode *n) { - CRBNode *p, *s, *gc, *x, *next = NULL; - unsigned long c; - - assert(t); - assert(n); - assert(c_rbnode_is_linked(n)); - - /* - * There are three distinct cases during node removal of a tree: - * * The node has no children, in which case it can simply be removed. - * * The node has exactly one child, in which case the child displaces - * its parent. - * * The node has two children, in which case there is guaranteed to - * be a successor to the node (successor being the node ordered - * directly after it). This successor cannot have two children by - * itself (two interior nodes can never be successive). Therefore, - * we can simply swap the node with its successor (including color) - * and have reduced this case to either of the first two. - * - * Whenever the node we removed was black, we have to rebalance the - * tree. Note that this affects the actual node we _remove_, not @n (in - * case we swap it). - * - * p: parent - * s: successor - * gc: grand-...-child - * x: temporary - * next: next node to rebalance on - */ - - if (!n->left) { - /* - * Case 1: - * The node has no left child. If it neither has a right child, - * it is a leaf-node and we can simply unlink it. If it also - * was black, we have to rebalance, as always if we remove a - * black node. - * But if the node has a right child, the child *must* be red - * (otherwise, the right path has more black nodes as the - * non-existing left path), and the node to be removed must - * hence be black. We simply replace the node with its child, - * turning the red child black, and thus no rebalancing is - * required. - */ - p = c_rbnode_parent(n); - c = c_rbnode_color(n); - c_rbtree_swap_child(t, p, n, n->right); - if (n->right) - c_rbnode_set_parent_and_color(n->right, p, c); - else - next = (c == C_RBNODE_BLACK) ? p : NULL; - } else if (!n->right) { - /* - * Case 1.1: - * The node has exactly one child, and it is on the left. Treat - * it as mirrored case of Case 1 (i.e., replace the node by its - * child). - */ - p = c_rbnode_parent(n); - c = c_rbnode_color(n); - c_rbtree_swap_child(t, p, n, n->left); - c_rbnode_set_parent_and_color(n->left, p, c); - } else { - /* - * Case 2: - * We are dealing with a full interior node with a child not on - * both sides. Find its successor and swap it. Then remove the - * node similar to Case 1. For performance reasons we don't - * perform the full swap, but skip links that are about to be - * removed, anyway. - */ - s = n->right; - if (!s->left) { - /* right child is next, no need to touch grandchild */ - p = s; - gc = s->right; - } else { - /* find successor and swap partially */ - s = c_rbnode_leftmost(s); - p = c_rbnode_parent(s); - - gc = s->right; - p->left = s->right; - s->right = n->right; - c_rbnode_set_parent(n->right, s); - } - - /* node is partially swapped, now remove as in Case 1 */ - s->left = n->left; - c_rbnode_set_parent(n->left, s); - - x = c_rbnode_parent(n); - c = c_rbnode_color(n); - c_rbtree_swap_child(t, x, n, s); - if (gc) - c_rbnode_set_parent_and_color(gc, p, C_RBNODE_BLACK); - else - next = c_rbnode_is_black(s) ? p : NULL; - c_rbnode_set_parent_and_color(s, x, c); - } - - if (next) - c_rbtree_rebalance(t, next); -} diff --git a/src/basic/c-rbtree.h b/src/basic/c-rbtree.h deleted file mode 100644 index 20c5515ca1..0000000000 --- a/src/basic/c-rbtree.h +++ /dev/null @@ -1,297 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. See COPYING for details. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -/* - * Standalone Red-Black-Tree Implementation in Standard ISO-C11 - * - * This header provides an RB-Tree API, that is fully implemented in ISO-C11 - * and has no external dependencies. Furthermore, tree traversal, memory - * allocations, and key comparisons a fully in control of the API user. The - * implementation only provides the RB-Tree specific rebalancing and coloring. - * - * A tree is represented by the "CRBTree" structure. It contains a *singly* - * field, which is a pointer to the root node. If NULL, the tree is empty. If - * non-NULL, there is at least a single element in the tree. - * - * Each node of the tree is represented by the "CRBNode" structure. It has - * three fields. The @left and @right members can be accessed by the API user - * directly to traverse the tree. The third member is an implementation detail - * and encodes the parent pointer and color of the node. - * API users are required to embed the CRBNode object into their own objects - * and then use offsetof() (i.e., container_of() and friends) to turn CRBNode - * pointers into pointers to their own structure. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct CRBNode CRBNode; -typedef struct CRBTree CRBTree; - -/** - * struct CRBNode - Node of a Red-Black Tree - * @__parent_and_color: internal state - * @left: left child, or NULL - * @right: right child, or NULL - * - * Each node in an RB-Tree must embed an CRBNode object. This object contains - * pointers to its left and right child, which can be freely accessed by the - * API user at any time. They are NULL, if the node does not have a left/right - * child. - * - * The @__parent_and_color field must never be accessed directly. It encodes - * the pointer to the parent node, and the color of the node. Use the accessor - * functions instead. - * - * There is no reason to initialize a CRBNode object before linking it. - * However, if you need a boolean state that tells you whether the node is - * linked or not, you should initialize the node via c_rbnode_init() or - * C_RBNODE_INIT. - */ -struct CRBNode { - CRBNode *__parent_and_color; - CRBNode *left; - CRBNode *right; -}; - -#define C_RBNODE_INIT(_var) { .__parent_and_color = &(_var) } - -CRBNode *c_rbnode_leftmost(CRBNode *n); -CRBNode *c_rbnode_rightmost(CRBNode *n); -CRBNode *c_rbnode_next(CRBNode *n); -CRBNode *c_rbnode_prev(CRBNode *n); - -/** - * struct CRBTree - Red-Black Tree - * @root: pointer to the root node, or NULL - * - * Each Red-Black Tree is rooted in an CRBTree object. This object contains a - * pointer to the root node of the tree. The API user is free to access the - * @root member at any time, and use it to traverse the tree. - * - * To initialize an RB-Tree, set it to NULL / all zero. - */ -struct CRBTree { - CRBNode *root; -}; - -CRBNode *c_rbtree_first(CRBTree *t); -CRBNode *c_rbtree_last(CRBTree *t); - -void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n); -void c_rbtree_remove(CRBTree *t, CRBNode *n); - -/** - * c_rbnode_init() - mark a node as unlinked - * @n: node to operate on - * - * This marks the node @n as unlinked. The node will be set to a valid state - * that can never happen if the node is linked in a tree. Furthermore, this - * state is fully known to the implementation, and as such handled gracefully - * in all cases. - * - * You are *NOT* required to call this on your node. c_rbtree_add() can handle - * uninitialized nodes just fine. However, calling this allows to use - * c_rbnode_is_linked() to check for the state of a node. Furthermore, - * iterators and accessors can be called on initialized (yet unlinked) nodes. - * - * Use the C_RBNODE_INIT macro if you want to initialize static variables. - */ -static inline void c_rbnode_init(CRBNode *n) { - *n = (CRBNode)C_RBNODE_INIT(*n); -} - -/** - * c_rbnode_is_linked() - check whether a node is linked - * @n: node to check, or NULL - * - * This checks whether the passed node is linked. If you pass NULL, or if the - * node is not linked into a tree, this will return false. Otherwise, this - * returns true. - * - * Note that you must have either linked the node or initialized it, before - * calling this function. Never call this function on uninitialized nodes. - * Furthermore, removing a node via c_rbtree_remove() does *NOT* mark the node - * as unlinked. You have to call c_rbnode_init() yourself after removal, or use - * the c_rbtree_remove_init() helper. - * - * Return: true if the node is linked, false if not. - */ -static inline _Bool c_rbnode_is_linked(CRBNode *n) { - return n && n->__parent_and_color != n; -} - -/** - * c_rbnode_parent() - return parent pointer - * @n node to access - * - * This returns a pointer to the parent of the given node @n. If @n does not - * have a parent, NULL is returned. If @n is not linked, @n itself is returned. - * - * You should not call this on unlinked or uninitialized nodes! If you do, you - * better know how its semantics. - * - * Return: Pointer to parent. - */ -static inline CRBNode *c_rbnode_parent(CRBNode *n) { - return (CRBNode*)((unsigned long)n->__parent_and_color & ~1UL); -} - -/** - * c_rbtree_remove_init() - safely remove node from tree and reinitialize it - * @t: tree to operate on - * @n: node to remove, or NULL - * - * This is almost the same as c_rbtree_remove(), but extends it slightly, to be - * more convenient to use in many cases: - * - if @n is unlinked or NULL, this is a no-op - * - @n is reinitialized after being removed - */ -static inline void c_rbtree_remove_init(CRBTree *t, CRBNode *n) { - if (c_rbnode_is_linked(n)) { - c_rbtree_remove(t, n); - c_rbnode_init(n); - } -} - -/** - * CRBCompareFunc - compare a node to a key - * @t: tree where the node is linked to - * @k: key to compare - * @n: node to compare - * - * If you use the tree-traversal helpers (which are optional), you need to - * provide this callback so they can compare nodes in a tree to the key you - * look for. - * - * The tree @t is provided as optional context to this callback. The key you - * look for is provided as @k, the current node that should be compared to is - * provided as @n. This function should work like strcmp(), that is, return -1 - * if @key orders before @n, 0 if both compare equal, and 1 if it orders after - * @n. - */ -typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n); - -/** - * c_rbtree_find_node() - find node - * @t: tree to search through - * @f: comparison function - * @k: key to search for - * - * This searches through @t for a node that compares equal to @k. The function - * @f must be provided by the caller, which is used to compare nodes to @k. See - * the documentation of CRBCompareFunc for details. - * - * If there are multiple entries that compare equal to @k, this will return a - * pseudo-randomly picked node. If you need stable lookup functions for trees - * where duplicate entries are allowed, you better code your own lookup. - * - * Return: Pointer to matching node, or NULL. - */ -static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) { - CRBNode *i; - - assert(t); - assert(f); - - i = t->root; - while (i) { - int v = f(t, (void *)k, i); - if (v < 0) - i = i->left; - else if (v > 0) - i = i->right; - else - return i; - } - - return NULL; -} - -/** - * c_rbtree_find_entry() - find entry - * @_t: tree to search through - * @_f: comparison function - * @_k: key to search for - * @_t: type of the structure that embeds the nodes - * @_o: name of the node-member in type @_t - * - * This is very similar to c_rbtree_find_node(), but instead of returning a - * pointer to the CRBNode, it returns a pointer to the surrounding object. This - * object must embed the CRBNode object. The type of the surrounding object - * must be given as @_t, and the name of the embedded CRBNode member as @_o. - * - * See c_rbtree_find_node() for more details. - * - * Return: Pointer to found entry, NULL if not found. - */ -#define c_rbtree_find_entry(_m, _f, _k, _t, _o) \ - ((_t *)(((char *)c_rbtree_find_node((_m), (_f), (_k)) ?: \ - (char *)NULL + offsetof(_t, _o)) - offsetof(_t, _o))) - -/** - * c_rbtree_find_slot() - find slot to insert new node - * @t: tree to search through - * @f: comparison function - * @k: key to search for - * @p: output storage for parent pointer - * - * This searches through @t just like c_rbtree_find_node() does. However, - * instead of returning a pointer to a node that compares equal to @k, this - * searches for a slot to insert a node with key @k. A pointer to the slot is - * returned, and a pointer to the parent of the slot is stored in @p. Both - * can be passed directly to c_rbtree_add(), together with your node to insert. - * - * If there already is a node in the tree, that compares equal to @k, this will - * return NULL and store the conflicting node in @p. In all other cases, - * this will return a pointer (non-NULL) to the empty slot to insert the node - * at. @p will point to the parent node of that slot. - * - * If you want trees that allow duplicate nodes, you better code your own - * insertion function. - * - * Return: Pointer to slot to insert node, or NULL on conflicts. - */ -static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) { - CRBNode **i; - - assert(t); - assert(f); - assert(p); - - i = &t->root; - *p = NULL; - while (*i) { - int v = f(t, (void *)k, *i); - *p = *i; - if (v < 0) - i = &(*i)->left; - else if (v > 0) - i = &(*i)->right; - else - return NULL; - } - - return i; -} - -#ifdef __cplusplus -} -#endif diff --git a/src/basic/def.h b/src/basic/def.h index 963343eb7d..1a7a0f4928 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -41,8 +41,6 @@ #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE -#define REBOOT_PARAM_FILE "/run/systemd/reboot-param" - #ifdef HAVE_SPLIT_USR #define KBD_KEYMAP_DIRS \ "/usr/share/keymaps/\0" \ diff --git a/src/basic/missing.h b/src/basic/missing.h index 66cd5921ad..6616f0b720 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -746,7 +746,40 @@ struct btrfs_ioctl_quota_ctl_args { #define IFLA_BR_AGEING_TIME 4 #define IFLA_BR_STP_STATE 5 #define IFLA_BR_PRIORITY 6 -#define __IFLA_BR_MAX 7 +#define IFLA_BR_VLAN_FILTERING 7 +#define IFLA_BR_VLAN_PROTOCOL 8 +#define IFLA_BR_GROUP_FWD_MASK 9 +#define IFLA_BR_ROOT_ID 10 +#define IFLA_BR_BRIDGE_ID 11 +#define IFLA_BR_ROOT_PORT 12 +#define IFLA_BR_ROOT_PATH_COST 13 +#define IFLA_BR_TOPOLOGY_CHANGE 14 +#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15 +#define IFLA_BR_HELLO_TIMER 16 +#define IFLA_BR_TCN_TIMER 17 +#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18 +#define IFLA_BR_GC_TIMER 19 +#define IFLA_BR_GROUP_ADDR 20 +#define IFLA_BR_FDB_FLUSH 21 +#define IFLA_BR_MCAST_ROUTER 22 +#define IFLA_BR_MCAST_SNOOPING 23 +#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24 +#define IFLA_BR_MCAST_QUERIER 25 +#define IFLA_BR_MCAST_HASH_ELASTICITY 26 +#define IFLA_BR_MCAST_HASH_MAX 27 +#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28 +#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29 +#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30 +#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31 +#define IFLA_BR_MCAST_QUERIER_INTVL 32 +#define IFLA_BR_MCAST_QUERY_INTVL 33 +#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34 +#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35 +#define IFLA_BR_NF_CALL_IPTABLES 36 +#define IFLA_BR_NF_CALL_IP6TABLES 37 +#define IFLA_BR_NF_CALL_ARPTABLES 38 +#define IFLA_BR_VLAN_DEFAULT_PVID 39 +#define __IFLA_BR_MAX 40 #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) #endif @@ -795,6 +828,10 @@ struct btrfs_ioctl_quota_ctl_args { #define IPV6_UNICAST_IF 76 #endif +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif + #ifndef IFF_MULTI_QUEUE #define IFF_MULTI_QUEUE 0x100 #endif diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 822c09bfba..044a12889d 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -569,10 +569,10 @@ static int binary_is_good(const char *binary) { if (r < 0) return r; - return !path_equal(d, "true") && - !path_equal(d, "/bin/true") && - !path_equal(d, "/usr/bin/true") && - !path_equal(d, "/dev/null"); + return !PATH_IN_SET(d, "true" + "/bin/true", + "/usr/bin/true", + "/dev/null"); } int fsck_exists(const char *fstype) { diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 2c2f87a9f2..34d5cd1570 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -48,6 +48,23 @@ bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b); char* path_join(const char *root, const char *path, const char *rest); +static inline bool path_equal_ptr(const char *a, const char *b) { + return !!a == !!b && (!a || path_equal(a, b)); +} + +/* Note: the search terminates on the first NULL item. */ +#define PATH_IN_SET(p, ...) \ + ({ \ + char **s; \ + bool _found = false; \ + STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ + if (path_equal(p, *s)) { \ + _found = true; \ + break; \ + } \ + _found; \ + }) + int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *prefix); char** path_strv_resolve_uniq(char **l, const char *prefix); diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h index 6d03268919..40b5b527d5 100644 --- a/src/basic/rm-rf.h +++ b/src/basic/rm-rf.h @@ -30,3 +30,12 @@ typedef enum RemoveFlags { int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); int rm_rf(const char *path, RemoveFlags flags); + +/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ +static inline void rm_rf_and_free(char *p) { + if (!p) + return; + (void) rm_rf(p, REMOVE_ROOT); + free(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_and_free); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index e3047b209b..280b5c3251 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -255,7 +255,7 @@ int signal_from_string(const char *s) { } if (safe_atou(s, &u) >= 0) { signo = (int) u + offset; - if (signo > 0 && signo < _NSIG) + if (SIGNAL_VALID(signo)) return signo; } return -EINVAL; diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index a7322ff26a..dfd6eb564d 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -50,3 +50,7 @@ static inline void block_signals_reset(sigset_t *ss) { assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ t; \ }) + +static inline bool SIGNAL_VALID(int signo) { + return signo > 0 && signo < _NSIG; +} diff --git a/src/basic/strv.c b/src/basic/strv.c index 8282298dca..97a96e5762 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -558,6 +558,42 @@ int strv_extend(char ***l, const char *value) { return strv_consume(l, v); } +int strv_extend_front(char ***l, const char *value) { + size_t n, m; + char *v, **c; + + assert(l); + + /* Like strv_extend(), but prepends rather than appends the new entry */ + + if (!value) + return 0; + + n = strv_length(*l); + + /* Increase and overflow check. */ + m = n + 2; + if (m < n) + return -ENOMEM; + + v = strdup(value); + if (!v) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) { + free(v); + return -ENOMEM; + } + + memmove(c+1, c, n * sizeof(char*)); + c[0] = v; + c[n+1] = NULL; + + *l = c; + return 0; +} + char **strv_uniq(char **l) { char **i; diff --git a/src/basic/strv.h b/src/basic/strv.h index 7bfa54408d..f61bbb5386 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -50,6 +50,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); +int strv_extend_front(char ***l, const char *value); int strv_push(char ***l, char *value); int strv_push_pair(char ***l, char *a, char *b); int strv_push_prepend(char ***l, char *value); diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 0a9d2bbdef..9521b79daa 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1135,14 +1135,19 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { } bool colors_enabled(void) { - const char *colors; + static int enabled = -1; - colors = getenv("SYSTEMD_COLORS"); - if (!colors) { - if (streq_ptr(getenv("TERM"), "dumb")) - return false; - return on_tty(); + if (_unlikely_(enabled < 0)) { + const char *colors; + + colors = getenv("SYSTEMD_COLORS"); + if (colors) + enabled = parse_boolean(colors) != 0; + else if (streq_ptr(getenv("TERM"), "dumb")) + enabled = false; + else + enabled = on_tty(); } - return parse_boolean(colors) != 0; + return enabled; } diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 19155bce53..f65ca3edaa 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -30,6 +30,7 @@ #include <sys/stat.h> #include <unistd.h> +#include "missing.h" #include "alloc-util.h" #include "fd-util.h" #include "formats-util.h" diff --git a/src/basic/util.c b/src/basic/util.c index ea1bed7ceb..b70c50047f 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -55,6 +55,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "umask-util.h" #include "user-util.h" #include "util.h" @@ -419,13 +420,17 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa _exit(EXIT_FAILURE); } - if (!stdout_is_tty) - dup2(fd, STDOUT_FILENO); + if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } - if (!stderr_is_tty) - dup2(fd, STDERR_FILENO); + if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } - if (fd > 2) + if (fd > STDERR_FILENO) close(fd); } @@ -773,15 +778,25 @@ uint64_t physical_memory(void) { return (uint64_t) mem * (uint64_t) page_size(); } -int update_reboot_param_file(const char *param) { - int r = 0; +int update_reboot_parameter_and_warn(const char *param) { + int r; + + if (isempty(param)) { + if (unlink("/run/systemd/reboot-param") < 0) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); + } - if (param) { - r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE); + return 0; + } + + RUN_WITH_UMASK(0022) { + r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); if (r < 0) - return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m"); - } else - (void) unlink(REBOOT_PARAM_FILE); + return log_warning_errno(r, "Failed to write reboot parameter file: %m"); + } return 0; } diff --git a/src/basic/util.h b/src/basic/util.h index 286db05159..1c032c15c9 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -184,6 +184,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int uint64_t physical_memory(void); -int update_reboot_param_file(const char *param); +int update_reboot_parameter_and_warn(const char *param); int version(void); diff --git a/src/core/automount.c b/src/core/automount.c index 5dc6fd98e7..7c55d7bc49 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -149,7 +149,7 @@ static int automount_add_default_dependencies(Automount *a) { if (!UNIT(a)->default_dependencies) return 0; - if (UNIT(a)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(a)->manager)) return 0; r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); diff --git a/src/core/busname.c b/src/core/busname.c index de2a21ccde..bbe61af4f0 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -149,7 +149,7 @@ static int busname_add_default_default_dependencies(BusName *n) { if (r < 0) return r; - if (UNIT(n)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -318,7 +318,7 @@ static int busname_open_fd(BusName *n) { if (n->starter_fd >= 0) return 0; - mode = UNIT(n)->manager->running_as == MANAGER_SYSTEM ? "system" : "user"; + mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user"; n->starter_fd = bus_kernel_open_bus_fd(mode, &path); if (n->starter_fd < 0) return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus"); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 9c34928052..25cc6962f9 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1265,7 +1265,7 @@ int manager_setup_cgroup(Manager *m) { * it. This is to support live upgrades from older systemd * versions where PID 1 was moved there. Also see * cg_get_root_path(). */ - if (!e && m->running_as == MANAGER_SYSTEM) { + if (!e && MANAGER_IS_SYSTEM(m)) { e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); if (!e) e = endswith(m->cgroup_root, "/system"); /* even more legacy */ @@ -1318,7 +1318,7 @@ int manager_setup_cgroup(Manager *m) { (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify"); - } else if (m->running_as == MANAGER_SYSTEM) { + } else if (MANAGER_IS_SYSTEM(m)) { /* On the legacy hierarchy we only get * notifications via cgroup agents. (Which diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 973a60187d..9dfca14914 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -837,9 +837,9 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { - if (isempty(uu)) { + if (isempty(uu)) c->user = mfree(c->user); - } else { + else { char *t; t = strdup(uu); @@ -864,9 +864,9 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { - if (isempty(gg)) { + if (isempty(gg)) c->group = mfree(c->group); - } else { + else { char *t; t = strdup(gg); diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index fc50fafaad..0f54c6b84b 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -58,7 +58,7 @@ int bus_kill_context_set_transient_property( k = kill_mode_from_string(m); if (k < 0) - return -EINVAL; + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m); if (mode != UNIT_CHECK) { c->kill_mode = k; @@ -75,7 +75,7 @@ int bus_kill_context_set_transient_property( if (r < 0) return r; - if (sig <= 0 || sig >= _NSIG) + if (!SIGNAL_VALID(sig)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig); if (mode != UNIT_CHECK) { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 00372b92b4..d48b0ca69d 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1187,7 +1187,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error * if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers."); m->exit_code = MANAGER_REBOOT; @@ -1206,7 +1206,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers."); m->exit_code = MANAGER_POWEROFF; @@ -1225,7 +1225,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers."); m->exit_code = MANAGER_HALT; @@ -1244,7 +1244,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers."); m->exit_code = MANAGER_KEXEC; @@ -1265,7 +1265,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager."); r = sd_bus_message_read(message, "ss", &root, &init); @@ -1433,7 +1433,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_ if (r < 0) return r; - if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers."); m->return_value = code; @@ -1466,7 +1466,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu if (!h) return -ENOMEM; - r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h); + r = unit_file_get_list(m->unit_file_scope, NULL, h); if (r < 0) goto fail; @@ -1498,7 +1498,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s Manager *m = userdata; const char *name; UnitFileState state; - UnitFileScope scope; int r; assert(message); @@ -1514,9 +1513,7 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s if (r < 0) return r; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_get_state(scope, NULL, name, &state); + r = unit_file_get_state(m->unit_file_scope, NULL, name, &state); if (r < 0) return r; @@ -1526,7 +1523,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *default_target = NULL; Manager *m = userdata; - UnitFileScope scope; int r; assert(message); @@ -1538,9 +1534,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd if (r < 0) return r; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_get_default(scope, NULL, &default_target); + r = unit_file_get_default(m->unit_file_scope, NULL, &default_target); if (r < 0) return r; @@ -1571,7 +1565,7 @@ static int reply_unit_file_changes_and_free( unsigned i; int r; - if (n_changes > 0) { + if (unit_file_changes_have_modification(changes, n_changes)) { r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); if (r < 0) log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); @@ -1591,15 +1585,19 @@ static int reply_unit_file_changes_and_free( if (r < 0) goto fail; - for (i = 0; i < n_changes; i++) { - r = sd_bus_message_append( - reply, "(sss)", - unit_file_change_type_to_string(changes[i].type), - changes[i].path, - changes[i].source); - if (r < 0) - goto fail; - } + for (i = 0; i < n_changes; i++) + if (changes[i].type >= 0) { + const char *change = unit_file_change_type_to_string(changes[i].type); + assert(change != NULL); + + r = sd_bus_message_append( + reply, "(sss)", + change, + changes[i].path, + changes[i].source); + if (r < 0) + goto fail; + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -1613,6 +1611,58 @@ fail: return r; } +/* Create an error reply, using the error information from changes[] + * if possible, and fall back to generating an error from error code c. + * The error message only describes the first error. + * + * Coordinate with unit_file_dump_changes() in install.c. + */ +static int install_error( + sd_bus_error *error, + int c, + UnitFileChange *changes, + unsigned n_changes) { + int r; + unsigned i; + assert(c < 0); + + for (i = 0; i < n_changes; i++) + switch(changes[i].type) { + case 0 ... INT_MAX: + continue; + case -EEXIST: + if (changes[i].source) + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "File %s already exists and is a symlink to %s.", + changes[i].path, changes[i].source); + else + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "File %s already exists.", + changes[i].path); + goto found; + case -ERFKILL: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, + "Unit file %s is masked.", changes[i].path); + goto found; + case -EADDRNOTAVAIL: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, + "Unit %s is transient or generated.", changes[i].path); + goto found; + case -ELOOP: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, + "Refusing to operate on linked unit file %s", changes[i].path); + goto found; + default: + r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path); + goto found; + } + + r = c; + found: + unit_file_changes_free(changes, n_changes); + return r; +} + static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, @@ -1624,7 +1674,6 @@ static int method_enable_unit_files_generic( _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int runtime, force, r; assert(message); @@ -1644,13 +1693,9 @@ static int method_enable_unit_files_generic( if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = call(scope, runtime, NULL, l, force, &changes, &n_changes); - if (r == -ESHUTDOWN) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); + r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes); } @@ -1686,7 +1731,6 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use unsigned n_changes = 0; Manager *m = userdata; UnitFilePresetMode mm; - UnitFileScope scope; int runtime, force, r; const char *mode; @@ -1715,26 +1759,23 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes); + r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, r, changes, n_changes); } static int method_disable_unit_files_generic( sd_bus_message *message, - Manager *m, const - char *verb, + Manager *m, + const char *verb, int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int r, runtime; assert(message); @@ -1748,17 +1789,15 @@ static int method_disable_unit_files_generic( if (r < 0) return r; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - r = bus_verify_manage_unit_files_async(m, message, error); if (r < 0) return r; if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = call(scope, runtime, NULL, l, &changes, &n_changes); + r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1771,11 +1810,37 @@ static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_ return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error); } +static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { UnitFileChange *changes = NULL; unsigned n_changes = 0; Manager *m = userdata; - UnitFileScope scope; const char *name; int force, r; @@ -1796,11 +1861,9 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_set_default(scope, NULL, name, force, &changes, &n_changes); + r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1810,7 +1873,6 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, unsigned n_changes = 0; Manager *m = userdata; UnitFilePresetMode mm; - UnitFileScope scope; const char *mode; int force, runtime, r; @@ -1839,13 +1901,9 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes); - if (r < 0) { - unit_file_changes_free(changes, n_changes); - return r; - } + r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1855,10 +1913,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd Manager *m = userdata; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int runtime, force, r; - char *target; - char *type; + char *target, *type; UnitDependency dep; assert(message); @@ -1882,13 +1938,9 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd if (dep < 0) return -EINVAL; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); - if (r == -ESHUTDOWN) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); + r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1924,7 +1976,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0), SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.unit_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), @@ -2025,6 +2077,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index bc121b83a2..a0e61b023e 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -156,7 +156,7 @@ static int property_get_next_elapse_monotonic( usec_t a, b; a = now(CLOCK_MONOTONIC); - b = now(CLOCK_BOOTTIME); + b = now(clock_boottime_or_monotonic()); if (t->next_elapse_monotonic_or_boottime + a > b) x = t->next_elapse_monotonic_or_boottime + a - b; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index b351f6a2c2..c507265070 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -27,6 +27,7 @@ #include "locale-util.h" #include "log.h" #include "selinux-access.h" +#include "signal-util.h" #include "special.h" #include "string-util.h" #include "strv.h" @@ -547,7 +548,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho); } - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range."); r = bus_verify_manage_units_async_full( @@ -1002,7 +1003,6 @@ int bus_unit_queue_job( type = JOB_TRY_RELOAD; } - if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) @@ -1259,6 +1259,7 @@ int bus_unit_set_properties( } int bus_unit_check_load_state(Unit *u, sd_bus_error *error) { + assert(u); if (u->load_state == UNIT_LOADED) return 0; diff --git a/src/core/dbus.c b/src/core/dbus.c index 413489373f..263955d874 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -112,7 +112,7 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus manager_notify_cgroup_empty(m, cgroup); /* if running as system-instance, forward under our name */ - if (m->running_as == MANAGER_SYSTEM && m->system_bus) { + if (MANAGER_IS_SYSTEM(m) && m->system_bus) { r = sd_bus_message_rewind(message, 1); if (r >= 0) r = sd_bus_send(m->system_bus, message, NULL); @@ -690,7 +690,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* When we run as system instance we get the Released * signal via a direct connection */ @@ -864,10 +864,10 @@ static int bus_init_api(Manager *m) { return 0; /* The API and system bus is the same if we are running in system mode */ - if (m->running_as == MANAGER_SYSTEM && m->system_bus) + if (MANAGER_IS_SYSTEM(m) && m->system_bus) bus = sd_bus_ref(m->system_bus); else { - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) r = sd_bus_open_system(&bus); else r = sd_bus_open_user(&bus); @@ -907,7 +907,7 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { assert(bus); /* On kdbus or if we are a user instance we get the Released message via the system bus */ - if (m->running_as == MANAGER_USER || m->kdbus_fd >= 0) { + if (MANAGER_IS_USER(m) || m->kdbus_fd >= 0) { r = sd_bus_add_match( bus, NULL, @@ -932,7 +932,7 @@ static int bus_init_system(Manager *m) { return 0; /* The API and system bus is the same if we are running in system mode */ - if (m->running_as == MANAGER_SYSTEM && m->api_bus) { + if (MANAGER_IS_SYSTEM(m) && m->api_bus) { m->system_bus = sd_bus_ref(m->api_bus); return 0; } @@ -983,7 +983,7 @@ static int bus_init_private(Manager *m) { if (m->kdbus_fd >= 0) return 0; - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* We want the private bus only when running as init */ if (getpid() != 1) @@ -1082,7 +1082,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) { /* Possibly flush unwritten data, but only if we are * unprivileged, since we don't want to sync here */ - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) sd_bus_flush(*bus); /* And destroy the object */ diff --git a/src/core/device.c b/src/core/device.c index 0671620a3e..d01bec53d8 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -265,7 +265,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { assert(u); assert(dev); - property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; + property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; wants = udev_device_get_property_value(dev, property); if (!wants) return 0; diff --git a/src/core/failure-action.c b/src/core/failure-action.c index bb2bc3f399..ddae46190f 100644 --- a/src/core/failure-action.c +++ b/src/core/failure-action.c @@ -47,7 +47,7 @@ int failure_action( if (action == FAILURE_ACTION_NONE) return -ECANCELED; - if (m->running_as == MANAGER_USER) { + if (!MANAGER_IS_SYSTEM(m)) { /* Downgrade all options to simply exiting if we run * in user mode */ @@ -61,17 +61,17 @@ int failure_action( case FAILURE_ACTION_REBOOT: log_and_status(m, "Rebooting as result of failure."); - update_reboot_param_file(reboot_arg); - (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, - JOB_REPLACE_IRREVERSIBLY, NULL); + (void) update_reboot_parameter_and_warn(reboot_arg); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); break; case FAILURE_ACTION_REBOOT_FORCE: log_and_status(m, "Forcibly rebooting as result of failure."); - update_reboot_param_file(reboot_arg); + (void) update_reboot_parameter_and_warn(reboot_arg); m->exit_code = MANAGER_REBOOT; + break; case FAILURE_ACTION_REBOOT_IMMEDIATE: @@ -79,9 +79,10 @@ int failure_action( sync(); - if (reboot_arg) { + if (!isempty(reboot_arg)) { log_info("Rebooting with argument '%s'.", reboot_arg); syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); } log_info("Rebooting."); @@ -90,8 +91,7 @@ int failure_action( case FAILURE_ACTION_POWEROFF: log_and_status(m, "Powering off as result of failure."); - (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, - JOB_REPLACE_IRREVERSIBLY, NULL); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); break; case FAILURE_ACTION_POWEROFF_FORCE: diff --git a/src/core/job.c b/src/core/job.c index 5557a6a942..d9c5669c9f 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -137,7 +137,7 @@ void job_uninstall(Job *j) { /* Detach from next 'bigger' objects */ /* daemon-reload should be transparent to job observers */ - if (j->manager->n_reloading <= 0) + if (!MANAGER_IS_RELOADING(j->manager)) bus_job_send_removed_signal(j); *pj = NULL; @@ -1156,7 +1156,7 @@ void job_shutdown_magic(Job *j) { if (j->type != JOB_START) return; - if (j->unit->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(j->unit->manager)) return; if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET)) diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 22b71b6f5e..f83fa09301 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -44,6 +44,7 @@ static int add_dependency_consumer( } int unit_load_dropin(Unit *u) { + _cleanup_strv_free_ char **l = NULL; Iterator i; char *t, **f; int r; @@ -55,7 +56,7 @@ int unit_load_dropin(Unit *u) { SET_FOREACH(t, u->names, i) { char **p; - STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { + STRV_FOREACH(p, u->manager->lookup_paths.search_path) { unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS, add_dependency_consumer, u, NULL); unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES, @@ -63,11 +64,19 @@ int unit_load_dropin(Unit *u) { } } - u->dropin_paths = strv_free(u->dropin_paths); - r = unit_find_dropin_paths(u, &u->dropin_paths); + r = unit_find_dropin_paths(u, &l); if (r <= 0) return 0; + if (!u->dropin_paths) { + u->dropin_paths = l; + l = NULL; + } else { + r = strv_extend_strv(&u->dropin_paths, l, true); + if (r < 0) + return log_oom(); + } + STRV_FOREACH(f, u->dropin_paths) { config_parse(u->id, *f, NULL, UNIT_VTABLE(u)->sections, diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index d8a4aefbb3..942d26724e 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -25,7 +25,7 @@ /* Read service data supplementary drop-in directories */ static inline int unit_find_dropin_paths(Unit *u, char ***paths) { - return unit_file_find_dropin_paths(u->manager->lookup_paths.unit_path, + return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path, u->manager->unit_path_cache, u->names, paths); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index d078924c5b..c4566f7709 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2495,7 +2495,7 @@ int config_parse_syscall_filter( /* Turn on NNP, but only if it wasn't configured explicitly * before, and only if we are in user mode. */ - if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER) + if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager)) c->no_new_privileges = true; return 0; @@ -2847,11 +2847,12 @@ int config_parse_device_allow( void *data, void *userdata) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *t = NULL; CGroupContext *c = data; CGroupDeviceAllow *a; - const char *m; + const char *m = NULL; size_t n; + int r; if (isempty(rvalue)) { while (c->device_allow) @@ -2860,8 +2861,16 @@ int config_parse_device_allow( return 0; } - n = strcspn(rvalue, WHITESPACE); - path = strndup(rvalue, n); + r = unit_full_printf(userdata, rvalue, &t); + if(r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve specifiers in %s, ignoring: %m", + rvalue); + } + + n = strcspn(t, WHITESPACE); + + path = strndup(t, n); if (!path) return log_oom(); @@ -2872,7 +2881,7 @@ int config_parse_device_allow( return 0; } - m = rvalue + n + strspn(rvalue + n, WHITESPACE); + m = t + n + strspn(t + n, WHITESPACE); if (isempty(m)) m = "rwm"; @@ -3574,7 +3583,7 @@ static int load_from_path(Unit *u, const char *path) { } else { char **p; - STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { + STRV_FOREACH(p, u->manager->lookup_paths.search_path) { /* Instead of opening the path right away, we manually * follow all symlinks and add their name to our unit @@ -3620,10 +3629,12 @@ static int load_from_path(Unit *u, const char *path) { if (fstat(fileno(f), &st) < 0) return -errno; - if (null_or_empty(&st)) + if (null_or_empty(&st)) { u->load_state = UNIT_MASKED; - else { + u->fragment_mtime = 0; + } else { u->load_state = UNIT_LOADED; + u->fragment_mtime = timespec_load(&st.st_mtim); /* Now, parse the file contents */ r = config_parse(u->id, filename, f, @@ -3638,8 +3649,6 @@ static int load_from_path(Unit *u, const char *path) { u->fragment_path = filename; filename = NULL; - u->fragment_mtime = timespec_load(&st.st_mtim); - if (u->source_path) { if (stat(u->source_path, &st) >= 0) u->source_mtime = timespec_load(&st.st_mtim); diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 7b25349c07..9de528b6cf 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -261,10 +261,10 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) { RUN_WITH_UMASK(0022) { r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE); - } - if (r < 0) { - (void) unlink(run_machine_id); - return log_error_errno(r, "Cannot write %s: %m", run_machine_id); + if (r < 0) { + (void) unlink(run_machine_id); + return log_error_errno(r, "Cannot write %s: %m", run_machine_id); + } } /* And now, let's mount it over */ diff --git a/src/core/main.c b/src/core/main.c index 56df32426a..8dfb3928de 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -81,6 +81,7 @@ #include "strv.h" #include "switch-root.h" #include "terminal-util.h" +#include "umask-util.h" #include "user-util.h" #include "virt.h" #include "watchdog.h" @@ -94,7 +95,7 @@ static enum { ACTION_DONE } arg_action = ACTION_RUN; static char *arg_default_unit = NULL; -static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID; +static bool arg_system = false; static bool arg_dump_core = true; static int arg_crash_chvt = -1; static bool arg_crash_shell = false; @@ -688,11 +689,11 @@ static int parse_config_file(void) { const char *fn, *conf_dirs_nulstr; - fn = arg_running_as == MANAGER_SYSTEM ? + fn = arg_system ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf"; - conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ? + conf_dirs_nulstr = arg_system ? CONF_PATHS_NULSTR("systemd/system.conf.d") : CONF_PATHS_NULSTR("systemd/user.conf.d"); @@ -866,11 +867,11 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SYSTEM: - arg_running_as = MANAGER_SYSTEM; + arg_system = true; break; case ARG_USER: - arg_running_as = MANAGER_USER; + arg_system = false; break; case ARG_TEST: @@ -1237,7 +1238,8 @@ static int write_container_id(void) { if (isempty(c)) return 0; - r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); + RUN_WITH_UMASK(0022) + r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); if (r < 0) return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m"); @@ -1336,7 +1338,7 @@ int main(int argc, char *argv[]) { saved_argv = argv; saved_argc = argc; - log_show_color(isatty(STDERR_FILENO) > 0); + log_show_color(colors_enabled()); log_set_upgrade_syslog_to_journal(true); /* Disable the umask logic */ @@ -1346,7 +1348,7 @@ int main(int argc, char *argv[]) { if (getpid() == 1 && detect_container() <= 0) { /* Running outside of a container as PID 1 */ - arg_running_as = MANAGER_SYSTEM; + arg_system = true; make_null_stdio(); log_set_target(LOG_TARGET_KMSG); log_open(); @@ -1430,7 +1432,7 @@ int main(int argc, char *argv[]) { } else if (getpid() == 1) { /* Running inside a container, as PID 1 */ - arg_running_as = MANAGER_SYSTEM; + arg_system = true; log_set_target(LOG_TARGET_CONSOLE); log_close_console(); /* force reopen of /dev/console */ log_open(); @@ -1443,7 +1445,7 @@ int main(int argc, char *argv[]) { kernel_timestamp = DUAL_TIMESTAMP_NULL; } else { /* Running as user instance */ - arg_running_as = MANAGER_USER; + arg_system = false; log_set_target(LOG_TARGET_AUTO); log_open(); @@ -1501,7 +1503,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_system) { r = parse_proc_cmdline(parse_proc_cmdline_item); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); @@ -1522,14 +1524,14 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_USER && + if (!arg_system && arg_action == ACTION_RUN && sd_booted() <= 0) { log_error("Trying to run as user instance, but the system has not been booted with systemd."); goto finish; } - if (arg_running_as == MANAGER_SYSTEM && + if (arg_system && arg_action == ACTION_RUN && running_in_chroot() > 0) { log_error("Cannot be run in a chroot() environment."); @@ -1557,7 +1559,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_USER && + if (!arg_system && !getenv("XDG_RUNTIME_DIR")) { log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set."); goto finish; @@ -1580,7 +1582,7 @@ int main(int argc, char *argv[]) { if (arg_serialization) assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); - if (arg_running_as == MANAGER_SYSTEM) + if (arg_system) /* Become a session leader if we aren't one yet. */ setsid(); @@ -1589,7 +1591,7 @@ int main(int argc, char *argv[]) { /* Reset the console, but only if this is really init and we * are freshly booted */ - if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) { + if (arg_system && arg_action == ACTION_RUN) { /* If we are init, we connect stdin/stdout/stderr to * /dev/null and make sure we don't have a controlling @@ -1616,7 +1618,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_system) { int v; log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")", @@ -1652,7 +1654,7 @@ int main(int argc, char *argv[]) { arg_action == ACTION_TEST ? " test" : "", getuid(), t); } - if (arg_running_as == MANAGER_SYSTEM && !skip_setup) { + if (arg_system && !skip_setup) { if (arg_show_status > 0) status_welcome(); @@ -1664,7 +1666,7 @@ int main(int argc, char *argv[]) { test_usr(); } - if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) + if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) watchdog_set_timeout(&arg_runtime_watchdog); if (arg_timer_slack_nsec != NSEC_INFINITY) @@ -1694,12 +1696,12 @@ int main(int argc, char *argv[]) { } } - if (arg_running_as == MANAGER_USER) + if (!arg_system) /* Become reaper of our children */ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) log_warning_errno(errno, "Failed to make us a subreaper: %m"); - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_system) { bump_rlimit_nofile(&saved_rlimit_nofile); if (empty_etc) { @@ -1711,7 +1713,7 @@ int main(int argc, char *argv[]) { } } - r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m); + r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m); if (r < 0) { log_emergency_errno(r, "Failed to allocate manager object: %m"); error_message = "Failed to allocate manager object"; @@ -1874,7 +1876,7 @@ int main(int argc, char *argv[]) { case MANAGER_EXIT: retval = m->return_value; - if (m->running_as == MANAGER_USER) { + if (MANAGER_IS_USER(m)) { log_debug("Exit."); goto finish; } @@ -1970,7 +1972,7 @@ finish: args[i++] = SYSTEMD_BINARY_PATH; if (switch_root_dir) args[i++] = "--switched-root"; - args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user"; + args[i++] = arg_system ? "--system" : "--user"; args[i++] = "--deserialize"; args[i++] = sfd; args[i++] = NULL; diff --git a/src/core/manager.c b/src/core/manager.c index e739795e70..5601770670 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -49,6 +49,7 @@ #include "dbus-manager.h" #include "dbus-unit.h" #include "dbus.h" +#include "dirent-util.h" #include "env-util.h" #include "escape.h" #include "exit-status.h" @@ -63,6 +64,7 @@ #include "manager.h" #include "missing.h" #include "mkdir.h" +#include "mkdir.h" #include "parse-util.h" #include "path-lookup.h" #include "path-util.h" @@ -98,7 +100,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32 static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); static int manager_run_generators(Manager *m); -static void manager_undo_generators(Manager *m); static void manager_watch_jobs_in_progress(Manager *m) { usec_t next; @@ -491,7 +492,7 @@ static int manager_setup_signals(Manager *m) { if (r < 0) return r; - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) return enable_special_signals(m); return 0; @@ -518,7 +519,7 @@ static void manager_clean_environment(Manager *m) { static int manager_default_environment(Manager *m) { assert(m); - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* The system manager always starts with a clean * environment for its children. It does not import * the kernel or the parents exported variables. @@ -547,43 +548,36 @@ static int manager_default_environment(Manager *m) { } -int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { - - static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = { - [MANAGER_SYSTEM] = "UNIT=", - [MANAGER_USER] = "USER_UNIT=", - }; - - static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = { - [MANAGER_SYSTEM] = "UNIT=%s", - [MANAGER_USER] = "USER_UNIT=%s", - }; - +int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { Manager *m; int r; assert(_m); - assert(running_as >= 0); - assert(running_as < _MANAGER_RUNNING_AS_MAX); + assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER)); m = new0(Manager, 1); if (!m) return -ENOMEM; -#ifdef ENABLE_EFI - if (running_as == MANAGER_SYSTEM && detect_container() <= 0) - boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp); -#endif - - m->running_as = running_as; + m->unit_file_scope = scope; m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->default_timer_accuracy_usec = USEC_PER_MINUTE; m->default_tasks_accounting = true; m->default_tasks_max = UINT64_C(512); +#ifdef ENABLE_EFI + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) + boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp); +#endif + /* Prepare log fields we can use for structured logging */ - m->unit_log_field = unit_log_fields[running_as]; - m->unit_log_format_string = unit_log_format_strings[running_as]; + if (MANAGER_IS_SYSTEM(m)) { + m->unit_log_field = "UNIT="; + m->unit_log_format_string = "UNIT=%s"; + } else { + m->unit_log_field = "USER_UNIT="; + m->unit_log_format_string = "USER_UNIT=%s"; + } m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; @@ -683,6 +677,7 @@ static int manager_setup_notify(Manager *m) { .sa.sa_family = AF_UNIX, }; static const int one = 1; + const char *e; /* First free all secondary fields */ m->notify_socket = mfree(m->notify_socket); @@ -694,19 +689,13 @@ static int manager_setup_notify(Manager *m) { fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE); - if (m->running_as == MANAGER_SYSTEM) - m->notify_socket = strdup("/run/systemd/notify"); - else { - const char *e; - - e = getenv("XDG_RUNTIME_DIR"); - if (!e) { - log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m"); - return -EINVAL; - } - - m->notify_socket = strappend(e, "/systemd/notify"); + e = manager_get_runtime_prefix(m); + if (!e) { + log_error("Failed to determine runtime prefix."); + return -EINVAL; } + + m->notify_socket = strappend(e, "/systemd/notify"); if (!m->notify_socket) return log_oom(); @@ -756,8 +745,8 @@ static int manager_setup_kdbus(Manager *m) { return -ESOCKTNOSUPPORT; m->kdbus_fd = bus_kernel_create_bus( - m->running_as == MANAGER_SYSTEM ? "system" : "user", - m->running_as == MANAGER_SYSTEM, &p); + MANAGER_IS_SYSTEM(m) ? "system" : "user", + MANAGER_IS_SYSTEM(m), &p); if (m->kdbus_fd < 0) return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m"); @@ -778,7 +767,7 @@ static int manager_connect_bus(Manager *m, bool reexecuting) { try_bus_connect = m->kdbus_fd >= 0 || reexecuting || - (m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS")); + (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")); /* Try to connect to the buses, if possible. */ return bus_init(m, try_bus_connect); @@ -940,7 +929,7 @@ Manager* manager_free(Manager *m) { * around */ manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); - manager_undo_generators(m); + lookup_paths_flush_generator(&m->lookup_paths); bus_done(m); @@ -1037,7 +1026,6 @@ static void manager_coldplug(Manager *m) { static void manager_build_unit_path_cache(Manager *m) { char **i; - _cleanup_closedir_ DIR *d = NULL; int r; assert(m); @@ -1046,29 +1034,27 @@ static void manager_build_unit_path_cache(Manager *m) { m->unit_path_cache = set_new(&string_hash_ops); if (!m->unit_path_cache) { - log_error("Failed to allocate unit path cache."); - return; + r = -ENOMEM; + goto fail; } /* This simply builds a list of files we know exist, so that * we don't always have to go to disk */ - STRV_FOREACH(i, m->lookup_paths.unit_path) { + STRV_FOREACH(i, m->lookup_paths.search_path) { + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; d = opendir(*i); if (!d) { if (errno != ENOENT) - log_error_errno(errno, "Failed to open directory %s: %m", *i); + log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i); continue; } - while ((de = readdir(d))) { + FOREACH_DIRENT(de, d, r = -errno; goto fail) { char *p; - if (hidden_file(de->d_name)) - continue; - p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); if (!p) { r = -ENOMEM; @@ -1079,20 +1065,15 @@ static void manager_build_unit_path_cache(Manager *m) { if (r < 0) goto fail; } - - d = safe_closedir(d); } return; fail: - log_error_errno(r, "Failed to build unit path cache: %m"); - - set_free_free(m->unit_path_cache); - m->unit_path_cache = NULL; + log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m"); + m->unit_path_cache = set_free_free(m->unit_path_cache); } - static void manager_distribute_fds(Manager *m, FDSet *fds) { Iterator i; Unit *u; @@ -1116,21 +1097,22 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { assert(m); - dual_timestamp_get(&m->generators_start_timestamp); - r = manager_run_generators(m); - dual_timestamp_get(&m->generators_finish_timestamp); + r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + if (r < 0) + return r; + + /* Make sure the transient directory always exists, so that it remains in the search path */ + r = mkdir_p_label(m->lookup_paths.transient, 0755); if (r < 0) return r; - r = lookup_paths_init( - &m->lookup_paths, m->running_as, true, - NULL, - m->generator_unit_path, - m->generator_unit_path_early, - m->generator_unit_path_late); + dual_timestamp_get(&m->generators_start_timestamp); + r = manager_run_generators(m); + dual_timestamp_get(&m->generators_finish_timestamp); if (r < 0) return r; + lookup_paths_reduce(&m->lookup_paths); manager_build_unit_path_cache(m); /* If we will deserialize make sure that during enumeration @@ -1744,7 +1726,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t } log_received_signal(sfsi.ssi_signo == SIGCHLD || - (sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER) + (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m)) ? LOG_DEBUG : LOG_INFO, &sfsi); @@ -1755,7 +1737,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case SIGTERM: - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* This is for compatibility with the * original sysvinit */ m->exit_code = MANAGER_REEXECUTE; @@ -1765,7 +1747,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t /* Fall through */ case SIGINT: - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* If the user presses C-A-D more than * 7 times within 2s, we reboot @@ -1791,14 +1773,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case SIGWINCH: - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); /* This is a nop on non-init */ break; case SIGPWR: - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); /* This is a nop on non-init */ @@ -1906,7 +1888,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case 24: - if (m->running_as == MANAGER_USER) { + if (MANAGER_IS_USER(m)) { m->exit_code = MANAGER_EXIT; return 0; } @@ -2022,7 +2004,7 @@ int manager_loop(Manager *m) { while (m->exit_code == MANAGER_OK) { usec_t wait_usec; - if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && m->running_as == MANAGER_SYSTEM) + if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) watchdog_ping(); if (!ratelimit_test(&rl)) { @@ -2047,7 +2029,7 @@ int manager_loop(Manager *m) { continue; /* Sleep for half the watchdog time */ - if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && m->running_as == MANAGER_SYSTEM) { + if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) { wait_usec = m->runtime_watchdog / 2; if (wait_usec <= 0) wait_usec = 1; @@ -2118,7 +2100,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { const char *msg; int audit_fd, r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; audit_fd = get_audit_fd(); @@ -2127,7 +2109,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { /* Don't generate audit events if the service was already * started and we're just deserializing */ - if (m->n_reloading > 0) + if (MANAGER_IS_RELOADING(m)) return; if (u->type != UNIT_SERVICE) @@ -2161,10 +2143,10 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { /* Don't generate plymouth events if the service was already * started and we're just deserializing */ - if (m->n_reloading > 0) + if (MANAGER_IS_RELOADING(m)) return; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; if (detect_container() > 0) @@ -2208,7 +2190,7 @@ int manager_open_serialization(Manager *m, FILE **_f) { assert(_f); - path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp"; + path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; fd = open_tmpfile(path, O_RDWR|O_CLOEXEC); if (fd < 0) return -errno; @@ -2539,23 +2521,19 @@ int manager_reload(Manager *m) { /* From here on there is no way back. */ manager_clear_jobs_and_units(m); - manager_undo_generators(m); + lookup_paths_flush_generator(&m->lookup_paths); lookup_paths_free(&m->lookup_paths); - /* Find new unit paths */ - q = manager_run_generators(m); + q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); if (q < 0 && r >= 0) r = q; - q = lookup_paths_init( - &m->lookup_paths, m->running_as, true, - NULL, - m->generator_unit_path, - m->generator_unit_path_early, - m->generator_unit_path_late); + /* Find new unit paths */ + q = manager_run_generators(m); if (q < 0 && r >= 0) r = q; + lookup_paths_reduce(&m->lookup_paths); manager_build_unit_path_cache(m); /* First, enumerate what we can from all config files */ @@ -2589,12 +2567,6 @@ int manager_reload(Manager *m) { return r; } -bool manager_is_reloading_or_reexecuting(Manager *m) { - assert(m); - - return m->n_reloading != 0; -} - void manager_reset_failed(Manager *m) { Unit *u; Iterator i; @@ -2626,7 +2598,7 @@ static void manager_notify_finished(Manager *m) { if (m->test_run) return; - if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) { + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { /* Note that m->kernel_usec.monotonic is always at 0, * and m->firmware_usec.monotonic and @@ -2691,7 +2663,7 @@ static void manager_notify_finished(Manager *m) { void manager_check_finished(Manager *m) { assert(m); - if (m->n_reloading > 0) + if (MANAGER_IS_RELOADING(m)) return; /* Verify that we are actually running currently. Initially @@ -2732,77 +2704,6 @@ void manager_check_finished(Manager *m) { manager_invalidate_startup_units(m); } -static int create_generator_dir(Manager *m, char **generator, const char *name) { - char *p; - int r; - - assert(m); - assert(generator); - assert(name); - - if (*generator) - return 0; - - if (m->running_as == MANAGER_SYSTEM && getpid() == 1) { - /* systemd --system, not running --test */ - - p = strappend("/run/systemd/", name); - if (!p) - return log_oom(); - - r = mkdir_p_label(p, 0755); - if (r < 0) { - log_error_errno(r, "Failed to create generator directory %s: %m", p); - free(p); - return r; - } - } else if (m->running_as == MANAGER_USER) { - const char *s = NULL; - - s = getenv("XDG_RUNTIME_DIR"); - if (!s) - return -EINVAL; - p = strjoin(s, "/systemd/", name, NULL); - if (!p) - return log_oom(); - - r = mkdir_p_label(p, 0755); - if (r < 0) { - log_error_errno(r, "Failed to create generator directory %s: %m", p); - free(p); - return r; - } - } else { - /* systemd --system --test */ - - p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL); - if (!p) - return log_oom(); - - if (!mkdtemp(p)) { - log_error_errno(errno, "Failed to create generator directory %s: %m", p); - free(p); - return -errno; - } - } - - *generator = p; - return 0; -} - -static void trim_generator_dir(Manager *m, char **generator) { - assert(m); - assert(generator); - - if (!*generator) - return; - - if (rmdir(*generator) >= 0) - *generator = mfree(*generator); - - return; -} - static int manager_run_generators(Manager *m) { _cleanup_strv_free_ char **paths = NULL; const char *argv[5]; @@ -2814,71 +2715,40 @@ static int manager_run_generators(Manager *m) { if (m->test_run) return 0; - paths = generator_paths(m->running_as); + paths = generator_binary_paths(m->unit_file_scope); if (!paths) return log_oom(); /* Optimize by skipping the whole process by not creating output directories * if no generators are found. */ STRV_FOREACH(path, paths) { - r = access(*path, F_OK); - if (r == 0) + if (access(*path, F_OK) >= 0) goto found; if (errno != ENOENT) log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); } + return 0; found: - r = create_generator_dir(m, &m->generator_unit_path, "generator"); - if (r < 0) - goto finish; - - r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early"); - if (r < 0) - goto finish; - - r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late"); + r = lookup_paths_mkdir_generator(&m->lookup_paths); if (r < 0) goto finish; argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */ - argv[1] = m->generator_unit_path; - argv[2] = m->generator_unit_path_early; - argv[3] = m->generator_unit_path_late; + argv[1] = m->lookup_paths.generator; + argv[2] = m->lookup_paths.generator_early; + argv[3] = m->lookup_paths.generator_late; argv[4] = NULL; RUN_WITH_UMASK(0022) execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); finish: - trim_generator_dir(m, &m->generator_unit_path); - trim_generator_dir(m, &m->generator_unit_path_early); - trim_generator_dir(m, &m->generator_unit_path_late); + lookup_paths_trim_generator(&m->lookup_paths); return r; } -static void remove_generator_dir(Manager *m, char **generator) { - assert(m); - assert(generator); - - if (!*generator) - return; - - strv_remove(m->lookup_paths.unit_path, *generator); - (void) rm_rf(*generator, REMOVE_ROOT); - - *generator = mfree(*generator); -} - -static void manager_undo_generators(Manager *m) { - assert(m); - - remove_generator_dir(m, &m->generator_unit_path); - remove_generator_dir(m, &m->generator_unit_path_early); - remove_generator_dir(m, &m->generator_unit_path_late); -} - int manager_environment_add(Manager *m, char **minus, char **plus) { char **a = NULL, **b = NULL, **l; assert(m); @@ -2941,7 +2811,7 @@ void manager_recheck_journal(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); @@ -2965,7 +2835,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) { assert(m); assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY)); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; if (m->show_status != mode) @@ -2982,7 +2852,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) { static bool manager_get_show_status(Manager *m, StatusType type) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return false; if (m->no_console_output) @@ -3004,7 +2874,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) { void manager_set_first_boot(Manager *m, bool b) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; if (m->first_boot != (int) b) { @@ -3050,7 +2920,7 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { const char *manager_get_runtime_prefix(Manager *m) { assert(m); - return m->running_as == MANAGER_SYSTEM ? + return MANAGER_IS_SYSTEM(m) ? "/run" : getenv("XDG_RUNTIME_DIR"); } diff --git a/src/core/manager.h b/src/core/manager.h index 9803f73129..17f84e6963 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -140,6 +140,7 @@ struct Manager { sd_event_source *jobs_in_progress_event_source; + UnitFileScope unit_file_scope; LookupPaths lookup_paths; Set *unit_path_cache; @@ -162,10 +163,6 @@ struct Manager { dual_timestamp units_load_start_timestamp; dual_timestamp units_load_finish_timestamp; - char *generator_unit_path; - char *generator_unit_path_early; - char *generator_unit_path_late; - struct udev* udev; /* Data specific to the device subsystem */ @@ -228,7 +225,6 @@ struct Manager { unsigned n_in_gc_queue; /* Flags */ - ManagerRunningAs running_as; ManagerExitCode exit_code:5; bool dispatching_load_queue:1; @@ -304,10 +300,15 @@ struct Manager { const char *unit_log_field; const char *unit_log_format_string; - int first_boot; + int first_boot; /* tri-state */ }; -int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); +#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) +#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM) + +#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) + +int manager_new(UnitFileScope scope, bool test_run, Manager **m); Manager* manager_free(Manager *m); void manager_enumerate(Manager *m); @@ -345,8 +346,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds); int manager_reload(Manager *m); -bool manager_is_reloading_or_reexecuting(Manager *m) _pure_; - void manager_reset_failed(Manager *m); void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 32fe51c67e..40fc548b42 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -375,6 +375,7 @@ int mount_setup(bool loaded_policy) { before_relabel = now(CLOCK_MONOTONIC); nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); after_relabel = now(CLOCK_MONOTONIC); diff --git a/src/core/mount.c b/src/core/mount.c index 0fd880df5d..632c5c824c 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -336,8 +336,7 @@ static int mount_add_device_links(Mount *m) { if (path_equal(m->where, "/")) return 0; - if (mount_is_auto(p) && !mount_is_automount(p) && - UNIT(m)->manager->running_as == MANAGER_SYSTEM) + if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager)) device_wants_mount = true; r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES); @@ -353,7 +352,7 @@ static int mount_add_quota_links(Mount *m) { assert(m); - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) return 0; p = get_mount_parameters_fragment(m); @@ -377,8 +376,7 @@ static int mount_add_quota_links(Mount *m) { static bool should_umount(Mount *m) { MountParameters *p; - if (path_equal(m->where, "/") || - path_equal(m->where, "/usr") || + if (PATH_IN_SET(m->where, "/", "/usr") || path_startswith(m->where, "/run/initramfs")) return false; @@ -400,7 +398,7 @@ static int mount_add_default_dependencies(Mount *m) { if (!UNIT(m)->default_dependencies) return 0; - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) return 0; /* We do not add any default dependencies to /, /usr or @@ -409,8 +407,7 @@ static int mount_add_default_dependencies(Mount *m) { * Also, don't bother with anything mounted below virtual * file systems, it's also going to be virtual, and hence * not worth the effort. */ - if (path_equal(m->where, "/") || - path_equal(m->where, "/usr") || + if (PATH_IN_SET(m->where, "/", "/usr") || path_startswith(m->where, "/run/initramfs") || path_startswith(m->where, "/proc") || path_startswith(m->where, "/sys") || @@ -1396,7 +1393,7 @@ static int mount_setup_unit( goto fail; } - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { const char* target; target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; @@ -1424,7 +1421,7 @@ static int mount_setup_unit( } } - if (m->running_as == MANAGER_SYSTEM && + if (MANAGER_IS_SYSTEM(m) && mount_needs_network(options, fstype)) { /* _netdev option may have shown up late, or on a * remount. Add remote-fs dependencies, even though diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 6a7a37ee92..f78eedbd6e 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -176,6 +176,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="RevertUnitFiles"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="PresetUnitFiles"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/path.c b/src/core/path.c index 6ac9b8b90d..5e7b3eb234 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -110,16 +110,14 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { } else { exists = true; - /* Path exists, we don't need to watch parent - too closely. */ + /* Path exists, we don't need to watch parent too closely. */ if (oldslash) { char *cut2 = oldslash + (oldslash == s->path); char tmp2 = *cut2; *cut2 = '\0'; - inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); - /* Error is ignored, the worst can happen is - we get spurious events. */ + (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); + /* Error is ignored, the worst can happen is we get spurious events. */ *cut2 = tmp2; } @@ -320,7 +318,7 @@ static int path_add_default_dependencies(Path *p) { if (r < 0) return r; - if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; diff --git a/src/core/scope.c b/src/core/scope.c index 361695c3f9..7078d1f7e9 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -138,7 +138,7 @@ static int scope_verify(Scope *s) { return 0; if (set_isempty(UNIT(s)->pids) && - !manager_is_reloading_or_reexecuting(UNIT(s)->manager) && + !MANAGER_IS_RELOADING(UNIT(s)->manager) && !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) { log_unit_error(UNIT(s), "Scope has no PIDs. Refusing."); return -EINVAL; @@ -154,26 +154,27 @@ static int scope_load(Unit *u) { assert(s); assert(u->load_state == UNIT_STUB); - if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager)) + if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) + /* Refuse to load non-transient scope units, but allow them while reloading. */ return -ENOENT; - u->load_state = UNIT_LOADED; - - r = unit_load_dropin(u); + r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; - r = unit_patch_contexts(u); - if (r < 0) - return r; + if (u->load_state == UNIT_LOADED) { + r = unit_patch_contexts(u); + if (r < 0) + return r; - r = unit_set_default_slice(u); - if (r < 0) - return r; + r = unit_set_default_slice(u); + if (r < 0) + return r; - r = scope_add_default_dependencies(s); - if (r < 0) - return r; + r = scope_add_default_dependencies(s); + if (r < 0) + return r; + } return scope_verify(s); } @@ -292,7 +293,7 @@ static int scope_start(Unit *u) { assert(s->state == SCOPE_DEAD); - if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager)) + if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) return -ENOENT; (void) unit_realize_cgroup(u); diff --git a/src/core/service.c b/src/core/service.c index 5d58b0b752..58084e2f82 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -523,7 +523,7 @@ static int service_add_default_dependencies(Service *s) { /* Add a number of automatic dependencies useful for the * majority of services. */ - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { /* First, pull in the really early boot stuff, and * require it, so that we fail if we can't acquire * it. */ @@ -832,7 +832,7 @@ static int service_load_pid_file(Service *s, bool may_warn) { return 0; } -static int service_search_main_pid(Service *s) { +static void service_search_main_pid(Service *s) { pid_t pid = 0; int r; @@ -841,30 +841,24 @@ static int service_search_main_pid(Service *s) { /* If we know it anyway, don't ever fallback to unreliable * heuristics */ if (s->main_pid_known) - return 0; + return; if (!s->guess_main_pid) - return 0; + return; assert(s->main_pid <= 0); - r = unit_search_main_pid(UNIT(s), &pid); - if (r < 0) - return r; + if (unit_search_main_pid(UNIT(s), &pid) < 0) + return; log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid); - r = service_set_main_pid(s, pid); - if (r < 0) - return r; + if (service_set_main_pid(s, pid) < 0) + return; r = unit_watch_pid(UNIT(s), pid); - if (r < 0) { + if (r < 0) /* FIXME: we need to do something here */ log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid); - return r; - } - - return 0; } static void service_set_state(Service *s, ServiceState state) { @@ -926,7 +920,7 @@ static void service_set_state(Service *s, ServiceState state) { /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) + if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager)) unit_prune_cgroup(UNIT(s)); /* For remain_after_exit services, let's see if we can "release" the @@ -1217,7 +1211,7 @@ static int service_spawn( if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) return -ENOMEM; - if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) return -ENOMEM; @@ -2729,7 +2723,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; } } else - (void) service_search_main_pid(s); + service_search_main_pid(s); service_enter_start_post(s); break; @@ -2751,16 +2745,15 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; } } else - (void) service_search_main_pid(s); + service_search_main_pid(s); service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_RELOAD: - if (f == SERVICE_SUCCESS) { - service_load_pid_file(s, true); - (void) service_search_main_pid(s); - } + if (f == SERVICE_SUCCESS) + if (service_load_pid_file(s, true) < 0) + service_search_main_pid(s); s->reload_result = f; service_enter_running(s, SERVICE_SUCCESS); diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 6296b4c94a..e14755d84e 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) { goto error; } - cg_get_root_path(&cgroup); + (void) cg_get_root_path(&cgroup); use_watchdog = !!getenv("WATCHDOG_USEC"); @@ -397,9 +397,14 @@ int main(int argc, char *argv[]) { if (!in_container) { _cleanup_free_ char *param = NULL; - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { + r = read_one_line_file("/run/systemd/reboot-param", ¶m); + if (r < 0) + log_warning_errno(r, "Failed to read reboot parameter file: %m"); + + if (!isempty(param)) { log_info("Rebooting with argument '%s'.", param); syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); } } diff --git a/src/core/slice.c b/src/core/slice.c index 667f61bde5..63a77c9bca 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -135,6 +135,7 @@ static int slice_load(Unit *u) { int r; assert(s); + assert(u->load_state == UNIT_STUB); r = unit_load_fragment_and_dropin_optional(u); if (r < 0) diff --git a/src/core/socket.c b/src/core/socket.c index dd515a17a5..65da0e3c5e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -301,7 +301,7 @@ static int socket_add_default_dependencies(Socket *s) { if (r < 0) return r; - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; diff --git a/src/core/swap.c b/src/core/swap.c index 11506d9ecb..c6502eb821 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -198,7 +198,7 @@ static int swap_add_device_links(Swap *s) { return 0; if (is_device_path(s->what)) - return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM, UNIT_BINDS_TO); + return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO); else /* File based swap devices need to be ordered after * systemd-remount-fs.service, since they might need a @@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) { if (!UNIT(s)->default_dependencies) return 0; - if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) return 0; if (detect_container() > 0) diff --git a/src/core/timer.c b/src/core/timer.c index 3d0bae16e5..b286b714fa 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -109,7 +109,7 @@ static int timer_add_default_dependencies(Timer *t) { if (r < 0) return r; - if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -135,7 +135,7 @@ static int timer_setup_persistent(Timer *t) { if (!t->persistent) return 0; - if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers"); if (r < 0) @@ -320,7 +320,7 @@ static usec_t monotonic_to_boottime(usec_t t) { if (t <= 0) return 0; - a = now(CLOCK_BOOTTIME); + a = now(clock_boottime_or_monotonic()); b = now(CLOCK_MONOTONIC); if (t + a > b) diff --git a/src/core/transaction.c b/src/core/transaction.c index c894001cf9..d5370b2a14 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -855,7 +855,7 @@ int transaction_add_job_and_dependencies( * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). * This way, we "recursively" coldplug units, ensuring that we do not look at state of * not-yet-coldplugged units. */ - if (unit->manager->n_reloading > 0) + if (MANAGER_IS_RELOADING(unit->manager)) unit_coldplug(unit); /* log_debug("Pulling in %s/%s from %s/%s", */ @@ -939,7 +939,7 @@ int transaction_add_job_and_dependencies( if (r < 0) { /* unit masked, job type not applicable and unit not found are not considered as errors. */ log_unit_full(dep, - IN_SET(r, -ESHUTDOWN, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING, + IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING, r, "Cannot add dependency job, ignoring: %s", bus_error_message(e, r)); sd_bus_error_free(e); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index fc057d965c..f11df42af3 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -140,14 +140,9 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char ** assert(u); - if (u->manager->running_as == MANAGER_SYSTEM) - e = "/run"; - else { - e = getenv("XDG_RUNTIME_DIR"); - if (!e) - return -EOPNOTSUPP; - } - + e = manager_get_runtime_prefix(u->manager); + if (!e) + return -EOPNOTSUPP; n = strdup(e); if (!n) return -ENOMEM; diff --git a/src/core/unit.c b/src/core/unit.c index af38beb0c3..c60ae2be9d 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -47,11 +47,13 @@ #include "path-util.h" #include "process-util.h" #include "set.h" +#include "signal-util.h" #include "special.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "umask-util.h" #include "unit-name.h" #include "unit.h" #include "user-util.h" @@ -418,13 +420,22 @@ static void unit_remove_transient(Unit *u) { (void) unlink(u->fragment_path); STRV_FOREACH(i, u->dropin_paths) { - _cleanup_free_ char *p = NULL; + _cleanup_free_ char *p = NULL, *pp = NULL; - (void) unlink(*i); + p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */ + if (!p) + continue; + + pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */ + if (!pp) + continue; + + /* Only drop transient drop-ins */ + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; - p = dirname_malloc(*i); - if (p) - (void) rmdir(p); + (void) unlink(*i); + (void) rmdir(p); } } @@ -483,7 +494,10 @@ void unit_free(Unit *u) { assert(u); - if (u->manager->n_reloading <= 0) + if (u->transient_file) + fclose(u->transient_file); + + if (!MANAGER_IS_RELOADING(u->manager)) unit_remove_transient(u); bus_unit_send_removed_signal(u); @@ -814,7 +828,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { return r; } - if (u->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(u->manager)) return 0; if (c->private_tmp) { @@ -1222,6 +1236,17 @@ int unit_load(Unit *u) { if (u->load_state != UNIT_STUB) return 0; + if (u->transient_file) { + r = fflush_and_check(u->transient_file); + if (r < 0) + goto fail; + + fclose(u->transient_file); + u->transient_file = NULL; + + u->dropin_mtime = now(CLOCK_REALTIME); + } + if (UNIT_VTABLE(u)->load) { r = UNIT_VTABLE(u)->load(u); if (r < 0) @@ -1834,7 +1859,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su m = u->manager; /* Update timestamps for state changes */ - if (m->n_reloading <= 0) { + if (!MANAGER_IS_RELOADING(m)) { dual_timestamp_get(&u->state_change_timestamp); if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) @@ -1941,7 +1966,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } else unexpected = true; - if (m->n_reloading <= 0) { + if (!MANAGER_IS_RELOADING(m)) { /* If this state change happened without being * requested by a job, then let's retroactively start @@ -1978,7 +2003,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->type == UNIT_SERVICE && !UNIT_IS_ACTIVE_OR_RELOADING(os) && - m->n_reloading <= 0) { + !MANAGER_IS_RELOADING(m)) { /* Write audit record if we have just finished starting up */ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true); u->in_audit = true; @@ -1995,7 +2020,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->type == UNIT_SERVICE && UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os) && - m->n_reloading <= 0) { + !MANAGER_IS_RELOADING(m)) { /* Hmm, if there was no start record written * write it now, so that we always have a nice @@ -2016,7 +2041,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su manager_recheck_journal(m); unit_trigger_notify(u); - if (u->manager->n_reloading <= 0) { + if (!MANAGER_IS_RELOADING(u->manager)) { /* Maybe we finished startup and are now ready for * being stopped because unneeded? */ unit_check_unneeded(u); @@ -2413,7 +2438,7 @@ int unit_set_default_slice(Unit *u) { if (!escaped) return -ENOMEM; - if (u->manager->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(u->manager)) b = strjoin("system-", escaped, ".slice", NULL); else b = strappend(escaped, ".slice"); @@ -2423,7 +2448,7 @@ int unit_set_default_slice(Unit *u) { slice_name = b; } else slice_name = - u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE) + MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE) ? SPECIAL_SYSTEM_SLICE : SPECIAL_ROOT_SLICE; @@ -2493,12 +2518,11 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { return -EBUSY; match = strjoina("type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "arg0='", name, "'", - NULL); + "sender='org.freedesktop.DBus'," + "path='/org/freedesktop/DBus'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "arg0='", name, "'"); return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); } @@ -2884,7 +2908,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep return r; r = unit_add_two_dependencies(u, UNIT_AFTER, - u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS, + MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS, device, true); if (r < 0) return r; @@ -2924,34 +2948,36 @@ int unit_coldplug(Unit *u) { return 0; } +static bool fragment_mtime_changed(const char *path, usec_t mtime) { + struct stat st; + + if (!path) + return false; + + if (stat(path, &st) < 0) + /* What, cannot access this anymore? */ + return true; + + if (mtime > 0) + /* For non-empty files check the mtime */ + return timespec_load(&st.st_mtim) != mtime; + else if (!null_or_empty(&st)) + /* For masked files check if they are still so */ + return true; + + return false; +} + bool unit_need_daemon_reload(Unit *u) { _cleanup_strv_free_ char **t = NULL; char **path; - struct stat st; unsigned loaded_cnt, current_cnt; assert(u); - if (u->fragment_path) { - zero(st); - if (stat(u->fragment_path, &st) < 0) - /* What, cannot access this anymore? */ - return true; - - if (u->fragment_mtime > 0 && - timespec_load(&st.st_mtim) != u->fragment_mtime) - return true; - } - - if (u->source_path) { - zero(st); - if (stat(u->source_path, &st) < 0) - return true; - - if (u->source_mtime > 0 && - timespec_load(&st.st_mtim) != u->source_mtime) - return true; - } + if (fragment_mtime_changed(u->fragment_path, u->fragment_mtime) || + fragment_mtime_changed(u->source_path, u->source_mtime)) + return true; (void) unit_find_dropin_paths(u, &t); loaded_cnt = strv_length(t); @@ -2962,21 +2988,15 @@ bool unit_need_daemon_reload(Unit *u) { return false; if (strv_overlap(u->dropin_paths, t)) { - STRV_FOREACH(path, u->dropin_paths) { - zero(st); - if (stat(*path, &st) < 0) - return true; - - if (u->dropin_mtime > 0 && - timespec_load(&st.st_mtim) > u->dropin_mtime) + STRV_FOREACH(path, u->dropin_paths) + if (fragment_mtime_changed(*path, u->dropin_mtime)) return true; - } return false; - } else - return true; - } else - return true; + } + } + + return true; } void unit_reset_failed(Unit *u) { @@ -3044,8 +3064,7 @@ bool unit_active_or_pending(Unit *u) { int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { assert(u); assert(w >= 0 && w < _KILL_WHO_MAX); - assert(signo > 0); - assert(signo < _NSIG); + assert(SIGNAL_VALID(signo)); if (!UNIT_VTABLE(u)->kill) return -EOPNOTSUPP; @@ -3162,7 +3181,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) { if (u->unit_file_state < 0 && u->fragment_path) { r = unit_file_get_state( - u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + u->manager->unit_file_scope, NULL, basename(u->fragment_path), &u->unit_file_state); @@ -3178,7 +3197,7 @@ int unit_get_unit_file_preset(Unit *u) { if (u->unit_file_preset < 0 && u->fragment_path) u->unit_file_preset = unit_file_query_preset( - u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + u->manager->unit_file_scope, NULL, basename(u->fragment_path)); @@ -3229,7 +3248,7 @@ int unit_patch_contexts(Unit *u) { return -ENOMEM; } - if (u->manager->running_as == MANAGER_USER && + if (MANAGER_IS_USER(u->manager) && !ec->working_directory) { r = get_home_dir(&ec->working_directory); @@ -3241,7 +3260,7 @@ int unit_patch_contexts(Unit *u) { ec->working_directory_missing_ok = true; } - if (u->manager->running_as == MANAGER_USER && + if (MANAGER_IS_USER(u->manager) && (ec->syscall_whitelist || !set_isempty(ec->syscall_filter) || !set_isempty(ec->syscall_archs) || @@ -3319,59 +3338,62 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) { return *(ExecRuntime**) ((uint8_t*) u + offset); } -static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) { +static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) { assert(u); - if (u->manager->running_as == MANAGER_USER) { - int r; + if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT)) + return NULL; - if (mode == UNIT_PERSISTENT && !transient) - r = user_config_home(dir); - else - r = user_runtime_dir(dir); - if (r == 0) - return -ENOENT; + if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */ + return u->manager->lookup_paths.transient; - return r; - } + if (mode == UNIT_RUNTIME) + return u->manager->lookup_paths.runtime_control; - if (mode == UNIT_PERSISTENT && !transient) - *dir = strdup("/etc/systemd/system"); - else - *dir = strdup("/run/systemd/system"); - if (!*dir) - return -ENOMEM; + if (mode == UNIT_PERSISTENT) + return u->manager->lookup_paths.persistent_control; - return 0; + return NULL; } int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - - _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL; + _cleanup_free_ char *p = NULL, *q = NULL; + const char *dir, *prefixed; int r; assert(u); + if (u->transient_file) { + /* When this is a transient unit file in creation, then let's not create a new drop-in but instead + * write to the transient unit file. */ + fputs(data, u->transient_file); + return 0; + } + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; - r = unit_drop_in_dir(u, mode, u->transient, &dir); - if (r < 0) - return r; + dir = unit_drop_in_dir(u, mode); + if (!dir) + return -EINVAL; + + prefixed = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\" or an equivalent operation. Do not edit.\n", + data); - r = write_drop_in(dir, u->id, 50, name, data); + r = drop_in_file(dir, u->id, 50, name, &p, &q); if (r < 0) return r; - r = drop_in_file(dir, u->id, 50, name, &p, &q); + (void) mkdir_p(p, 0755); + r = write_string_file_atomic_label(q, prefixed); if (r < 0) return r; - r = strv_extend(&u->dropin_paths, q); + r = strv_push(&u->dropin_paths, q); if (r < 0) return r; + q = NULL; - strv_sort(u->dropin_paths); strv_uniq(u->dropin_paths); u->dropin_mtime = now(CLOCK_REALTIME); @@ -3402,7 +3424,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n } int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - _cleanup_free_ char *ndata = NULL; + const char *ndata; assert(u); assert(name); @@ -3414,9 +3436,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char * if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; - ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL); - if (!ndata) - return -ENOMEM; + ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data); return unit_write_drop_in(u, mode, name, ndata); } @@ -3444,24 +3464,51 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const } int unit_make_transient(Unit *u) { + FILE *f; + char *path; + assert(u); if (!UNIT_VTABLE(u)->can_transient) return -EOPNOTSUPP; - u->load_state = UNIT_STUB; - u->load_error = 0; - u->transient = true; + path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL); + if (!path) + return -ENOMEM; + + /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are + * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */ + + RUN_WITH_UMASK(0022) { + f = fopen(path, "we"); + if (!f) { + free(path); + return -errno; + } + } + + if (u->transient_file) + fclose(u->transient_file); + u->transient_file = f; + + free(u->fragment_path); + u->fragment_path = path; - u->fragment_path = mfree(u->fragment_path); u->source_path = mfree(u->source_path); u->dropin_paths = strv_free(u->dropin_paths); u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0; + u->load_state = UNIT_STUB; + u->load_error = 0; + u->transient = true; + unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); unit_add_to_load_queue(u); + fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n", + u->transient_file); + return 0; } diff --git a/src/core/unit.h b/src/core/unit.h index 601e763ce2..cfdac852a5 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -95,6 +95,9 @@ struct Unit { usec_t source_mtime; usec_t dropin_mtime; + /* If this is a transient unit we are currently writing, this is where we are writing it to */ + FILE *transient_file; + /* If there is something to do with this unit, then this is the installed job for it */ Job *job; diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 085909c20c..2bbb958861 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -49,6 +49,7 @@ #include "journald-native.h" #include "log.h" #include "macro.h" +#include "missing.h" #include "mkdir.h" #include "parse-util.h" #include "process-util.h" @@ -212,6 +213,10 @@ static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) { #define filename_escape(s) xescape((s), "./ ") +static inline const char *coredump_tmpfile_name(const char *s) { + return s ? s : "(unnamed temporary file)"; +} + static int fix_permissions( int fd, const char *filename, @@ -220,7 +225,6 @@ static int fix_permissions( uid_t uid) { assert(fd >= 0); - assert(filename); assert(target); assert(context); @@ -230,10 +234,20 @@ static int fix_permissions( (void) fix_xattr(fd, context); if (fsync(fd) < 0) - return log_error_errno(errno, "Failed to sync coredump %s: %m", filename); + return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); + + if (filename) { + if (rename(filename, target) < 0) + return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target); + } else { + _cleanup_free_ char *proc_fd_path = NULL; - if (rename(filename, target) < 0) - return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target); + if (asprintf(&proc_fd_path, "/proc/self/fd/%d", fd) < 0) + return log_oom(); + + if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) + return log_error_errno(errno, "Failed to create coredump %s: %m", target); + } return 0; } @@ -294,6 +308,33 @@ static int make_filename(const char *context[_CONTEXT_MAX], char **ret) { return 0; } +static int open_coredump_tmpfile(const char *target, char **ret_filename) { + _cleanup_free_ char *tmp = NULL; + int fd; + int r; + + assert(target); + assert(ret_filename); + + fd = open("/var/lib/systemd/coredump", O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0640); + if (fd < 0) { + log_debug_errno(errno, "Failed to use O_TMPFILE: %m"); + + r = tempfn_random(target, NULL, &tmp); + if (r < 0) + return log_error_errno(r, "Failed to determine temporary file name: %m"); + + fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); + if (fd < 0) + return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); + } + + *ret_filename = tmp; + tmp = NULL; + + return fd; +} + static int save_external_coredump( const char *context[_CONTEXT_MAX], int input_fd, @@ -335,15 +376,11 @@ static int save_external_coredump( if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); - r = tempfn_random(fn, NULL, &tmp); - if (r < 0) - return log_error_errno(r, "Failed to determine temporary file name: %m"); - mkdir_p_label("/var/lib/systemd/coredump", 0755); - fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); + fd = open_coredump_tmpfile(fn, &tmp); if (fd < 0) - return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); + return fd; r = copy_bytes(input_fd, fd, max_size, false); if (r == -EFBIG) { @@ -358,12 +395,12 @@ static int save_external_coredump( } if (fstat(fd, &st) < 0) { - log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp); + log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { - log_error_errno(errno, "Failed to seek on %s: %m", tmp); + log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); goto fail; } @@ -381,21 +418,13 @@ static int save_external_coredump( goto uncompressed; } - r = tempfn_random(fn_compressed, NULL, &tmp_compressed); - if (r < 0) { - log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed); + fd_compressed = open_coredump_tmpfile(fn_compressed, &tmp_compressed); + if (fd_compressed < 0) goto uncompressed; - } - - fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); - if (fd_compressed < 0) { - log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed); - goto uncompressed; - } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { - log_error_errno(r, "Failed to compress %s: %m", tmp_compressed); + log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); goto fail_compressed; } @@ -404,7 +433,8 @@ static int save_external_coredump( goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ - unlink_noerrno(tmp); + if (tmp) + unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_node_fd = fd_compressed; /* compressed */ @@ -417,7 +447,8 @@ static int save_external_coredump( return 0; fail_compressed: - (void) unlink(tmp_compressed); + if (tmp_compressed) + (void) unlink(tmp_compressed); } uncompressed: @@ -438,7 +469,8 @@ uncompressed: return 0; fail: - (void) unlink(tmp); + if (tmp) + (void) unlink(tmp); return r; } @@ -1095,7 +1127,7 @@ static int process_kernel(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[n_iovec++], core_environ); } - core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL); + core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000"); IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp); IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); diff --git a/src/import/curl-util.c b/src/import/curl-util.c index a04c8c49ff..6990c47f48 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -137,7 +137,7 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0) return -1; - sd_event_source_set_description(io, "curl-io"); + (void) sd_event_source_set_description(io, "curl-io"); r = hashmap_put(g->ios, FD_TO_PTR(s), io); if (r < 0) { @@ -204,7 +204,7 @@ static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0) return -1; - sd_event_source_set_description(g->timer, "curl-timer"); + (void) sd_event_source_set_description(g->timer, "curl-timer"); } return 0; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 998857035a..3a152a50e3 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -44,15 +44,6 @@ typedef enum PullJobState { #define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED)) -typedef enum PullJobCompression { - PULL_JOB_UNCOMPRESSED, - PULL_JOB_XZ, - PULL_JOB_GZIP, - PULL_JOB_BZIP2, - _PULL_JOB_COMPRESSION_MAX, - _PULL_JOB_COMPRESSION_INVALID = -1, -} PullJobCompression; - struct PullJob { PullJobState state; int error; diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c index 8956eb1d58..612b10f3a9 100644 --- a/src/journal/fsprg.c +++ b/src/journal/fsprg.c @@ -58,7 +58,7 @@ static gcry_mpi_t mpi_import(const void *buf, size_t buflen) { gcry_mpi_t h; unsigned len; - gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL); + assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0); len = (gcry_mpi_get_nbits(h) + 7) / 8; assert(len <= buflen); assert(gcry_mpi_cmp_ui(h, 0) >= 0); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 2939322925..b1d1bf9e14 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -492,38 +492,36 @@ static void server_cache_hostname(Server *s) { } static bool shall_try_append_again(JournalFile *f, int r) { - - /* -E2BIG Hit configured limit - -EFBIG Hit fs limit - -EDQUOT Quota limit hit - -ENOSPC Disk full - -EIO I/O error of some kind (mmap) - -EHOSTDOWN Other machine - -EBUSY Unclean shutdown - -EPROTONOSUPPORT Unsupported feature - -EBADMSG Corrupted - -ENODATA Truncated - -ESHUTDOWN Already archived - -EIDRM Journal file has been deleted */ - - if (r == -E2BIG || r == -EFBIG || r == -EDQUOT || r == -ENOSPC) + switch(r) { + case -E2BIG: /* Hit configured limit */ + case -EFBIG: /* Hit fs limit */ + case -EDQUOT: /* Quota limit hit */ + case -ENOSPC: /* Disk full */ log_debug("%s: Allocation limit reached, rotating.", f->path); - else if (r == -EHOSTDOWN) + return true; + case -EIO: /* I/O error of some kind (mmap) */ + log_warning("%s: IO error, rotating.", f->path); + return true; + case -EHOSTDOWN: /* Other machine */ log_info("%s: Journal file from other machine, rotating.", f->path); - else if (r == -EBUSY) + return true; + case -EBUSY: /* Unclean shutdown */ log_info("%s: Unclean shutdown, rotating.", f->path); - else if (r == -EPROTONOSUPPORT) + return true; + case -EPROTONOSUPPORT: /* Unsupported feature */ log_info("%s: Unsupported feature, rotating.", f->path); - else if (r == -EBADMSG || r == -ENODATA || r == ESHUTDOWN) + return true; + case -EBADMSG: /* Corrupted */ + case -ENODATA: /* Truncated */ + case -ESHUTDOWN: /* Already archived */ log_warning("%s: Journal file corrupted, rotating.", f->path); - else if (r == -EIO) - log_warning("%s: IO error, rotating.", f->path); - else if (r == -EIDRM) + return true; + case -EIDRM: /* Journal file has been deleted */ log_warning("%s: Journal file has been deleted, rotating.", f->path); - else + return true; + default: return false; - - return true; + } } static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned n, int priority) { diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 6370061daf..02e3bf904c 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -38,6 +38,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 464834979a..c8f369cb78 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -34,6 +34,8 @@ #define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" #define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" #define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked" +#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated" +#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked" #define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" #define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" #define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index e860876c12..52128e7b5c 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -678,6 +678,7 @@ int bus_get_name_creds_kdbus( (mask & (SD_BUS_CREDS_PPID| SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| @@ -795,6 +796,7 @@ static int bus_get_name_creds_dbus1( ((mask & SD_BUS_CREDS_AUGMENT) && (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| @@ -947,6 +949,7 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds ** (mask & (SD_BUS_CREDS_PPID| SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 862f26aad7..04da94e7e3 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -1778,7 +1778,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, r = bus_write_message(bus, m, hint_sync_call, &idx); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); return -ECONNRESET; } @@ -2083,7 +2083,7 @@ _public_ int sd_bus_call( r = bus_read_message(bus, false, 0); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = -ECONNRESET; } @@ -2116,7 +2116,7 @@ _public_ int sd_bus_call( r = dispatch_wqueue(bus); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = -ECONNRESET; } @@ -2766,7 +2766,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_OPENING: r = bus_socket_process_opening(bus); - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = 1; } else if (r < 0) @@ -2777,7 +2777,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_AUTHENTICATING: r = bus_socket_process_authenticating(bus); - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = 1; } else if (r < 0) @@ -2791,7 +2791,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_RUNNING: case BUS_HELLO: r = process_running(bus, hint_priority, priority, ret); - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = 1; @@ -2914,7 +2914,7 @@ _public_ int sd_bus_flush(sd_bus *bus) { for (;;) { r = dispatch_wqueue(bus); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); return -ECONNRESET; } diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 841358ed03..79b98c293c 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -1145,8 +1145,7 @@ _public_ int sd_event_add_signal( int r; assert_return(e, -EINVAL); - assert_return(sig > 0, -EINVAL); - assert_return(sig < _NSIG, -EINVAL); + assert_return(SIGNAL_VALID(sig), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -2200,7 +2199,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { if (_unlikely_(n != sizeof(si))) return -EIO; - assert(si.ssi_signo < _NSIG); + assert(SIGNAL_VALID(si.ssi_signo)); read_one = true; @@ -2528,7 +2527,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { } dual_timestamp_get(&e->timestamp); - e->timestamp_boottime = now(CLOCK_BOOTTIME); + e->timestamp_boottime = now(clock_boottime_or_monotonic()); for (i = 0; i < m; i++) { diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index a5758bb516..3a4bac2ced 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -95,12 +95,43 @@ static const NLType rtnl_link_info_data_macvlan_types[] = { }; static const NLType rtnl_link_info_data_bridge_types[] = { - [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 }, }; static const NLType rtnl_link_info_data_vlan_types[] = { diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 37585048b8..d8303e2e69 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -579,9 +579,10 @@ static void resolve_free(sd_resolve *resolve) { (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); } - /* Now terminate them and wait until they are gone. */ + /* Now terminate them and wait until they are gone. + If we get an error than most likely the thread already exited. */ for (i = 0; i < resolve->n_valid_workers; i++) - pthread_join(resolve->workers[i], NULL); + (void) pthread_join(resolve->workers[i], NULL); /* Close all communication channels */ for (i = 0; i < _FD_MAX; i++) diff --git a/src/locale/localed.c b/src/locale/localed.c index 46405ca68a..3b22a582ac 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -24,6 +24,7 @@ #ifdef HAVE_XKBCOMMON #include <xkbcommon/xkbcommon.h> +#include <dlfcn.h> #endif #include "sd-bus.h" @@ -1101,6 +1102,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro } #ifdef HAVE_XKBCOMMON + _printf_(3, 0) static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { const char *fmt; @@ -1109,7 +1111,24 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); } +#define LOAD_SYMBOL(symbol, dl, name) \ + ({ \ + (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \ + (symbol) ? 0 : -EOPNOTSUPP; \ + }) + static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { + + /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge + * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function + * pointers to the shared library are below: */ + + struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL; + void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL; + void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL; + struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL; + void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL; + const struct xkb_rule_names rmlvo = { .model = model, .layout = layout, @@ -1118,35 +1137,68 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v }; struct xkb_context *ctx = NULL; struct xkb_keymap *km = NULL; + void *dl; int r; - /* compile keymap from RMLVO information to check out its validity */ + /* Compile keymap from RMLVO information to check out its validity */ + + dl = dlopen("libxkbcommon.so.0", RTLD_LAZY); + if (!dl) + return -EOPNOTSUPP; + + r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn"); + if (r < 0) + goto finish; - ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref"); + if (r < 0) + goto finish; + + ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); if (!ctx) { r = -ENOMEM; - goto exit; + goto finish; } - xkb_context_set_log_fn(ctx, log_xkb); + symbol_xkb_context_set_log_fn(ctx, log_xkb); - km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); + km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!km) { r = -EINVAL; - goto exit; + goto finish; } r = 0; -exit: - xkb_keymap_unref(km); - xkb_context_unref(ctx); +finish: + if (symbol_xkb_keymap_unref && km) + symbol_xkb_keymap_unref(km); + + if (symbol_xkb_context_unref && ctx) + symbol_xkb_context_unref(ctx); + + (void) dlclose(dl); return r; } + #else + static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { return 0; } + #endif static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { @@ -1203,7 +1255,11 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err if (r < 0) { log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m", strempty(model), strempty(layout), strempty(variant), strempty(options)); - return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing"); + + if (r == -EOPNOTSUPP) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system."); + + return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid."); } if (free_and_strdup(&c->x11_layout, layout) < 0 || diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index ff9170683b..0f8862c0d9 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -28,6 +28,7 @@ #include "logind-session-device.h" #include "logind-session.h" #include "logind.h" +#include "signal-util.h" #include "strv.h" #include "util.h" @@ -300,7 +301,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); } - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); r = bus_verify_polkit_async( diff --git a/src/login/logind-session.c b/src/login/logind-session.c index e088225beb..a8b1d5943d 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -513,7 +513,7 @@ static int session_start_scope(Session *s) { if (!scope) return log_oom(); - description = strjoina("Session ", s->id, " of user ", s->user->name, NULL); + description = strjoina("Session ", s->id, " of user ", s->user->name); r = manager_start_scope( s->manager, @@ -797,7 +797,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) { /* Graphical sessions should really implement a real * idle hint logic */ - if (s->display) + if (SESSION_TYPE_IS_GRAPHICAL(s->type)) goto dont_know; /* For sessions with an explicitly configured tty, let's check diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index fd98c7beca..b73f9ea69e 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -25,6 +25,7 @@ #include "formats-util.h" #include "logind-user.h" #include "logind.h" +#include "signal-util.h" #include "strv.h" #include "user-util.h" @@ -222,7 +223,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * if (r < 0) return r; - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); r = user_kill(u, signo); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index c5bbf2fbde..ab54d9e934 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -46,6 +46,7 @@ #include "mkdir.h" #include "path-util.h" #include "process-util.h" +#include "signal-util.h" #include "strv.h" #include "terminal-util.h" #include "user-util.h" @@ -166,7 +167,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); } - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); r = bus_verify_polkit_async( @@ -729,7 +730,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu return r; /* Name and mode */ - unit = strjoina("container-shell@", p, ".service", NULL); + unit = strjoina("container-shell@", p, ".service"); r = sd_bus_message_append(tm, "ss", unit, "fail"); if (r < 0) return r; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 1d3264a1de..c370ed57ec 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -2338,6 +2338,50 @@ static int set_limit(int argc, char *argv[], void *userdata) { return 0; } +static int clean_images(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + uint64_t usage, total = 0; + char fb[FORMAT_BYTES_MAX]; + sd_bus *bus = userdata; + const char *name; + unsigned c = 0; + int r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CleanPool", + &error, + &reply, + "s", arg_all ? "all" : "hidden"); + if (r < 0) + return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, 'a', "(st)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) { + log_info("Removed image '%s'. Freed exclusive disk space: %s", + name, format_bytes(fb, sizeof(fb), usage)); + + total += usage; + c++; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + log_info("Removed %u images in total. Total freed exclusive disk space %s.", + c, format_bytes(fb, sizeof(fb), total)); + + return 0; +} + static int help(int argc, char *argv[], void *userdata) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" @@ -2358,7 +2402,7 @@ static int help(int argc, char *argv[], void *userdata) { " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" " --uid=USER Specify user ID to invoke shell as\n" - " --setenv=VAR=VALUE Add an environment variable for shell\n" + " -E --setenv=VAR=VALUE Add an environment variable for shell\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" @@ -2396,6 +2440,7 @@ static int help(int argc, char *argv[], void *userdata) { " read-only NAME [BOOL] Mark or unmark image read-only\n" " remove NAME... Remove an image\n" " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n" + " clean Remove hidden (or all) images\n" "Image Transfer Commands:\n" " pull-tar URL [NAME] Download a TAR container image\n" " pull-raw URL [NAME] Download a RAW container or VM image\n" @@ -2425,7 +2470,6 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_FORMAT, ARG_UID, - ARG_SETENV, }; static const struct option options[] = { @@ -2451,7 +2495,7 @@ static int parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, ARG_FORCE }, { "format", required_argument, NULL, ARG_FORMAT }, { "uid", required_argument, NULL, ARG_UID }, - { "setenv", required_argument, NULL, ARG_SETENV }, + { "setenv", required_argument, NULL, 'E' }, {} }; @@ -2579,7 +2623,7 @@ static int parse_argv(int argc, char *argv[]) { arg_uid = optarg; break; - case ARG_SETENV: + case 'E': if (!env_assignment_is_valid(optarg)) { log_error("Environment assignment invalid: %s", optarg); return -EINVAL; @@ -2635,6 +2679,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "list-transfers", VERB_ANY, 1, 0, list_transfers }, { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer }, { "set-limit", 2, 3, 0, set_limit }, + { "clean", VERB_ANY, 1, 0, clean_images }, {} }; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 20894433e7..c9639c3cf2 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -802,6 +802,93 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata, return bus_image_method_mark_read_only(message, i, error); } +static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) { + enum { + REMOVE_ALL, + REMOVE_HIDDEN, + } mode; + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(image_hashmap_freep) Hashmap *images = NULL; + Manager *m = userdata; + Image *image; + const char *mm; + Iterator i; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &mm); + if (r < 0) + return r; + + if (streq(mm, "all")) + mode = REMOVE_ALL; + else if (streq(mm, "hidden")) + mode = REMOVE_HIDDEN; + else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + images = hashmap_new(&string_hash_ops); + if (!images) + return -ENOMEM; + + r = image_discover(images); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(image, images, i) { + + /* We can't remove vendor images (i.e. those in /usr) */ + if (IMAGE_IS_VENDOR(image)) + continue; + + if (IMAGE_IS_HOST(image)) + continue; + + if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) + continue; + + r = image_remove(image); + if (r == -EBUSY) /* keep images that are currently being used. */ + continue; + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to remove image %s: %m", image->name); + + r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; uint64_t limit; @@ -1144,6 +1231,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index d9d487d805..ebc6c9eb9e 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -148,8 +148,12 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union for (;;) { if (!address_pool_prefix_is_taken(p, &u, prefixlen)) { _cleanup_free_ char *s = NULL; + int r; + + r = in_addr_to_string(p->family, &u, &s); + if (r < 0) + return r; - in_addr_to_string(p->family, &u, &s); log_debug("Found range %s/%u", strna(s), prefixlen); *found = u; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 0589ebf227..c5b61abc9e 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -57,6 +57,10 @@ static int link_set_dhcp_routes(Link *link) { assert(link); assert(link->dhcp_lease); + assert(link->network); + + if (!link->network->dhcp_use_routes) + return 0; r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r < 0 && r != -ENODATA) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 88b3cbe90a..0fb3aa6c43 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -99,6 +99,15 @@ static bool link_ipv6ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV6; } +static bool link_ipv6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + return link_dhcp6_enabled(link) || link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); +} + static bool link_lldp_rx_enabled(Link *link) { assert(link); @@ -165,6 +174,21 @@ static bool link_ipv6_forward_enabled(Link *link) { return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } +static bool link_proxy_arp_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->proxy_arp < 0) + return false; + + return true; +} + static bool link_ipv6_accept_ra_enabled(Link *link) { assert(link); @@ -203,6 +227,31 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { return link->network->ipv6_privacy_extensions; } +static int link_enable_ipv6(Link *link) { + const char *p = NULL; + bool disabled; + int r; + + if (link->flags & IFF_LOOPBACK) + return 0; + + disabled = !link_ipv6_enabled(link); + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6"); + + r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname); + else { + if (disabled) + log_link_info(link, "IPv6 disabled for interface: %m"); + else + log_link_info(link, "IPv6 enabled for interface: %m"); + } + + return 0; +} + void link_update_operstate(Link *link) { LinkOperationalState operstate; assert(link); @@ -1039,6 +1088,22 @@ static int link_set_bridge_fdb(Link *const link) { return r; } +static int link_set_proxy_arp(Link *const link) { + const char *p = NULL; + int r; + + if (!link_proxy_arp_enabled(link)) + return 0; + + p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp"); + + r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); + + return 0; +} + static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -1479,7 +1544,21 @@ static int link_up(Link *link) { return log_link_error_errno(link, r, "Could not set MAC address: %m"); } + /* If IPv6 not configured (no static IPv6 address and neither DHCPv6 nor IPv6LL is enabled) + for this interface then disable IPv6 else enable it. */ + (void) link_enable_ipv6(link); + if (link->network->mtu) { + /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes + on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */ + if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) { + + log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as " + "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m"); + + link->network->mtu = IPV6_MIN_MTU; + } + r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu); if (r < 0) return log_link_error_errno(link, r, "Could not set MTU: %m"); @@ -1489,7 +1568,7 @@ static int link_up(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - if (socket_ipv6_is_supported()) { + if (link_ipv6_enabled(link)) { /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */ r = sd_netlink_message_open_container(req, AF_INET6); if (r < 0) @@ -2167,6 +2246,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_proxy_arp(link); + if (r < 0) + return r; + r = link_set_ipv4_forward(link); if (r < 0) return r; diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index c940e63052..6bde04bc32 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -30,6 +30,8 @@ #include "string-util.h" #include "unaligned.h" +#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } + /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ #define LLDP_TX_FAST_INIT 4U @@ -127,7 +129,7 @@ static int lldp_make_packet( h = (struct ether_header*) packet; h->ether_type = htobe16(ETHERTYPE_LLDP); - memcpy(h->ether_dhost, &(struct ether_addr) { SD_LLDP_MULTICAST_ADDR }, ETH_ALEN); + memcpy(h->ether_dhost, &(struct ether_addr) { LLDP_MULTICAST_ADDR }, ETH_ALEN); memcpy(h->ether_shost, hwaddr, ETH_ALEN); p = (uint8_t*) packet + sizeof(struct ether_header); @@ -199,7 +201,7 @@ static int lldp_send_packet(int ifindex, const void *packet, size_t packet_size) .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), .ll.sll_ifindex = ifindex, .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = SD_LLDP_MULTICAST_ADDR, + .ll.sll_addr = LLDP_MULTICAST_ADDR, }; _cleanup_close_ int fd = -1; diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index cdcd08f057..3f91b2eaea 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -89,6 +89,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m"); } + if (b->mcast_querier >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); @@ -106,8 +112,19 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return r; } +static void bridge_init(NetDev *n) { + Bridge *b; + + b = BRIDGE(n); + + assert(b); + + b->mcast_querier = -1; +} + const NetDevVTable bridge_vtable = { .object_size = sizeof(Bridge), + .init = bridge_init, .sections = "Match\0NetDev\0Bridge\0", .post_create = netdev_bridge_post_create, .create_type = NETDEV_CREATE_MASTER, diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h index 27f26f7870..3f6f1d0502 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/networkd-netdev-bridge.h @@ -26,6 +26,8 @@ typedef struct Bridge Bridge; struct Bridge { NetDev meta; + int mcast_querier; + usec_t forward_delay; usec_t hello_time; usec_t max_age; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 8f506af092..15a787a9e3 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -92,3 +92,4 @@ Bond.LearnPacketIntervalSec, config_parse_sec, 0, Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 9793938080..1da99cd5bc 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -61,6 +61,7 @@ Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits) Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) +Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 5946ba18dc..07f8fb028f 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -132,6 +132,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->ipv6_dad_transmits = -1; network->ipv6_hop_limit = -1; network->duid_type = _DUID_TYPE_INVALID; + network->proxy_arp = -1; r = config_parse(NULL, filename, file, "Match\0" @@ -398,6 +399,19 @@ int network_apply(Manager *manager, Network *network, Link *link) { return 0; } +bool network_has_static_ipv6_addresses(Network *network) { + Address *address; + + assert(network); + + LIST_FOREACH(addresses, address, network->static_addresses) { + if (address->family == AF_INET6) + return true; + } + + return false; +} + int config_parse_netdev(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 5400a8bc9d..15417f4828 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -139,6 +139,7 @@ struct Network { int ipv6_accept_ra; int ipv6_dad_transmits; int ipv6_hop_limit; + int proxy_arp; union in_addr_union ipv6_token; IPv6PrivacyExtensions ipv6_privacy_extensions; @@ -185,6 +186,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret); int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); int network_apply(Manager *manager, Network *network, Link *link); +bool network_has_static_ipv6_addresses(Network *network); + int config_parse_netdev(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tunnel(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e065a5a5a9..ab9b777d9a 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -52,8 +52,7 @@ int route_new_static(Network *network, unsigned section, Route **ret) { int r; if (section) { - route = hashmap_get(network->routes_by_section, - UINT_TO_PTR(section)); + route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section)); if (route) { *ret = route; route = NULL; @@ -67,16 +66,18 @@ int route_new_static(Network *network, unsigned section, Route **ret) { return r; route->protocol = RTPROT_STATIC; - route->network = network; - - LIST_PREPEND(routes, network->static_routes, route); if (section) { + r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); + if (r < 0) + return r; + route->section = section; - hashmap_put(network->routes_by_section, - UINT_TO_PTR(route->section), route); } + LIST_PREPEND(routes, network->static_routes, route); + route->network = network; + *ret = route; route = NULL; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index eb89916b7e..a07f148ef6 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -64,6 +64,7 @@ #include "hostname-util.h" #include "log.h" #include "loopback-setup.h" +#include "machine-id-setup.h" #include "machine-image.h" #include "macro.h" #include "missing.h" @@ -249,7 +250,7 @@ static void help(void) { " the container\n" " --overlay-ro=PATH[:PATH...]:PATH\n" " Similar, but creates a read-only overlay mount\n" - " --setenv=NAME=VALUE Pass an environment variable to PID 1\n" + " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" " --share-system Share system namespaces with host\n" " --register=BOOLEAN Register container as machine\n" " --keep-unit Do not register a scope for the machine, reuse\n" @@ -332,7 +333,6 @@ static int parse_argv(int argc, char *argv[]) { ARG_TMPFS, ARG_OVERLAY, ARG_OVERLAY_RO, - ARG_SETENV, ARG_SHARE_SYSTEM, ARG_REGISTER, ARG_KEEP_UNIT, @@ -373,7 +373,7 @@ static int parse_argv(int argc, char *argv[]) { { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, { "machine", required_argument, NULL, 'M' }, { "slice", required_argument, NULL, 'S' }, - { "setenv", required_argument, NULL, ARG_SETENV }, + { "setenv", required_argument, NULL, 'E' }, { "selinux-context", required_argument, NULL, 'Z' }, { "selinux-apifs-context", required_argument, NULL, 'L' }, { "quiet", no_argument, NULL, 'q' }, @@ -710,7 +710,7 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_SETENV: { + case 'E': { char **n; if (!env_assignment_is_valid(optarg)) { @@ -1375,11 +1375,11 @@ static int setup_hostname(void) { } static int setup_journal(const char *directory) { - sd_id128_t machine_id, this_id; + sd_id128_t this_id; _cleanup_free_ char *b = NULL, *d = NULL; - const char *etc_machine_id, *p, *q; + const char *p, *q; bool try; - char *id; + char id[33]; int r; /* Don't link journals in ephemeral mode */ @@ -1391,28 +1391,11 @@ static int setup_journal(const char *directory) { try = arg_link_journal_try || arg_link_journal == LINK_AUTO; - etc_machine_id = prefix_roota(directory, "/etc/machine-id"); - - r = read_one_line_file(etc_machine_id, &b); - if (r == -ENOENT && try) - return 0; - else if (r < 0) - return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id); - - id = strstrip(b); - if (isempty(id) && try) - return 0; - - /* Verify validity */ - r = sd_id128_from_string(id, &machine_id); - if (r < 0) - return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id); - r = sd_id128_get_machine(&this_id); if (r < 0) return log_error_errno(r, "Failed to retrieve machine ID: %m"); - if (sd_id128_equal(machine_id, this_id)) { + if (sd_id128_equal(arg_uuid, this_id)) { log_full(try ? LOG_WARNING : LOG_ERR, "Host and machine ids are equal (%s): refusing to link journals", id); if (try) @@ -1432,6 +1415,8 @@ static int setup_journal(const char *directory) { if (r < 0) return log_error_errno(r, "Failed to create /var/log/journal: %m"); + (void) sd_id128_to_string(arg_uuid, id); + p = strjoina("/var/log/journal/", id); q = prefix_roota(directory, p); @@ -2201,6 +2186,38 @@ static int mount_device(const char *what, const char *where, const char *directo #endif } +static int setup_machine_id(const char *directory) { + int r; + const char *etc_machine_id, *t; + _cleanup_free_ char *s = NULL; + + etc_machine_id = prefix_roota(directory, "/etc/machine-id"); + + r = read_one_line_file(etc_machine_id, &s); + if (r < 0) + return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id); + + t = strstrip(s); + + if (!isempty(t)) { + r = sd_id128_from_string(t, &arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id); + } else { + if (sd_id128_is_null(arg_uuid)) { + r = sd_id128_randomize(&arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to generate random machine ID: %m"); + } + } + + r = machine_id_setup(directory, arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to setup machine ID: %m"); + + return 0; +} + static int mount_devices( const char *where, const char *root_device, bool root_device_rw, @@ -2458,6 +2475,7 @@ static int inner_child( FDSet *fds) { _cleanup_free_ char *home = NULL; + char as_uuid[37]; unsigned n_env = 1; const char *envp[] = { "PATH=" DEFAULT_PATH_SPLIT_USR, @@ -2575,12 +2593,10 @@ static int inner_child( (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) return log_oom(); - if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) { - char as_uuid[37]; + assert(!sd_id128_equal(arg_uuid, SD_ID128_NULL)); - if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) - return log_oom(); - } + if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) + return log_oom(); if (fdset_size(fds) > 0) { r = fdset_cloexec(fds, false); @@ -2648,7 +2664,8 @@ static int inner_child( execvpe(arg_parameters[0], arg_parameters, env_use); else { if (!arg_chdir) - chdir(home ?: "/root"); + /* If we cannot change the directory, we'll end up in /, that is expected. */ + (void) chdir(home ?: "/root"); execle("/bin/bash", "-bash", NULL, env_use); execle("/bin/sh", "-sh", NULL, env_use); @@ -2669,6 +2686,7 @@ static int outer_child( bool interactive, bool secondary, int pid_socket, + int uuid_socket, int kmsg_socket, int rtnl_socket, int uid_shift_socket, @@ -2682,6 +2700,7 @@ static int outer_child( assert(directory); assert(console); assert(pid_socket >= 0); + assert(uuid_socket >= 0); assert(kmsg_socket >= 0); cg_unified_flush(); @@ -2796,6 +2815,10 @@ static int outer_child( if (r < 0) return r; + r = setup_machine_id(directory); + if (r < 0) + return r; + r = setup_journal(directory); if (r < 0) return r; @@ -2821,6 +2844,7 @@ static int outer_child( return log_error_errno(errno, "Failed to fork inner child: %m"); if (pid == 0) { pid_socket = safe_close(pid_socket); + uuid_socket = safe_close(uuid_socket); uid_shift_socket = safe_close(uid_shift_socket); /* The inner child has all namespaces that are @@ -2842,7 +2866,16 @@ static int outer_child( return -EIO; } + l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send machine ID: %m"); + if (l != sizeof(arg_uuid)) { + log_error("Short write while sending machine ID."); + return -EIO; + } + pid_socket = safe_close(pid_socket); + uuid_socket = safe_close(uuid_socket); kmsg_socket = safe_close(kmsg_socket); rtnl_socket = safe_close(rtnl_socket); @@ -3318,7 +3351,8 @@ int main(int argc, char *argv[]) { } for (;;) { - _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 }; + _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, + pid_socket_pair[2] = { -1, -1 }, uuid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 }; ContainerStatus container_status; _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; static const struct sigaction sa = { @@ -3353,6 +3387,11 @@ int main(int argc, char *argv[]) { goto finish; } + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create id socket pair: %m"); + goto finish; + } + if (arg_userns) if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create uid shift socket pair: %m"); @@ -3393,6 +3432,7 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); pid_socket_pair[0] = safe_close(pid_socket_pair[0]); + uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]); uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]); (void) reset_all_signal_handlers(); @@ -3407,6 +3447,7 @@ int main(int argc, char *argv[]) { interactive, secondary, pid_socket_pair[1], + uuid_socket_pair[1], kmsg_socket_pair[1], rtnl_socket_pair[1], uid_shift_socket_pair[1], @@ -3424,6 +3465,7 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]); pid_socket_pair[1] = safe_close(pid_socket_pair[1]); + uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); /* Wait for the outer child. */ @@ -3448,6 +3490,18 @@ int main(int argc, char *argv[]) { goto finish; } + /* We also retrieve container UUID in case it was generated by outer child */ + l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read container machine ID: %m"); + goto finish; + } + if (l != sizeof(arg_uuid)) { + log_error("Short read while reading container machined ID."); + r = EIO; + goto finish; + } + log_debug("Init process invoked as PID " PID_FMT, pid); if (arg_userns) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 16cae8c1e5..33f7c61557 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -424,8 +424,9 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (added <= 0) { _cleanup_free_ char *ip = NULL; - in_addr_to_string(q->request_family, &q->request_address, &ip); - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip)); + (void) in_addr_to_string(q->request_family, &q->request_address, &ip); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, + "Address '%s' does not have any RR of requested type", strnull(ip)); goto finish; } diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 0af7551425..a54aed3a63 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1734,7 +1734,7 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - wc = strjoina("*.", common_suffix, NULL); + wc = strjoina("*.", common_suffix); return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name); } diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index a5129c201e..081131ede0 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -262,7 +262,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { if (manager_our_packet(t->scope->manager, p) != 0) return; - in_addr_to_string(p->family, &p->sender, &pretty); + (void) in_addr_to_string(p->family, &p->sender, &pretty); log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.", t->id, @@ -270,7 +270,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { dns_protocol_to_string(t->scope->protocol), t->scope->link ? t->scope->link->name : "*", af_to_name_short(t->scope->family), - pretty); + strnull(pretty)); /* RFC 4795, Section 4.1 says that the peer with the * lexicographically smaller IP address loses */ diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index c9b5ffa62b..b3018e8239 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -27,77 +27,89 @@ #include "string-util.h" #include "hexdecoct.h" -static void test_dnssec_verify_rrset2(void) { +static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { + char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - static const uint8_t signature_blob[] = { - 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, - 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, - 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, - 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, - 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, - 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, - 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, - 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, - }; + assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); + if (r < 0) + return; + assert_se(streq(canonicalized, canonical)); +} + +static void test_dnssec_canonicalize(void) { + test_dnssec_canonicalize_one("", ".", 1); + test_dnssec_canonicalize_one(".", ".", 1); + test_dnssec_canonicalize_one("foo", "foo.", 4); + test_dnssec_canonicalize_one("foo.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); + test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); +} + +#ifdef HAVE_GCRYPT + +static void test_dnssec_verify_dns_key(void) { + + static const uint8_t ds1_fprint[] = { + 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, + 0x80, 0x67, 0x14, 0x01, + }; + static const uint8_t ds2_fprint[] = { + 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, + 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + }; static const uint8_t dnskey_blob[] = { - 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, - 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, - 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, - 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, - 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, - 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, - 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, - 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, - 0x74, 0x62, 0xfe, 0xd7, + 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, + 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, + 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, + 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, + 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, + 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, + 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, + 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, + 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, + 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, + 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, + 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, + 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, + 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, + 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, + 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, + 0xe7, 0xea, 0x77, 0x03, }; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - DnssecResult result; - - nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); - assert_se(nsec); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL; - nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); - assert_se(nsec->nsec.next_domain_name); + /* The two DS RRs in effect for nasa.gov on 2015-12-01. */ + ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov"); + assert_se(ds1); - nsec->nsec.types = bitmap_new(); - assert_se(nsec->nsec.types); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); - assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); + ds1->ds.key_tag = 47857; + ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; + ds1->ds.digest_size = sizeof(ds1_fprint); + ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size); + assert_se(ds1->ds.digest); - log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec))); + log_info("DS1: %s", strna(dns_resource_record_to_string(ds1))); - rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); - assert_se(rrsig); + ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); + assert_se(ds2); - rrsig->rrsig.type_covered = DNS_TYPE_NSEC; - rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; - rrsig->rrsig.labels = 2; - rrsig->rrsig.original_ttl = 300; - rrsig->rrsig.expiration = 0x5689002f; - rrsig->rrsig.inception = 0x56617230; - rrsig->rrsig.key_tag = 30390; - rrsig->rrsig.signer = strdup("Nasa.Gov."); - assert_se(rrsig->rrsig.signer); - rrsig->rrsig.signature_size = sizeof(signature_blob); - rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); - assert_se(rrsig->rrsig.signature); + ds2->ds.key_tag = 47857; + ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; + ds2->ds.digest_size = sizeof(ds2_fprint); + ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size); + assert_se(ds2->ds.digest); - log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + log_info("DS2: %s", strna(dns_resource_record_to_string(ds2))); - dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); assert_se(dnskey); - dnskey->dnskey.flags = 256; + dnskey->dnskey.flags = 257; dnskey->dnskey.protocol = 3; dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; dnskey->dnskey.key_size = sizeof(dnskey_blob); @@ -107,16 +119,8 @@ static void test_dnssec_verify_rrset2(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); - assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); - - answer = dns_answer_new(1); - assert_se(answer); - assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0); - - /* Validate the RR as it if was 2015-12-11 today */ - assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); - assert_se(result == DNSSEC_VALIDATED); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); } static void test_dnssec_verify_rrset(void) { @@ -198,67 +202,77 @@ static void test_dnssec_verify_rrset(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_dns_key(void) { +static void test_dnssec_verify_rrset2(void) { - static const uint8_t ds1_fprint[] = { - 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, - 0x80, 0x67, 0x14, 0x01, - }; - static const uint8_t ds2_fprint[] = { - 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, - 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + static const uint8_t signature_blob[] = { + 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, + 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, + 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, + 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, + 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, + 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, + 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, + 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, }; + static const uint8_t dnskey_blob[] = { - 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, - 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, - 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, - 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, - 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, - 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, - 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, - 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, - 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, - 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, - 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, - 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, - 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, - 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, - 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, - 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, - 0xe7, 0xea, 0x77, 0x03, + 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, + 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, + 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, + 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, + 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, + 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, + 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, + 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, + 0x74, 0x62, 0xfe, 0xd7, }; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; - /* The two DS RRs in effect for nasa.gov on 2015-12-01. */ - ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov"); - assert_se(ds1); + nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); + assert_se(nsec); - ds1->ds.key_tag = 47857; - ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; - ds1->ds.digest_size = sizeof(ds1_fprint); - ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size); - assert_se(ds1->ds.digest); + nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); + assert_se(nsec->nsec.next_domain_name); - log_info("DS1: %s", strna(dns_resource_record_to_string(ds1))); + nsec->nsec.types = bitmap_new(); + assert_se(nsec->nsec.types); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); + assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); - ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); - assert_se(ds2); + log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec))); - ds2->ds.key_tag = 47857; - ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; - ds2->ds.digest_size = sizeof(ds2_fprint); - ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size); - assert_se(ds2->ds.digest); + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); + assert_se(rrsig); - log_info("DS2: %s", strna(dns_resource_record_to_string(ds2))); + rrsig->rrsig.type_covered = DNS_TYPE_NSEC; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rrsig->rrsig.labels = 2; + rrsig->rrsig.original_ttl = 300; + rrsig->rrsig.expiration = 0x5689002f; + rrsig->rrsig.inception = 0x56617230; + rrsig->rrsig.key_tag = 30390; + rrsig->rrsig.signer = strdup("Nasa.Gov."); + assert_se(rrsig->rrsig.signer); + rrsig->rrsig.signature_size = sizeof(signature_blob); + rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); + assert_se(rrsig->rrsig.signature); - dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); + log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); assert_se(dnskey); - dnskey->dnskey.flags = 257; + dnskey->dnskey.flags = 256; dnskey->dnskey.protocol = 3; dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; dnskey->dnskey.key_size = sizeof(dnskey_blob); @@ -268,28 +282,16 @@ static void test_dnssec_verify_dns_key(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); - assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); -} - -static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { - char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - - assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); - if (r < 0) - return; + assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); - assert_se(streq(canonicalized, canonical)); -} + answer = dns_answer_new(1); + assert_se(answer); + assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0); -static void test_dnssec_canonicalize(void) { - test_dnssec_canonicalize_one("", ".", 1); - test_dnssec_canonicalize_one(".", ".", 1); - test_dnssec_canonicalize_one("foo", "foo.", 4); - test_dnssec_canonicalize_one("foo.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); - test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); + /* Validate the RR as it if was 2015-12-11 today */ + assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); + assert_se(result == DNSSEC_VALIDATED); } static void test_dnssec_nsec3_hash(void) { @@ -324,9 +326,12 @@ static void test_dnssec_nsec3_hash(void) { assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0); } +#endif + int main(int argc, char*argv[]) { test_dnssec_canonicalize(); + #ifdef HAVE_GCRYPT test_dnssec_verify_dns_key(); test_dnssec_verify_rrset(); diff --git a/src/run/run.c b/src/run/run.c index f92a7f4e2e..1993a424ca 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -103,7 +103,7 @@ static void help(void) { " --uid=USER Run as system user\n" " --gid=GROUP Run as system group\n" " --nice=NICE Nice level\n" - " --setenv=NAME=VALUE Set environment\n" + " -E --setenv=NAME=VALUE Set environment\n" " -t --pty Run service on pseudo tty\n" " -q --quiet Suppress information messages during runtime\n\n" "Timer options:\n\n" @@ -125,7 +125,6 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, - ARG_NO_ASK_PASSWORD, ARG_USER, ARG_SYSTEM, ARG_SCOPE, @@ -133,12 +132,10 @@ static int parse_argv(int argc, char *argv[]) { ARG_DESCRIPTION, ARG_SLICE, ARG_SEND_SIGHUP, + ARG_SERVICE_TYPE, ARG_EXEC_USER, ARG_EXEC_GROUP, - ARG_SERVICE_TYPE, ARG_NICE, - ARG_SETENV, - ARG_TTY, ARG_ON_ACTIVE, ARG_ON_BOOT, ARG_ON_STARTUP, @@ -147,6 +144,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_ON_CALENDAR, ARG_TIMER_PROPERTY, ARG_NO_BLOCK, + ARG_NO_ASK_PASSWORD, }; static const struct option options[] = { @@ -166,9 +164,10 @@ static int parse_argv(int argc, char *argv[]) { { "uid", required_argument, NULL, ARG_EXEC_USER }, { "gid", required_argument, NULL, ARG_EXEC_GROUP }, { "nice", required_argument, NULL, ARG_NICE }, - { "setenv", required_argument, NULL, ARG_SETENV }, + { "setenv", required_argument, NULL, 'E' }, { "property", required_argument, NULL, 'p' }, - { "tty", no_argument, NULL, 't' }, + { "tty", no_argument, NULL, 't' }, /* deprecated */ + { "pty", no_argument, NULL, 't' }, { "quiet", no_argument, NULL, 'q' }, { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, { "on-boot", required_argument, NULL, ARG_ON_BOOT }, @@ -266,7 +265,7 @@ static int parse_argv(int argc, char *argv[]) { arg_nice_set = true; break; - case ARG_SETENV: + case 'E': if (strv_extend(&arg_environment, optarg) < 0) return log_oom(); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 0caaca03c7..6a1877d8aa 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -2050,8 +2050,8 @@ static void log_job_error_with_service_result(const char* service, const char *r _cleanup_free_ char *t; t = strv_join((char**) extra_args, " "); - systemctl = strjoina("systemctl ", t ?: "<args>", NULL); - journalctl = strjoina("journalctl ", t ?: "<args>", NULL); + systemctl = strjoina("systemctl ", t ? : "<args>"); + journalctl = strjoina("journalctl ", t ? : "<args>"); } if (!isempty(result)) { @@ -2200,14 +2200,16 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, Un return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { - if (!quiet) { - if (streq(type, "symlink")) - log_info("Created symlink from %s to %s.", path, source); - else - log_info("Removed symlink %s.", path); + /* We expect only "success" changes to be sent over the bus. + Hence, reject anything negative. */ + UnitFileChangeType ch = unit_file_change_type_from_string(type); + + if (ch < 0) { + log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); + continue; } - r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source); + r = unit_file_changes_add(changes, n_changes, ch, path, source); if (r < 0) return r; } @@ -2218,6 +2220,7 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, Un if (r < 0) return bus_log_parse_error(r); + unit_file_dump_changes(0, NULL, *changes, *n_changes, false); return 0; } diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index bd0a1f483b..1141f9964f 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -732,7 +732,7 @@ int config_parse_strv(const char *unit, for (;;) { char *word = NULL; int r; - r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE); if (r == 0) break; if (r == -ENOMEM) diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 0d3da2e6d2..ade2de7727 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -17,14 +17,24 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#warning "Temporary work-around for broken glibc vs. linux kernel header definitions" +#warning "This really should be removed sooner rather than later, when this is fixed upstream" +#define _NET_IF_H 1 + #include <alloca.h> #include <arpa/inet.h> #include <endian.h> #include <errno.h> -#include <net/if.h> #include <stddef.h> #include <string.h> #include <sys/socket.h> +#include <net/if.h> +#include <linux/if.h> +#ifndef IFNAMSIZ +#undef _NET_IF_H +/* Let's make sure to include this one, too, if IFNAMSIZ isn't defined yet, as it is for kernels <= 4.2 */ +#include <net/if.h> +#endif #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter/nf_nat.h> #include <linux/netfilter/xt_addrtype.h> diff --git a/src/shared/install.c b/src/shared/install.c index ef8f485cae..71012eafb4 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -45,6 +45,7 @@ #include "mkdir.h" #include "path-lookup.h" #include "path-util.h" +#include "rm-rf.h" #include "set.h" #include "special.h" #include "stat-util.h" @@ -65,7 +66,9 @@ typedef struct { OrderedHashmap *have_processed; } InstallContext; -static int in_search_path(const char *path, char **search) { +static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); + +static int in_search_path(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; char **i; @@ -75,141 +78,141 @@ static int in_search_path(const char *path, char **search) { if (!parent) return -ENOMEM; - STRV_FOREACH(i, search) + STRV_FOREACH(i, p->search_path) if (path_equal(parent, *i)) return true; return false; } -static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { - char *p = NULL; - int r; +static const char* skip_root(const LookupPaths *p, const char *path) { + char *e; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(ret); - - /* This determines where we shall create or remove our - * installation ("configuration") symlinks */ + assert(p); + assert(path); - switch (scope) { + if (!p->root_dir) + return path; - case UNIT_FILE_SYSTEM: + e = path_startswith(path, p->root_dir); + if (!e) + return NULL; - if (runtime) - p = path_join(root_dir, "/run/systemd/system", NULL); - else - p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL); - break; + /* Make sure the returned path starts with a slash */ + if (e[0] != '/') { + if (e == path || e[-1] != '/') + return NULL; - case UNIT_FILE_GLOBAL: + e--; + } - if (root_dir) - return -EINVAL; + return e; +} - if (runtime) - p = strdup("/run/systemd/user"); - else - p = strdup(USER_CONFIG_UNIT_PATH); - break; +static int path_is_generator(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - case UNIT_FILE_USER: + assert(p); + assert(path); - if (root_dir) - return -EINVAL; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - if (runtime) - r = user_runtime_dir(&p); - else - r = user_config_home(&p); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; + return path_equal_ptr(parent, p->generator) || + path_equal_ptr(parent, p->generator_early) || + path_equal_ptr(parent, p->generator_late); +} - break; +static int path_is_transient(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - default: - assert_not_reached("Bad scope"); - } + assert(p); + assert(path); - if (!p) + parent = dirname_malloc(path); + if (!parent) return -ENOMEM; - *ret = p; - return 0; + return path_equal_ptr(parent, p->transient); } -static bool is_config_path(UnitFileScope scope, const char *path) { - int r; +static int path_is_control(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(p); assert(path); - /* Checks whether the specified path is intended for - * configuration or is outside of it */ + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - switch (scope) { + return path_equal_ptr(parent, p->persistent_control) || + path_equal_ptr(parent, p->runtime_control); +} - case UNIT_FILE_SYSTEM: - case UNIT_FILE_GLOBAL: - return path_startswith(path, "/etc") || - path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) || - path_startswith(path, "/run"); +static int path_is_config(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + assert(p); + assert(path); - case UNIT_FILE_USER: { - _cleanup_free_ char *p = NULL; + /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern + * configuration from transient or generated units */ - r = user_config_home(&p); - if (r < 0) - return r; - if (r > 0 && path_startswith(path, p)) - return true; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - p = mfree(p); + return path_equal_ptr(parent, p->persistent_config) || + path_equal_ptr(parent, p->runtime_config); +} - r = user_runtime_dir(&p); - if (r < 0) - return r; - if (r > 0 && path_startswith(path, p)) - return true; +static int path_is_runtime(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + const char *rpath; - return false; - } + assert(p); + assert(path); - default: - assert_not_reached("Bad scope"); - } -} + /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime + * directories, as safety net. */ + rpath = skip_root(p, path); + if (rpath && path_startswith(rpath, "/run")) + return true; -static int verify_root_dir(UnitFileScope scope, const char **root_dir) { - int r; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - assert(root_dir); + return path_equal_ptr(parent, p->runtime_config) || + path_equal_ptr(parent, p->generator) || + path_equal_ptr(parent, p->generator_early) || + path_equal_ptr(parent, p->generator_late) || + path_equal_ptr(parent, p->transient) || + path_equal_ptr(parent, p->runtime_control); +} + +static int path_is_vendor(const LookupPaths *p, const char *path) { + const char *rpath; - /* Verifies that the specified root directory to operate on - * makes sense. Reset it to NULL if it is the root directory - * or set to empty */ + assert(p); + assert(path); - if (isempty(*root_dir) || path_equal(*root_dir, "/")) { - *root_dir = NULL; + rpath = skip_root(p, path); + if (!rpath) return 0; - } - if (scope != UNIT_FILE_SYSTEM) - return -EINVAL; + if (path_startswith(rpath, "/usr")) + return true; - r = is_dir(*root_dir, true); - if (r < 0) - return r; - if (r == 0) - return -ENOTDIR; +#ifdef HAVE_SPLIT_USR + if (path_startswith(rpath, "/lib")) + return true; +#endif - return 0; + return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); } int unit_file_changes_add( @@ -273,6 +276,70 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { free(changes); } +void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) { + unsigned i; + bool logged = false; + + assert(changes || n_changes == 0); + /* If verb is not specified, errors are not allowed! */ + assert(verb || r >= 0); + + for (i = 0; i < n_changes; i++) { + assert(verb || changes[i].type >= 0); + + switch(changes[i].type) { + case UNIT_FILE_SYMLINK: + if (!quiet) + log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source); + break; + case UNIT_FILE_UNLINK: + if (!quiet) + log_info("Removed %s.", changes[i].path); + break; + case UNIT_FILE_IS_MASKED: + if (!quiet) + log_info("Unit %s is masked, ignoring.", changes[i].path); + break; + case -EEXIST: + if (changes[i].source) + log_error_errno(changes[i].type, + "Failed to %s unit, file %s already exists and is a symlink to %s.", + verb, changes[i].path, changes[i].source); + else + log_error_errno(changes[i].type, + "Failed to %s unit, file %s already exists.", + verb, changes[i].path); + logged = true; + break; + case -ERFKILL: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.", + verb, changes[i].path); + logged = true; + break; + case -EADDRNOTAVAIL: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.", + verb, changes[i].path); + logged = true; + break; + case -ELOOP: + log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s", + verb, changes[i].path); + logged = true; + break; + default: + assert(changes[i].type < 0); + log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.", + verb, changes[i].path); + logged = true; + } + } + + if (r < 0 && !logged) + log_error_errno(r, "Failed to %s: %m.", verb); +} + + + static int create_symlink( const char *old_path, const char *new_path, @@ -294,11 +361,13 @@ static int create_symlink( if (symlink(old_path, new_path) >= 0) { unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + return 1; } - if (errno != EEXIST) + if (errno != EEXIST) { + unit_file_changes_add(changes, n_changes, -errno, new_path, NULL); return -errno; + } r = readlink_malloc(new_path, &dest); if (r < 0) @@ -307,8 +376,10 @@ static int create_symlink( if (path_equal(dest, old_path)) return 0; - if (!force) + if (!force) { + unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest); return -EEXIST; + } r = symlink_atomic(old_path, new_path); if (r < 0) @@ -317,7 +388,7 @@ static int create_symlink( unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + return 1; } static int mark_symlink_for_removal( @@ -353,6 +424,7 @@ static int remove_marked_symlinks_fd( int fd, const char *path, const char *config_path, + const LookupPaths *lp, bool *restart, UnitFileChange **changes, unsigned *n_changes) { @@ -365,6 +437,7 @@ static int remove_marked_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(restart); d = fdopendir(fd); @@ -400,12 +473,13 @@ static int remove_marked_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes); + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, restart, changes, n_changes); if (q < 0 && r == 0) r = q; } else if (de->d_type == DT_LNK) { _cleanup_free_ char *p = NULL, *dest = NULL; + const char *rp; bool found; int q; @@ -415,42 +489,43 @@ static int remove_marked_symlinks_fd( p = path_make_absolute(de->d_name, path); if (!p) return -ENOMEM; + path_kill_slashes(p); q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; if (q < 0) { - if (q == -ENOENT) - continue; - if (r == 0) r = q; continue; } - /* We remove all links pointing to a file or - * path that is marked, as well as all files - * sharing the same name as a file that is - * marked. */ + /* We remove all links pointing to a file or path that is marked, as well as all files sharing + * the same name as a file that is marked. */ - found = - set_contains(remove_symlinks_to, dest) || + found = set_contains(remove_symlinks_to, dest) || set_contains(remove_symlinks_to, basename(dest)) || set_contains(remove_symlinks_to, de->d_name); if (!found) continue; - if (unlink(p) < 0 && errno != ENOENT) { + if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { if (r == 0) r = -errno; + unit_file_changes_add(changes, n_changes, -errno, p, NULL); continue; } - path_kill_slashes(p); (void) rmdir_parents(p, config_path); unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - q = mark_symlink_for_removal(&remove_symlinks_to, p); + /* Now, remember the full path (but with the root prefix removed) of the symlink we just + * removed, and remove any symlinks to it, too */ + + rp = skip_root(lp, p); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); if (q < 0) return q; if (q > 0) @@ -464,6 +539,7 @@ static int remove_marked_symlinks_fd( static int remove_marked_symlinks( Set *remove_symlinks_to, const char *config_path, + const LookupPaths *lp, UnitFileChange **changes, unsigned *n_changes) { @@ -472,6 +548,7 @@ static int remove_marked_symlinks( int r = 0; assert(config_path); + assert(lp); if (set_size(remove_symlinks_to) <= 0) return 0; @@ -489,7 +566,7 @@ static int remove_marked_symlinks( return -errno; /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes); + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, &restart, changes, n_changes); if (r == 0) r = q; } while (restart); @@ -503,6 +580,7 @@ static int find_symlinks_fd( int fd, const char *path, const char *config_path, + const LookupPaths *lp, bool *same_name_link) { _cleanup_closedir_ DIR *d = NULL; @@ -513,6 +591,7 @@ static int find_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(same_name_link); d = fdopendir(fd); @@ -546,7 +625,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); if (q > 0) return 1; if (r == 0) @@ -624,6 +703,7 @@ static int find_symlinks( const char *root_dir, const char *name, const char *config_path, + const LookupPaths *lp, bool *same_name_link) { int fd; @@ -640,29 +720,25 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); } static int find_symlinks_in_scope( UnitFileScope scope, - const char *root_dir, + const LookupPaths *paths, const char *name, UnitFileState *state) { - _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL; bool same_name_link_runtime = false, same_name_link = false; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(paths); assert(name); - /* First look in the normal config path */ - r = get_config_path(scope, false, root_dir, &normal_path); - if (r < 0) - return r; - - r = find_symlinks(root_dir, name, normal_path, &same_name_link); + /* First look in the persistent config path */ + r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); if (r < 0) return r; if (r > 0) { @@ -671,11 +747,7 @@ static int find_symlinks_in_scope( } /* Then look in runtime config path */ - r = get_config_path(scope, true, root_dir, &runtime_path); - if (r < 0) - return r; - - r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime); + r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); if (r < 0) return r; if (r > 0) { @@ -742,6 +814,30 @@ static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *nam return ordered_hashmap_get(c->will_process, name); } +static int install_info_may_process( + UnitFileInstallInfo *i, + const LookupPaths *paths, + UnitFileChange **changes, + unsigned *n_changes) { + assert(i); + assert(paths); + + /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus + * not subject to enable/disable operations. */ + + if (i->type == UNIT_FILE_TYPE_MASKED) { + unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL); + return -ERFKILL; + } + if (path_is_generator(paths, i->path) || + path_is_transient(paths, i->path)) { + unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL); + return -EADDRNOTAVAIL; + } + + return 0; +} + static int install_info_add( InstallContext *c, const char *name, @@ -804,20 +900,6 @@ fail: return r; } -static int install_info_add_auto( - InstallContext *c, - const char *name_or_path, - UnitFileInstallInfo **ret) { - - assert(c); - assert(name_or_path); - - if (path_is_absolute(name_or_path)) - return install_info_add(c, NULL, name_or_path, ret); - else - return install_info_add(c, name_or_path, NULL, ret); -} - static int config_parse_also( const char *unit, const char *filename, @@ -900,7 +982,6 @@ static int unit_file_load( InstallContext *c, UnitFileInstallInfo *info, const char *path, - const char *root_dir, SearchFlags flags) { const ConfigTableItem items[] = { @@ -921,8 +1002,6 @@ static int unit_file_load( assert(info); assert(path); - path = prefix_roota(root_dir, path); - if (!(flags & SEARCH_LOAD)) { r = lstat(path, &st); if (r < 0) @@ -983,26 +1062,26 @@ static int unit_file_load_or_readlink( const char *root_dir, SearchFlags flags) { - _cleanup_free_ char *np = NULL; + _cleanup_free_ char *target = NULL; int r; - r = unit_file_load(c, info, path, root_dir, flags); + r = unit_file_load(c, info, path, flags); if (r != -ELOOP) return r; /* This is a symlink, let's read it. */ - r = readlink_and_make_absolute_root(root_dir, path, &np); + r = readlink_malloc(path, &target); if (r < 0) return r; - if (path_equal(np, "/dev/null")) + if (path_equal(target, "/dev/null")) info->type = UNIT_FILE_TYPE_MASKED; else { const char *bn; UnitType a, b; - bn = basename(np); + bn = basename(target); if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { @@ -1029,9 +1108,16 @@ static int unit_file_load_or_readlink( if (a < 0 || b < 0 || a != b) return -EINVAL; + if (path_is_absolute(target)) + /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ + info->symlink_target = prefix_root(root_dir, target); + else + /* This is a relative path, take it relative to the dir the symlink is located in. */ + info->symlink_target = file_in_same_dir(path, target); + if (!info->symlink_target) + return -ENOMEM; + info->type = UNIT_FILE_TYPE_SYMLINK; - info->symlink_target = np; - np = NULL; } return 0; @@ -1041,7 +1127,6 @@ static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, - const char *root_dir, SearchFlags flags) { char **p; @@ -1056,18 +1141,18 @@ static int unit_file_search( return 0; if (info->path) - return unit_file_load_or_readlink(c, info, info->path, root_dir, flags); + return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags); assert(info->name); - STRV_FOREACH(p, paths->unit_path) { + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", info->name, NULL); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); if (r < 0) { if (r != -ENOENT) return r; @@ -1090,14 +1175,14 @@ static int unit_file_search( if (r < 0) return r; - STRV_FOREACH(p, paths->unit_path) { + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", template, NULL); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); if (r < 0) { if (r != -ENOENT) return r; @@ -1143,7 +1228,6 @@ static int install_info_follow( static int install_info_traverse( UnitFileScope scope, InstallContext *c, - const char *root_dir, const LookupPaths *paths, UnitFileInstallInfo *start, SearchFlags flags, @@ -1157,7 +1241,7 @@ static int install_info_traverse( assert(start); assert(c); - r = unit_file_search(c, start, paths, root_dir, flags); + r = unit_file_search(c, start, paths, flags); if (r < 0) return r; @@ -1168,10 +1252,15 @@ static int install_info_traverse( if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) return -ELOOP; - if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path)) - return -ELOOP; + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { + r = path_is_config(paths, i->path); + if (r < 0) + return r; + if (r > 0) + return -ELOOP; + } - r = install_info_follow(c, i, root_dir, flags); + r = install_info_follow(c, i, paths->root_dir, flags); if (r < 0) { _cleanup_free_ char *buffer = NULL; const char *bn; @@ -1205,7 +1294,7 @@ static int install_info_traverse( if (r < 0) return r; - r = unit_file_search(c, i, paths, root_dir, flags); + r = unit_file_search(c, i, paths, flags); if (r < 0) return r; } @@ -1219,10 +1308,28 @@ static int install_info_traverse( return 0; } +static int install_info_add_auto( + InstallContext *c, + const LookupPaths *paths, + const char *name_or_path, + UnitFileInstallInfo **ret) { + + assert(c); + assert(name_or_path); + + if (path_is_absolute(name_or_path)) { + const char *pp; + + pp = prefix_roota(paths->root_dir, name_or_path); + + return install_info_add(c, NULL, pp, ret); + } else + return install_info_add(c, name_or_path, NULL, ret); +} + static int install_info_discover( UnitFileScope scope, InstallContext *c, - const char *root_dir, const LookupPaths *paths, const char *name, SearchFlags flags, @@ -1235,15 +1342,16 @@ static int install_info_discover( assert(paths); assert(name); - r = install_info_add_auto(c, name, &i); + r = install_info_add_auto(c, paths, name, &i); if (r < 0) return r; - return install_info_traverse(scope, c, root_dir, paths, i, flags, ret); + return install_info_traverse(scope, c, paths, i, flags, ret); } static int install_info_symlink_alias( UnitFileInstallInfo *i, + const LookupPaths *paths, const char *config_path, bool force, UnitFileChange **changes, @@ -1253,10 +1361,12 @@ static int install_info_symlink_alias( int r = 0, q; assert(i); + assert(paths); assert(config_path); STRV_FOREACH(s, i->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL; + const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1266,7 +1376,9 @@ static int install_info_symlink_alias( if (!alias_path) return -ENOMEM; - q = create_symlink(i->path, alias_path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); if (r == 0) r = q; } @@ -1276,6 +1388,7 @@ static int install_info_symlink_alias( static int install_info_symlink_wants( UnitFileInstallInfo *i, + const LookupPaths *paths, const char *config_path, char **list, const char *suffix, @@ -1289,6 +1402,7 @@ static int install_info_symlink_wants( int r = 0, q; assert(i); + assert(paths); assert(config_path); if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { @@ -1309,6 +1423,7 @@ static int install_info_symlink_wants( STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; + const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1323,7 +1438,9 @@ static int install_info_symlink_wants( if (!path) return -ENOMEM; - q = create_symlink(i->path, path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, path, force, changes, n_changes); if (r == 0) r = q; } @@ -1335,12 +1452,12 @@ static int install_info_symlink_link( UnitFileInstallInfo *i, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { _cleanup_free_ char *path = NULL; + const char *rp; int r; assert(i); @@ -1348,22 +1465,25 @@ static int install_info_symlink_link( assert(config_path); assert(i->path); - r = in_search_path(i->path, paths->unit_path); - if (r != 0) + r = in_search_path(paths, i->path); + if (r < 0) return r; + if (r > 0) + return 0; path = strjoin(config_path, "/", i->name, NULL); if (!path) return -ENOMEM; - return create_symlink(i->path, path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + return create_symlink(rp ?: i->path, path, force, changes, n_changes); } static int install_info_apply( UnitFileInstallInfo *i, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1377,18 +1497,19 @@ static int install_info_apply( if (i->type != UNIT_FILE_TYPE_REGULAR) return 0; - r = install_info_symlink_alias(i, config_path, force, changes, n_changes); + r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); - q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); + q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", force, changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes); + q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", force, changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + /* Do not count links to the unit file towards the "carries_install_info" count */ + if (r == 0 && q < 0) r = q; return r; @@ -1399,7 +1520,6 @@ static int install_context_apply( InstallContext *c, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, SearchFlags flags, UnitFileChange **changes, @@ -1427,19 +1547,19 @@ static int install_context_apply( if (q < 0) return q; - r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL); + r = install_info_traverse(scope, c, paths, i, flags, NULL); if (r < 0) return r; if (i->type != UNIT_FILE_TYPE_REGULAR) continue; - q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); + q = install_info_apply(i, paths, config_path, force, changes, n_changes); if (r >= 0) { if (q < 0) r = q; else - r+= q; + r += q; } } @@ -1451,8 +1571,7 @@ static int install_context_mark_for_removal( InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, - const char *config_path, - const char *root_dir) { + const char *config_path) { UnitFileInstallInfo *i; int r; @@ -1476,7 +1595,7 @@ static int install_context_mark_for_removal( if (r < 0) return r; - r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r < 0) return r; @@ -1500,20 +1619,19 @@ int unit_file_mask( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_free_ char *prefix = NULL; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + const char *config_path; char **i; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1525,7 +1643,7 @@ int unit_file_mask( continue; } - path = path_make_absolute(*i, prefix); + path = path_make_absolute(*i, config_path); if (!path) return -ENOMEM; @@ -1545,23 +1663,22 @@ int unit_file_unmask( UnitFileChange **changes, unsigned *n_changes) { + _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_free_ char *config_path = NULL; _cleanup_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; + const char *config_path; char **i; int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1592,24 +1709,31 @@ int unit_file_unmask( r = 0; STRV_FOREACH(i, todo) { _cleanup_free_ char *path = NULL; + const char *rp; path = path_make_absolute(*i, config_path); if (!path) return -ENOMEM; if (unlink(path) < 0) { - if (errno != -ENOENT && r >= 0) - r = -errno; - } else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - if (q < 0) - return q; + if (errno != ENOENT) { + if (r >= 0) + r = -errno; + unit_file_changes_add(changes, n_changes, -errno, path, NULL); + } - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + continue; } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + + rp = skip_root(&paths, path); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); + if (q < 0) + return q; } - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); if (r >= 0) r = q; @@ -1626,26 +1750,20 @@ int unit_file_link( unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; _cleanup_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; + const char *config_path; char **i; int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *full = NULL; @@ -1659,7 +1777,7 @@ int unit_file_link( if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) return -EINVAL; - full = prefix_root(root_dir, *i); + full = prefix_root(paths.root_dir, *i); if (!full) return -ENOMEM; @@ -1672,7 +1790,7 @@ int unit_file_link( if (!S_ISREG(st.st_mode)) return -ENOTTY; - q = in_search_path(*i, paths.unit_path); + q = in_search_path(&paths, *i); if (q < 0) return q; if (q > 0) @@ -1688,13 +1806,15 @@ int unit_file_link( r = 0; STRV_FOREACH(i, todo) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *new_path = NULL; + const char *old_path; - path = path_make_absolute(basename(*i), config_path); - if (!path) + old_path = skip_root(&paths, *i); + new_path = path_make_absolute(basename(*i), config_path); + if (!new_path) return -ENOMEM; - q = create_symlink(*i, path, force, changes, n_changes); + q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -1702,6 +1822,182 @@ int unit_file_link( return r; } +static int path_shall_revert(const LookupPaths *paths, const char *path) { + int r; + + assert(paths); + assert(path); + + /* Checks whether the path is one where the drop-in directories shall be removed. */ + + r = path_is_config(paths, path); + if (r != 0) + return r; + + r = path_is_control(paths, path); + if (r != 0) + return r; + + return path_is_transient(paths, path); +} + +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + /* _cleanup_(install_context_done) InstallContext c = {}; */ + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + /* Puts a unit file back into vendor state. This means: + * + * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and + * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). + * + * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in + * "config", but not in "transient" or "control" or even "generated"). + * + * We remove all that in both the runtime and the persistant directories, if that applies. + */ + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + bool has_vendor = false; + char **p; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL, *dropin = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode)) { + /* Check if there's a vendor version */ + r = path_is_vendor(&paths, path); + if (r < 0) + return r; + if (r > 0) + has_vendor = true; + } + + dropin = strappend(path, ".d"); + if (!dropin) + return -ENOMEM; + + r = lstat(dropin, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISDIR(st.st_mode)) { + /* Remove the drop-ins */ + r = path_shall_revert(&paths, dropin); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = dropin; + dropin = NULL; + } + } + } + + if (!has_vendor) + continue; + + /* OK, there's a vendor version, hence drop all configuration versions */ + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + r = path_is_config(&paths, path); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = path; + path = NULL; + } + } + } + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_strv_free_ char **fs = NULL; + const char *rp; + char **j; + + (void) get_files_in_directory(*i, &fs); + + q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); + if (q < 0 && q != -ENOENT && r >= 0) { + r = q; + continue; + } + + STRV_FOREACH(j, fs) { + _cleanup_free_ char *t = NULL; + + t = strjoin(*i, "/", *j, NULL); + if (!t) + return -ENOMEM; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); + } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); + + rp = skip_root(&paths, *i); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); + if (q < 0) + return q; + } + + q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + int unit_file_add_dependency( UnitFileScope scope, bool runtime, @@ -1715,8 +2011,8 @@ int unit_file_add_dependency( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; UnitFileInstallInfo *i, *target_info; + const char *config_path; char **f; int r; @@ -1730,34 +2026,30 @@ int unit_file_add_dependency( if (!unit_name_is_valid(target, UNIT_NAME_ANY)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - r = get_config_path(scope, runtime, root_dir, &config_path); + r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + r = install_info_may_process(target_info, &paths, changes, n_changes); if (r < 0) return r; - if (target_info->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(target_info->type == UNIT_FILE_TYPE_REGULAR); STRV_FOREACH(f, files) { char ***l; - r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); @@ -1776,7 +2068,7 @@ int unit_file_add_dependency( return -ENOMEM; } - return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } int unit_file_enable( @@ -1790,7 +2082,7 @@ int unit_file_enable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path; UnitFileInstallInfo *i; char **f; int r; @@ -1798,24 +2090,19 @@ int unit_file_enable( assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(f, files) { - r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i); + r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); } @@ -1825,7 +2112,7 @@ int unit_file_enable( is useful to determine whether the passed files had any installation data at all. */ - return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes); } int unit_file_disable( @@ -1838,25 +2125,19 @@ int unit_file_disable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + const char *config_path; char **i; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) @@ -1867,11 +2148,11 @@ int unit_file_disable( return r; } - r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir); + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path); if (r < 0) return r; - return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); } int unit_file_reenable( @@ -1912,41 +2193,34 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; UnitFileInstallInfo *i; - const char *path; + const char *new_path, *old_path; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - if (unit_name_to_type(name) != UNIT_TARGET) + if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ return -EINVAL; if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, false, root_dir, &config_path); + r = install_info_discover(scope, &c, &paths, name, 0, &i); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i); + r = install_info_may_process(i, &paths, changes, n_changes); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; - path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET); + old_path = skip_root(&paths, i->path); + new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - return create_symlink(i->path, path, force, changes, n_changes); + return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); } int unit_file_get_default( @@ -1964,19 +2238,16 @@ int unit_file_get_default( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_may_process(i, &paths, NULL, 0); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; n = strdup(i->name); if (!n) @@ -1986,9 +2257,8 @@ int unit_file_get_default( return 0; } -int unit_file_lookup_state( +static int unit_file_lookup_state( UnitFileScope scope, - const char *root_dir, const LookupPaths *paths, const char *name, UnitFileState *ret) { @@ -2004,11 +2274,7 @@ int unit_file_lookup_state( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; @@ -2019,11 +2285,31 @@ int unit_file_lookup_state( switch (i->type) { case UNIT_FILE_TYPE_MASKED: - state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + r = path_is_runtime(paths, i->path); + if (r < 0) + return r; + + state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; break; case UNIT_FILE_TYPE_REGULAR: - r = find_symlinks_in_scope(scope, root_dir, i->name, &state); + r = path_is_generator(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_GENERATED; + break; + } + + r = path_is_transient(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_TRANSIENT; + break; + } + + r = find_symlinks_in_scope(scope, paths, i->name, &state); if (r < 0) return r; if (r == 0) { @@ -2058,15 +2344,30 @@ int unit_file_get_state( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + return unit_file_lookup_state(scope, &paths, name, ret); +} + +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { + _cleanup_(install_context_done) InstallContext c = {}; + int r; + + assert(paths); + assert(name); + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_discover(scope, &c, paths, name, 0, NULL); + if (r == -ENOENT) + return 0; if (r < 0) return r; - return unit_file_lookup_state(scope, root_dir, &paths, name, ret); + return 1; } int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { @@ -2078,10 +2379,6 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; @@ -2164,7 +2461,6 @@ static int execute_preset( InstallContext *minus, const LookupPaths *paths, const char *config_path, - const char *root_dir, char **files, UnitFilePresetMode mode, bool force, @@ -2181,11 +2477,11 @@ static int execute_preset( if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir); + r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path); if (r < 0) return r; - r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, changes, n_changes); } else r = 0; @@ -2193,12 +2489,12 @@ static int execute_preset( int q; /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes); if (r >= 0) { if (q < 0) r = q; else - r+= q; + r += q; } } @@ -2210,9 +2506,10 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - const char *root_dir, UnitFilePresetMode mode, - const char *name) { + const char *name, + UnitFileChange **changes, + unsigned *n_changes) { UnitFileInstallInfo *i; int r; @@ -2221,19 +2518,20 @@ static int preset_prepare_one( install_info_find(minus, name)) return 0; - r = unit_file_query_preset(scope, root_dir, name); + r = unit_file_query_preset(scope, paths->root_dir, name); if (r < 0) return r; if (r > 0) { - r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; + r = install_info_may_process(i, paths, changes, n_changes); + if (r < 0) + return r; } else - r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); return r; } @@ -2250,7 +2548,7 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path; char **i; int r; @@ -2258,28 +2556,22 @@ int unit_file_preset( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i); + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, changes, n_changes); if (r < 0) return r; } - return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes); + return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes); } int unit_file_preset_all( @@ -2293,7 +2585,7 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path = NULL; char **i; int r; @@ -2301,28 +2593,17 @@ int unit_file_preset_all( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - STRV_FOREACH(i, paths.unit_path) { + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *units_dir; struct dirent *de; - units_dir = path_join(root_dir, *i, NULL); - if (!units_dir) - return -ENOMEM; - - d = opendir(units_dir); + d = opendir(*i); if (!d) { if (errno == ENOENT) continue; @@ -2340,13 +2621,17 @@ int unit_file_preset_all( if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name); + /* we don't pass changes[] in, because we want to handle errors on our own */ + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, NULL, 0); + if (r == -ERFKILL) + r = unit_file_changes_add(changes, n_changes, + UNIT_FILE_IS_MASKED, de->d_name, NULL); if (r < 0) return r; } } - return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes); + return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes); } static void unit_file_list_free_one(UnitFileList *f) { @@ -2381,24 +2666,15 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - STRV_FOREACH(i, paths.unit_path) { + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *units_dir; struct dirent *de; - units_dir = path_join(root_dir, *i, NULL); - if (!units_dir) - return -ENOMEM; - - d = opendir(units_dir); + d = opendir(*i); if (!d) { if (errno == ENOENT) continue; @@ -2424,11 +2700,11 @@ int unit_file_get_list( if (!f) return -ENOMEM; - f->path = path_make_absolute(de->d_name, units_dir); + f->path = path_make_absolute(de->d_name, *i); if (!f->path) return -ENOMEM; - r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state); + r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); if (r < 0) f->state = UNIT_FILE_BAD; @@ -2453,6 +2729,8 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", [UNIT_FILE_BAD] = "bad", }; @@ -2461,6 +2739,7 @@ DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { [UNIT_FILE_SYMLINK] = "symlink", [UNIT_FILE_UNLINK] = "unlink", + [UNIT_FILE_IS_MASKED] = "masked", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); diff --git a/src/shared/install.h b/src/shared/install.h index c1a43e23e7..4133faffa2 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -54,6 +54,8 @@ enum UnitFileState { UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT, UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 @@ -70,16 +72,29 @@ enum UnitFilePresetMode { enum UnitFileChangeType { UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK, + UNIT_FILE_IS_MASKED, _UNIT_FILE_CHANGE_TYPE_MAX, - _UNIT_FILE_CHANGE_TYPE_INVALID = -1 + _UNIT_FILE_CHANGE_INVALID = INT_MIN }; +/* type can either one of the UnitFileChangeTypes listed above, or a negative error. + * If source is specified, it should be the contents of the path symlink. + * In case of an error, source should be the existing symlink contents or NULL + */ struct UnitFileChange { - UnitFileChangeType type; + int type; /* UnitFileChangeType or bust */ char *path; char *source; }; +static inline bool unit_file_changes_have_modification(const UnitFileChange* changes, unsigned n_changes) { + unsigned i; + for (i = 0; i < n_changes; i++) + if (IN_SET(changes[i].type, UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK)) + return true; + return false; +} + struct UnitFileList { char *path; UnitFileState state; @@ -123,31 +138,112 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { return !strv_isempty(i->also); } -int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); -int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_preset_all(UnitFileScope scope, bool runtime, const char *root_dir, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); -int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); -int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); - -int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret); +int unit_file_enable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_disable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_reenable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_preset( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_preset_all( + UnitFileScope scope, + bool runtime, + const char *root_dir, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_link( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_set_default( + UnitFileScope scope, + const char *root_dir, + const char *file, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_get_default( + UnitFileScope scope, + const char *root_dir, + char **name); +int unit_file_add_dependency( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + const char *target, + UnitDependency dep, + bool force, + UnitFileChange **changes, + unsigned *n_changes); + int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); Hashmap* unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); +void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet); int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name); const char *unit_file_state_to_string(UnitFileState s) _const_; UnitFileState unit_file_state_from_string(const char *s) _pure_; +/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */ const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_; UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_; diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index d2f1c4a40c..bebfc40efe 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -401,8 +401,7 @@ int image_remove(Image *i) { assert(i); - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; settings = image_settings_path(i); @@ -474,8 +473,7 @@ int image_rename(Image *i, const char *new_name) { if (!image_name_is_valid(new_name)) return -EINVAL; - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; settings = image_settings_path(i); @@ -642,8 +640,7 @@ int image_read_only(Image *i, bool b) { int r; assert(i); - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; /* Make sure we don't interfere with a running nspawn */ @@ -751,8 +748,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile int image_set_limit(Image *i, uint64_t referenced_max) { assert(i); - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; if (i->type != IMAGE_SUBVOLUME) diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index 31b720d50c..7410168c4f 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -25,6 +25,8 @@ #include "hashmap.h" #include "lockfile-util.h" #include "macro.h" +#include "path-util.h" +#include "string-util.h" #include "time-util.h" typedef enum ImageType { @@ -75,3 +77,27 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile int image_name_lock(const char *name, int operation, LockFile *ret); int image_set_limit(Image *i, uint64_t referenced_max); + +static inline bool IMAGE_IS_HIDDEN(const struct Image *i) { + assert(i); + + return i->name && i->name[0] == '.'; +} + +static inline bool IMAGE_IS_VENDOR(const struct Image *i) { + assert(i); + + return i->path && path_startswith(i->path, "/usr"); +} + +static inline bool IMAGE_IS_HOST(const struct Image *i) { + assert(i); + + if (i->name && streq(i->name, ".host")) + return true; + + if (i->path && path_equal(i->path, "/")) + return true; + + return false; +} diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 5410620725..80a2ea7940 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -26,61 +26,66 @@ #include "install.h" #include "log.h" #include "macro.h" +#include "mkdir.h" #include "path-lookup.h" #include "path-util.h" +#include "rm-rf.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "util.h" -int user_config_home(char **config_home) { +static int user_runtime_dir(char **ret, const char *suffix) { const char *e; - char *r; + char *j; - e = getenv("XDG_CONFIG_HOME"); - if (e) { - r = strappend(e, "/systemd/user"); - if (!r) - return -ENOMEM; - - *config_home = r; - return 1; - } else { - const char *home; + assert(ret); + assert(suffix); - home = getenv("HOME"); - if (home) { - r = strappend(home, "/.config/systemd/user"); - if (!r) - return -ENOMEM; + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; - *config_home = r; - return 1; - } - } + j = strappend(e, suffix); + if (!j) + return -ENOMEM; + *ret = j; return 0; } -int user_runtime_dir(char **runtime_dir) { +static int user_config_dir(char **ret, const char *suffix) { const char *e; - char *r; + char *j; - e = getenv("XDG_RUNTIME_DIR"); - if (e) { - r = strappend(e, "/systemd/user"); - if (!r) - return -ENOMEM; + assert(ret); + + e = getenv("XDG_CONFIG_HOME"); + if (e) + j = strappend(e, suffix); + else { + const char *home; - *runtime_dir = r; - return 1; + home = getenv("HOME"); + if (!home) + return -ENXIO; + + j = strjoin(home, "/.config", suffix, NULL); } + if (!j) + return -ENOMEM; + + *ret = j; return 0; } -static int user_data_home_dir(char **dir, const char *suffix) { +static int user_data_dir(char **ret, const char *suffix) { const char *e; - char *res; + char *j; + + assert(ret); + assert(suffix); /* We don't treat /etc/xdg/systemd here as the spec * suggests because we assume that that is a link to @@ -88,27 +93,33 @@ static int user_data_home_dir(char **dir, const char *suffix) { e = getenv("XDG_DATA_HOME"); if (e) - res = strappend(e, suffix); + j = strappend(e, suffix); else { const char *home; home = getenv("HOME"); - if (home) - res = strjoin(home, "/.local/share", suffix, NULL); - else - return 0; + if (!home) + return -ENXIO; + + + j = strjoin(home, "/.local/share", suffix, NULL); } - if (!res) + if (!j) return -ENOMEM; - *dir = res; - return 0; + *ret = j; + return 1; } static char** user_dirs( + const char *persistent_config, + const char *runtime_config, const char *generator, const char *generator_early, - const char *generator_late) { + const char *generator_late, + const char *transient, + const char *persistent_control, + const char *runtime_control) { const char * const config_unit_paths[] = { USER_CONFIG_UNIT_PATH, @@ -116,8 +127,6 @@ static char** user_dirs( NULL }; - const char * const runtime_unit_path = "/run/systemd/user"; - const char * const data_unit_paths[] = { "/usr/local/lib/systemd/user", "/usr/local/share/systemd/user", @@ -128,8 +137,8 @@ static char** user_dirs( }; const char *e; - _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL; _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; + _cleanup_free_ char *data_home = NULL; _cleanup_free_ char **res = NULL; char **tmp; int r; @@ -143,12 +152,6 @@ static char** user_dirs( * as data, and allow overriding as configuration. */ - if (user_config_home(&config_home) < 0) - return NULL; - - if (user_runtime_dir(&runtime_dir) < 0) - return NULL; - e = getenv("XDG_CONFIG_DIRS"); if (e) { config_dirs = strv_split(e, ":"); @@ -156,8 +159,8 @@ static char** user_dirs( return NULL; } - r = user_data_home_dir(&data_home, "/systemd/user"); - if (r < 0) + r = user_data_dir(&data_home, "/systemd/user"); + if (r < 0 && r != -ENXIO) return NULL; e = getenv("XDG_DATA_DIRS"); @@ -171,35 +174,36 @@ static char** user_dirs( return NULL; /* Now merge everything we found. */ - if (generator_early) - if (strv_extend(&res, generator_early) < 0) - return NULL; + if (strv_extend(&res, persistent_control) < 0) + return NULL; - if (config_home) - if (strv_extend(&res, config_home) < 0) - return NULL; + if (strv_extend(&res, runtime_control) < 0) + return NULL; + + if (strv_extend(&res, transient) < 0) + return NULL; + + if (strv_extend(&res, generator_early) < 0) + return NULL; if (!strv_isempty(config_dirs)) if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) + if (strv_extend(&res, persistent_config) < 0) return NULL; - if (runtime_dir) - if (strv_extend(&res, runtime_dir) < 0) - return NULL; + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) + return NULL; - if (strv_extend(&res, runtime_unit_path) < 0) + if (strv_extend(&res, runtime_config) < 0) return NULL; - if (generator) - if (strv_extend(&res, generator) < 0) - return NULL; + if (strv_extend(&res, generator) < 0) + return NULL; - if (data_home) - if (strv_extend(&res, data_home) < 0) - return NULL; + if (strv_extend(&res, data_home) < 0) + return NULL; if (!strv_isempty(data_dirs)) if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) @@ -208,9 +212,8 @@ static char** user_dirs( if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) return NULL; - if (generator_late) - if (strv_extend(&res, generator_late) < 0) - return NULL; + if (strv_extend(&res, generator_late) < 0) + return NULL; if (path_strv_make_absolute_cwd(res) < 0) return NULL; @@ -220,58 +223,298 @@ static char** user_dirs( return tmp; } -char **generator_paths(ManagerRunningAs running_as) { - if (running_as == MANAGER_USER) - return strv_new("/run/systemd/user-generators", - "/etc/systemd/user-generators", - "/usr/local/lib/systemd/user-generators", - USER_GENERATOR_PATH, - NULL); - else - return strv_new("/run/systemd/system-generators", - "/etc/systemd/system-generators", - "/usr/local/lib/systemd/system-generators", - SYSTEM_GENERATOR_PATH, - NULL); +static int acquire_generator_dirs( + UnitFileScope scope, + char **generator, + char **generator_early, + char **generator_late) { + + _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; + const char *prefix; + + assert(generator); + assert(generator_early); + assert(generator_late); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + prefix = "/run/systemd/"; + break; + + case UNIT_FILE_USER: { + const char *e; + + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; + + prefix = strjoina(e, "/systemd/"); + break; + } + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + x = strappend(prefix, "generator"); + if (!x) + return -ENOMEM; + + y = strappend(prefix, "generator.early"); + if (!y) + return -ENOMEM; + + z = strappend(prefix, "generator.late"); + if (!z) + return -ENOMEM; + + *generator = x; + *generator_early = y; + *generator_late = z; + + x = y = z = NULL; + return 0; +} + +static int acquire_transient_dir(UnitFileScope scope, char **ret) { + assert(ret); + + switch (scope) { + + case UNIT_FILE_SYSTEM: { + char *transient; + + transient = strdup("/run/systemd/transient"); + if (!transient) + return -ENOMEM; + + *ret = transient; + return 0; + } + + case UNIT_FILE_USER: + return user_runtime_dir(ret, "/systemd/transient"); + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } +} + +static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { + _cleanup_free_ char *a = NULL, *b = NULL; + int r; + + assert(persistent); + assert(runtime); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + a = strdup(SYSTEM_CONFIG_UNIT_PATH); + b = strdup("/run/systemd/system"); + break; + + case UNIT_FILE_GLOBAL: + a = strdup(USER_CONFIG_UNIT_PATH); + b = strdup("/run/systemd/user"); + break; + + case UNIT_FILE_USER: + r = user_config_dir(&a, "/systemd/user"); + if (r < 0) + return r; + + r = user_runtime_dir(runtime, "/systemd/user"); + if (r < 0) + return r; + + *persistent = a; + a = NULL; + + return 0; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + if (!a || !b) + return -ENOMEM; + + *persistent = a; + *runtime = b; + a = b = NULL; + + return 0; +} + +static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) { + _cleanup_free_ char *a = NULL; + int r; + + assert(persistent); + assert(runtime); + + switch (scope) { + + case UNIT_FILE_SYSTEM: { + _cleanup_free_ char *b = NULL; + + a = strdup("/etc/systemd/system.control"); + if (!a) + return -ENOMEM; + + b = strdup("/run/systemd/system.control"); + if (!b) + return -ENOMEM; + + *runtime = b; + b = NULL; + + break; + } + + case UNIT_FILE_USER: + r = user_config_dir(&a, "/systemd/system.control"); + if (r < 0) + return r; + + r = user_runtime_dir(runtime, "/systemd/system.control"); + if (r < 0) + return r; + + break; + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + *persistent = a; + a = NULL; + + return 0; +} + +static int patch_root_prefix(char **p, const char *root_dir) { + char *c; + + assert(p); + + if (!*p) + return 0; + + c = prefix_root(root_dir, *p); + if (!c) + return -ENOMEM; + + free(*p); + *p = c; + + return 0; +} + +static int patch_root_prefix_strv(char **l, const char *root_dir) { + char **i; + int r; + + if (!root_dir) + return 0; + + STRV_FOREACH(i, l) { + r = patch_root_prefix(i, root_dir); + if (r < 0) + return r; + } + + return 0; } int lookup_paths_init( LookupPaths *p, - ManagerRunningAs running_as, - bool personal, - const char *root_dir, - const char *generator, - const char *generator_early, - const char *generator_late) { - - const char *e; + UnitFileScope scope, + LookupPathsFlags flags, + const char *root_dir) { + + _cleanup_free_ char + *root = NULL, + *persistent_config = NULL, *runtime_config = NULL, + *generator = NULL, *generator_early = NULL, *generator_late = NULL, + *transient = NULL, + *persistent_control = NULL, *runtime_control = NULL; bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */ + _cleanup_strv_free_ char **paths = NULL; + const char *e; int r; assert(p); + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + if (!isempty(root_dir) && !path_equal(root_dir, "/")) { + if (scope == UNIT_FILE_USER) + return -EINVAL; + + r = is_dir(root_dir, true); + if (r < 0) + return r; + if (r == 0) + return -ENOTDIR; + + root = strdup(root_dir); + if (!root) + return -ENOMEM; + } + + r = acquire_config_dirs(scope, &persistent_config, &runtime_config); + if (r < 0 && r != -ENXIO) + return r; + + if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) { + r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + } + + r = acquire_transient_dir(scope, &transient); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + + r = acquire_control_dirs(scope, &persistent_control, &runtime_control); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; - /* First priority is whatever has been passed to us via env - * vars */ + /* First priority is whatever has been passed to us via env vars */ e = getenv("SYSTEMD_UNIT_PATH"); if (e) { - if (endswith(e, ":")) { - e = strndupa(e, strlen(e) - 1); + const char *k; + + k = endswith(e, ":"); + if (k) { + e = strndupa(e, k - e); append = true; } /* FIXME: empty components in other places should be * rejected. */ - r = path_split_and_make_absolute(e, &p->unit_path); + r = path_split_and_make_absolute(e, &paths); if (r < 0) return r; - } else - p->unit_path = NULL; + } - if (!p->unit_path || append) { + if (!paths || append) { /* Let's figure something out. */ - _cleanup_strv_free_ char **unit_path; + _cleanup_strv_free_ char **add = NULL; /* For the user units we include share/ in the search * path in order to comply with the XDG basedir spec. @@ -279,17 +522,45 @@ int lookup_paths_init( * we include /lib in the search path for the system * stuff but avoid it for user stuff. */ - if (running_as == MANAGER_USER) { - if (personal) - unit_path = user_dirs(generator, generator_early, generator_late); - else - unit_path = strv_new( + switch (scope) { + + case UNIT_FILE_SYSTEM: + add = strv_new( + /* If you modify this you also want to modify + * systemdsystemunitpath= in systemd.pc.in! */ + STRV_IFNOTNULL(persistent_control), + STRV_IFNOTNULL(runtime_control), + STRV_IFNOTNULL(transient), + STRV_IFNOTNULL(generator_early), + persistent_config, + SYSTEM_CONFIG_UNIT_PATH, + "/etc/systemd/system", + runtime_config, + "/run/systemd/system", + STRV_IFNOTNULL(generator), + "/usr/local/lib/systemd/system", + SYSTEM_DATA_UNIT_PATH, + "/usr/lib/systemd/system", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/system", +#endif + STRV_IFNOTNULL(generator_late), + NULL); + break; + + case UNIT_FILE_GLOBAL: + add = strv_new( /* If you modify this you also want to modify * systemduserunitpath= in systemd.pc.in, and * the arrays in user_dirs() above! */ + STRV_IFNOTNULL(persistent_control), + STRV_IFNOTNULL(runtime_control), + STRV_IFNOTNULL(transient), STRV_IFNOTNULL(generator_early), + persistent_config, USER_CONFIG_UNIT_PATH, "/etc/systemd/user", + runtime_config, "/run/systemd/user", STRV_IFNOTNULL(generator), "/usr/local/lib/systemd/user", @@ -299,143 +570,246 @@ int lookup_paths_init( "/usr/share/systemd/user", STRV_IFNOTNULL(generator_late), NULL); - } else - unit_path = strv_new( - /* If you modify this you also want to modify - * systemdsystemunitpath= in systemd.pc.in! */ - STRV_IFNOTNULL(generator_early), - SYSTEM_CONFIG_UNIT_PATH, - "/etc/systemd/system", - "/run/systemd/system", - STRV_IFNOTNULL(generator), - "/usr/local/lib/systemd/system", - SYSTEM_DATA_UNIT_PATH, - "/usr/lib/systemd/system", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/system", -#endif - STRV_IFNOTNULL(generator_late), - NULL); + break; - if (!unit_path) + case UNIT_FILE_USER: + add = user_dirs(persistent_config, runtime_config, + generator, generator_early, generator_late, + transient, + persistent_config, runtime_control); + break; + + default: + assert_not_reached("Hmm, unexpected scope?"); + } + + if (!add) return -ENOMEM; - r = strv_extend_strv(&p->unit_path, unit_path, false); + r = strv_extend_strv(&paths, add, true); if (r < 0) - return r; + return r; } - if (!path_strv_resolve_uniq(p->unit_path, root_dir)) + r = patch_root_prefix(&persistent_config, root); + if (r < 0) + return r; + r = patch_root_prefix(&runtime_config, root); + if (r < 0) + return r; + + r = patch_root_prefix(&generator, root); + if (r < 0) + return r; + r = patch_root_prefix(&generator_early, root); + if (r < 0) + return r; + r = patch_root_prefix(&generator_late, root); + if (r < 0) + return r; + + r = patch_root_prefix(&transient, root); + if (r < 0) + return r; + + r = patch_root_prefix(&persistent_control, root); + if (r < 0) + return r; + + r = patch_root_prefix(&runtime_control, root); + if (r < 0) + return r; + + r = patch_root_prefix_strv(paths, root); + if (r < 0) return -ENOMEM; - if (!strv_isempty(p->unit_path)) { - _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t"); - if (!t) - return -ENOMEM; - log_debug("Looking for unit files in (higher priority first):\n\t%s", t); - } else { - log_debug("Ignoring unit files."); - p->unit_path = strv_free(p->unit_path); - } + p->search_path = strv_uniq(paths); + paths = NULL; - if (running_as == MANAGER_SYSTEM) { -#ifdef HAVE_SYSV_COMPAT - /* /etc/init.d/ compatibility does not matter to users */ + p->persistent_config = persistent_config; + p->runtime_config = runtime_config; + persistent_config = runtime_config = NULL; - e = getenv("SYSTEMD_SYSVINIT_PATH"); - if (e) { - r = path_split_and_make_absolute(e, &p->sysvinit_path); - if (r < 0) - return r; - } else - p->sysvinit_path = NULL; + p->generator = generator; + p->generator_early = generator_early; + p->generator_late = generator_late; + generator = generator_early = generator_late = NULL; - if (strv_isempty(p->sysvinit_path)) { - strv_free(p->sysvinit_path); + p->transient = transient; + transient = NULL; - p->sysvinit_path = strv_new( - SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */ - NULL); - if (!p->sysvinit_path) - return -ENOMEM; - } + p->persistent_control = persistent_control; + p->runtime_control = runtime_control; + persistent_control = runtime_control = NULL; - e = getenv("SYSTEMD_SYSVRCND_PATH"); - if (e) { - r = path_split_and_make_absolute(e, &p->sysvrcnd_path); - if (r < 0) - return r; - } else - p->sysvrcnd_path = NULL; + p->root_dir = root; + root = NULL; + + return 0; +} - if (strv_isempty(p->sysvrcnd_path)) { - strv_free(p->sysvrcnd_path); +void lookup_paths_free(LookupPaths *p) { + if (!p) + return; - p->sysvrcnd_path = strv_new( - SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */ - NULL); - if (!p->sysvrcnd_path) - return -ENOMEM; + p->search_path = strv_free(p->search_path); + + p->persistent_config = mfree(p->persistent_config); + p->runtime_config = mfree(p->runtime_config); + + p->generator = mfree(p->generator); + p->generator_early = mfree(p->generator_early); + p->generator_late = mfree(p->generator_late); + + p->transient = mfree(p->transient); + + p->persistent_control = mfree(p->persistent_control); + p->runtime_control = mfree(p->runtime_control); + + p->root_dir = mfree(p->root_dir); +} + +int lookup_paths_reduce(LookupPaths *p) { + _cleanup_free_ struct stat *stats = NULL; + size_t n_stats = 0, allocated = 0; + unsigned c = 0; + int r; + + assert(p); + + /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are + * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set, + * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into + * account when following symlinks. When we have no root path set this restriction does not apply however. */ + + if (!p->search_path) + return 0; + + while (p->search_path[c]) { + struct stat st; + unsigned k; + + if (p->root_dir) + r = lstat(p->search_path[c], &st); + else + r = stat(p->search_path[c], &st); + if (r < 0) { + if (errno == ENOENT) + goto remove_item; + + /* If something we don't grok happened, let's better leave it in. */ + log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]); + c++; + continue; } - if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir)) - return -ENOMEM; + for (k = 0; k < n_stats; k++) { + if (stats[k].st_dev == st.st_dev && + stats[k].st_ino == st.st_ino) + break; + } + + if (k < n_stats) /* Is there already an entry with the same device/inode? */ + goto remove_item; - if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir)) + if (!GREEDY_REALLOC(stats, allocated, n_stats+1)) return -ENOMEM; - if (!strv_isempty(p->sysvinit_path)) { - _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t"); - if (!t) - return -ENOMEM; - log_debug("Looking for SysV init scripts in:\n\t%s", t); - } else { - log_debug("Ignoring SysV init scripts."); - p->sysvinit_path = strv_free(p->sysvinit_path); - } + stats[n_stats++] = st; + c++; + continue; - if (!strv_isempty(p->sysvrcnd_path)) { - _cleanup_free_ char *t = - strv_join(p->sysvrcnd_path, "\n\t"); - if (!t) - return -ENOMEM; + remove_item: + free(p->search_path[c]); + memmove(p->search_path + c, + p->search_path + c + 1, + (strv_length(p->search_path + c + 1) + 1) * sizeof(char*)); + } - log_debug("Looking for SysV rcN.d links in:\n\t%s", t); - } else { - log_debug("Ignoring SysV rcN.d links."); - p->sysvrcnd_path = strv_free(p->sysvrcnd_path); - } -#else - log_debug("SysV init scripts and rcN.d links support disabled"); -#endif + if (strv_isempty(p->search_path)) { + log_debug("Ignoring unit files."); + p->search_path = strv_free(p->search_path); + } else { + _cleanup_free_ char *t; + + t = strv_join(p->search_path, "\n\t"); + if (!t) + return -ENOMEM; + + log_debug("Looking for unit files in (higher priority first):\n\t%s", t); } return 0; } -void lookup_paths_free(LookupPaths *p) { +int lookup_paths_mkdir_generator(LookupPaths *p) { + int r, q; + assert(p); - p->unit_path = strv_free(p->unit_path); + if (!p->generator || !p->generator_early || !p->generator_late) + return -EINVAL; -#ifdef HAVE_SYSV_COMPAT - p->sysvinit_path = strv_free(p->sysvinit_path); - p->sysvrcnd_path = strv_free(p->sysvrcnd_path); -#endif + r = mkdir_p_label(p->generator, 0755); + + q = mkdir_p_label(p->generator_early, 0755); + if (q < 0 && r >= 0) + r = q; + + q = mkdir_p_label(p->generator_late, 0755); + if (q < 0 && r >= 0) + r = q; + + return r; } -int lookup_paths_init_from_scope(LookupPaths *paths, - UnitFileScope scope, - const char *root_dir) { - assert(paths); - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); +void lookup_paths_trim_generator(LookupPaths *p) { + assert(p); + + /* Trim empty dirs */ + + if (p->generator) + (void) rmdir(p->generator); + if (p->generator_early) + (void) rmdir(p->generator_early); + if (p->generator_late) + (void) rmdir(p->generator_late); +} + +void lookup_paths_flush_generator(LookupPaths *p) { + assert(p); + + /* Flush the generated unit files in full */ + + if (p->generator) + (void) rm_rf(p->generator, REMOVE_ROOT); + if (p->generator_early) + (void) rm_rf(p->generator_early, REMOVE_ROOT); + if (p->generator_late) + (void) rm_rf(p->generator_late, REMOVE_ROOT); +} + +char **generator_binary_paths(UnitFileScope scope) { + + switch (scope) { + + case UNIT_FILE_SYSTEM: + return strv_new("/run/systemd/system-generators", + "/etc/systemd/system-generators", + "/usr/local/lib/systemd/system-generators", + SYSTEM_GENERATOR_PATH, + NULL); - zero(*paths); + case UNIT_FILE_GLOBAL: + case UNIT_FILE_USER: + return strv_new("/run/systemd/user-generators", + "/etc/systemd/user-generators", + "/usr/local/lib/systemd/user-generators", + USER_GENERATOR_PATH, + NULL); - return lookup_paths_init(paths, - scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, - scope == UNIT_FILE_USER, - root_dir, - NULL, NULL, NULL); + default: + assert_not_reached("Hmm, unexpected scope."); + } } diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index 26c83d6111..f9bb2fe237 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -20,41 +20,57 @@ ***/ #include <stdbool.h> -#include "macro.h" -typedef struct LookupPaths { - char **unit_path; -#ifdef HAVE_SYSV_COMPAT - char **sysvinit_path; - char **sysvrcnd_path; -#endif -} LookupPaths; - -typedef enum ManagerRunningAs { - MANAGER_SYSTEM, - MANAGER_USER, - _MANAGER_RUNNING_AS_MAX, - _MANAGER_RUNNING_AS_INVALID = -1 -} ManagerRunningAs; - -int user_config_home(char **config_home); -int user_runtime_dir(char **runtime_dir); - -char **generator_paths(ManagerRunningAs running_as); - -int lookup_paths_init(LookupPaths *p, - ManagerRunningAs running_as, - bool personal, - const char *root_dir, - const char *generator, - const char *generator_early, - const char *generator_late); +typedef struct LookupPaths LookupPaths; #include "install.h" +#include "macro.h" + +typedef enum LookupPathsFlags { + LOOKUP_PATHS_EXCLUDE_GENERATED = 1, +} LookupPathsFlags; + +struct LookupPaths { + /* Where we look for unit files. This includes the individual special paths below, but also any vendor + * supplied, static unit file paths. */ + char **search_path; + + /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin + * shall place his own unit files. */ + char *persistent_config; + char *runtime_config; + + /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of + * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should + * not alter these directories directly. */ + char *generator; + char *generator_early; + char *generator_late; -int lookup_paths_init_from_scope(LookupPaths *paths, - UnitFileScope scope, - const char *root_dir); + /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special + * semantics of this directory: all units created transiently have their unit files removed as the transient + * unit is unloaded. The user should not alter this directory directly. */ + char *transient; + + /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the + * snippets are placed in the transient directory though (see above). The user should not alter this directory + * directly. */ + char *persistent_control; + char *runtime_control; + + /* The root directory prepended to all items above, or NULL */ + char *root_dir; +}; + +int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); + +int lookup_paths_reduce(LookupPaths *p); + +int lookup_paths_mkdir_generator(LookupPaths *p); +void lookup_paths_trim_generator(LookupPaths *p); +void lookup_paths_flush_generator(LookupPaths *p); void lookup_paths_free(LookupPaths *p); #define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free) + +char **generator_binary_paths(UnitFileScope scope); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 35aa60101f..f00624d0f2 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "conf-parser.h" #include "def.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" @@ -231,6 +232,9 @@ static bool enough_memory_for_hibernation(void) { size_t size = 0, used = 0; int r; + if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) + return true; + r = hibernation_partition_size(&size, &used); if (r < 0) return false; diff --git a/src/shared/tests.c b/src/shared/tests.c new file mode 100644 index 0000000000..409116290d --- /dev/null +++ b/src/shared/tests.c @@ -0,0 +1,33 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <util.h> + +#include "tests.h" + +char* setup_fake_runtime_dir(void) { + char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; + + assert_se(mkdtemp(t)); + assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0); + assert_se(p = strdup(t)); + + return p; +} diff --git a/src/shared/tests.h b/src/shared/tests.h new file mode 100644 index 0000000000..93f09013a1 --- /dev/null +++ b/src/shared/tests.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +char* setup_fake_runtime_dir(void); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 62cff3a677..059e985463 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -341,6 +341,12 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) { if (arg_all) return true; + /* Note that '--all' is not purely a state filter, but also a + * filter that hides units that "follow" other units (which is + * used for device units that appear under different names). */ + if (!isempty(u->following)) + return false; + if (!strv_isempty(arg_states)) return true; @@ -349,7 +355,7 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) { if (u->job_id > 0) return true; - if (streq(u->active_state, "inactive") || u->following[0]) + if (streq(u->active_state, "inactive")) return false; return true; @@ -1984,19 +1990,6 @@ static int get_default(int argc, char *argv[], void *userdata) { return 0; } -static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); - - for (i = 0; i < n_changes; i++) { - if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source); - else - log_info("Removed symlink %s.", changes[i].path); - } -} - static int set_default(int argc, char *argv[], void *userdata) { _cleanup_free_ char *unit = NULL; int r; @@ -2013,14 +2006,9 @@ static int set_default(int argc, char *argv[], void *userdata) { unsigned n_changes = 0; r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); - if (r < 0) - return log_error_errno(r, "Failed to set default target: %m"); - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - + unit_file_dump_changes(r, "set default", changes, n_changes, arg_quiet); unit_file_changes_free(changes, n_changes); - r = 0; + return r; } else { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -2295,7 +2283,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un assert(unit_name); assert(unit_path); - STRV_FOREACH(p, lp->unit_path) { + STRV_FOREACH(p, lp->search_path) { _cleanup_free_ char *path; path = path_join(arg_root, *p, unit_name); @@ -2395,7 +2383,7 @@ static int unit_find_paths( } if (dropin_paths) { - r = unit_file_find_dropin_paths(lp->unit_path, NULL, names, &dropins); + r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins); if (r < 0) return r; } @@ -3109,7 +3097,7 @@ static int set_exit_code(uint8_t code) { NULL, "y", code); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r)); return 0; } @@ -3136,7 +3124,7 @@ static int start_special(int argc, char *argv[], void *userdata) { return r; if (a == ACTION_REBOOT && argc > 1) { - r = update_reboot_param_file(argv[1]); + r = update_reboot_parameter_and_warn(argv[1]); if (r < 0) return r; @@ -3254,7 +3242,7 @@ static int kill_unit(int argc, char *argv[], void *userdata) { /* --fail was specified */ if (streq(arg_job_mode, "fail")) - kill_who = strjoina(arg_kill_who, "-fail", NULL); + kill_who = strjoina(arg_kill_who, "-fail"); r = expand_names(bus, strv_skip(argv, 1), NULL, &names); if (r < 0) @@ -4780,34 +4768,6 @@ static int show(int argc, char *argv[], void *userdata) { return ret; } -static int init_home_and_lookup_paths(char **user_home, char **user_runtime, LookupPaths *lp) { - int r; - - assert(user_home); - assert(user_runtime); - assert(lp); - - if (arg_scope == UNIT_FILE_USER) { - r = user_config_home(user_home); - if (r < 0) - return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m"); - else if (r == 0) - return log_error_errno(ENOTDIR, "Cannot find units: $XDG_CONFIG_HOME and $HOME are not set."); - - r = user_runtime_dir(user_runtime); - if (r < 0) - return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m"); - else if (r == 0) - return log_error_errno(ENOTDIR, "Cannot find units: $XDG_RUNTIME_DIR is not set."); - } - - r = lookup_paths_init_from_scope(lp, arg_scope, arg_root); - if (r < 0) - return log_error_errno(r, "Failed to query unit lookup paths: %m"); - - return 0; -} - static int cat_file(const char *filename, bool newline) { _cleanup_close_ int fd; @@ -4826,8 +4786,6 @@ static int cat_file(const char *filename, bool newline) { } static int cat(int argc, char *argv[], void *userdata) { - _cleanup_free_ char *user_home = NULL; - _cleanup_free_ char *user_runtime = NULL; _cleanup_lookup_paths_free_ LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; char **name; @@ -4840,9 +4798,9 @@ static int cat(int argc, char *argv[], void *userdata) { return -EINVAL; } - r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp); + r = lookup_paths_init(&lp, arg_scope, 0, arg_root); if (r < 0) - return r; + return log_error_errno(r, "Failed to determine unit paths: %m"); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -4989,7 +4947,7 @@ static int daemon_reload(int argc, char *argv[], void *userdata) { * reply */ r = 0; else if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); return r < 0 ? r : 0; } @@ -5253,8 +5211,10 @@ static int enable_sysv_units(const char *verb, char **args) { int r = 0; #if defined(HAVE_SYSV_COMPAT) - unsigned f = 0; _cleanup_lookup_paths_free_ LookupPaths paths = {}; + unsigned f = 0; + + /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ if (arg_scope != UNIT_FILE_SYSTEM) return 0; @@ -5268,24 +5228,28 @@ static int enable_sysv_units(const char *verb, char **args) { "is-enabled")) return 0; - /* Processes all SysV units, and reshuffles the array so that - * afterwards only the native units remain */ - - r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, arg_root, NULL, NULL, NULL); + r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); if (r < 0) return r; r = 0; while (args[f]) { - const char *name; + + const char *argv[] = { + ROOTLIBEXECDIR "/systemd-sysv-install", + NULL, + NULL, + NULL, + NULL, + }; + _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; bool found_native = false, found_sysv; + siginfo_t status; + const char *name; unsigned c = 1; - const char *argv[6] = { ROOTLIBEXECDIR "/systemd-sysv-install", NULL, NULL, NULL, NULL }; - char **k; - int j; pid_t pid; - siginfo_t status; + int j; name = args[f++]; @@ -5295,21 +5259,13 @@ static int enable_sysv_units(const char *verb, char **args) { if (path_is_absolute(name)) continue; - STRV_FOREACH(k, paths.unit_path) { - _cleanup_free_ char *path = NULL; + j = unit_file_exists(arg_scope, &paths, name); + if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) + return log_error_errno(j, "Failed to lookup unit file state: %m"); + found_native = j != 0; - path = path_join(arg_root, *k, name); - if (!path) - return log_oom(); - - found_native = access(path, F_OK) >= 0; - if (found_native) - break; - } - - /* If we have both a native unit and a SysV script, - * enable/disable them both (below); for is-enabled, prefer the - * native unit */ + /* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled, + * prefer the native unit */ if (found_native && streq(verb, "is-enabled")) continue; @@ -5323,9 +5279,9 @@ static int enable_sysv_units(const char *verb, char **args) { continue; if (found_native) - log_info("Synchronizing state of %s with SysV init with %s...", name, argv[0]); + log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); else - log_info("%s is not a native service, redirecting to systemd-sysv-install", name); + log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); if (!isempty(arg_root)) argv[c++] = q = strappend("--root=", arg_root); @@ -5338,7 +5294,7 @@ static int enable_sysv_units(const char *verb, char **args) { if (!l) return log_oom(); - log_info("Executing %s", l); + log_info("Executing: %s", l); pid = fork(); if (pid < 0) @@ -5350,7 +5306,7 @@ static int enable_sysv_units(const char *verb, char **args) { (void) reset_signal_mask(); execv(argv[0], (char**) argv); - log_error_errno(r, "Failed to execute %s: %m", argv[0]); + log_error_errno(errno, "Failed to execute %s: %m", argv[0]); _exit(EXIT_FAILURE); } @@ -5372,9 +5328,11 @@ static int enable_sysv_units(const char *verb, char **args) { } } else if (status.si_status != 0) - return -EINVAL; - } else + return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ + } else { + log_error("Unexpected waitid() result."); return -EPROTO; + } if (found_native) continue; @@ -5445,8 +5403,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { if (r < 0) return r; - /* If the operation was fully executed by the SysV compat, - * let's finish early */ + /* If the operation was fully executed by the SysV compat, let's finish early */ if (strv_isempty(names)) return 0; @@ -5468,23 +5425,20 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); + else if (streq(verb, "revert")) + r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes); else assert_not_reached("Unknown verb"); - if (r == -ESHUTDOWN) - return log_error_errno(r, "Unit file is masked."); + unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet); if (r < 0) - return log_error_errno(r, "Operation failed: %m"); - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - + return r; r = 0; } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int expect_carries_install_info = false; - bool send_force = true, send_preset_mode = false; + bool send_runtime = true, send_force = true, send_preset_mode = false; const char *method; sd_bus *bus; @@ -5519,6 +5473,9 @@ static int enable_unit(int argc, char *argv[], void *userdata) { else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; + } else if (streq(verb, "revert")) { + method = "RevertUnitFiles"; + send_runtime = send_force = false; } else assert_not_reached("Unknown verb"); @@ -5542,9 +5499,11 @@ static int enable_unit(int argc, char *argv[], void *userdata) { return bus_log_create_error(r); } - r = sd_bus_message_append(m, "b", arg_runtime); - if (r < 0) - return bus_log_create_error(r); + if (send_runtime) { + r = sd_bus_message_append(m, "b", arg_runtime); + if (r < 0) + return bus_log_create_error(r); + } if (send_force) { r = sd_bus_message_append(m, "b", arg_force); @@ -5554,7 +5513,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r)); if (expect_carries_install_info) { r = sd_bus_message_read(reply, "b", &carries_install_info); @@ -5574,15 +5533,18 @@ static int enable_unit(int argc, char *argv[], void *userdata) { } if (carries_install_info == 0) - log_warning("The unit files have no [Install] section. They are not meant to be enabled\n" - "using systemctl.\n" + log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n" + "settings in the [Install] section, and DefaultInstance for template units).\n" + "This means they are not meant to be enabled using systemctl.\n" "Possible reasons for having this kind of units are:\n" "1) A unit may be statically enabled by being symlinked from another unit's\n" " .wants/ or .requires/ directory.\n" "2) A unit's purpose may be to act as a helper for some other unit which has\n" " a requirement dependency on it.\n" "3) A unit may be started when needed via activation (socket, path, timer,\n" - " D-Bus, udev, scripted systemctl call, ...).\n"); + " D-Bus, udev, scripted systemctl call, ...).\n" + "4) In case of template units, the unit is meant to be enabled with some\n" + " instance name specified."); if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) { char *new_args[n_changes + 2]; @@ -5637,16 +5599,9 @@ static int add_dependency(int argc, char *argv[], void *userdata) { unsigned n_changes = 0; r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); - if (r == -ESHUTDOWN) - return log_error_errno(r, "Unit file is masked."); - if (r < 0) - return log_error_errno(r, "Can't add dependency: %m"); - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - + unit_file_dump_changes(r, "add dependency on", changes, n_changes, arg_quiet); unit_file_changes_free(changes, n_changes); - + return r; } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -5678,39 +5633,29 @@ static int add_dependency(int argc, char *argv[], void *userdata) { r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to add dependency: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) return r; - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else - r = 0; + if (arg_no_reload) + return 0; + return daemon_reload(argc, argv, userdata); } - - return r; } static int preset_all(int argc, char *argv[], void *userdata) { - UnitFileChange *changes = NULL; - unsigned n_changes = 0; int r; if (install_client_side()) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes); - if (r < 0) { - log_error_errno(r, "Operation failed: %m"); - goto finish; - } - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - - r = 0; - + unit_file_dump_changes(r, "preset", changes, n_changes, arg_quiet); + unit_file_changes_free(changes, n_changes); + return r; } else { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -5735,22 +5680,16 @@ static int preset_all(int argc, char *argv[], void *userdata) { arg_runtime, arg_force); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to preset all units: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) return r; - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else - r = 0; + if (arg_no_reload) + return 0; + return daemon_reload(argc, argv, userdata); } - -finish: - unit_file_changes_free(changes, n_changes); - - return r; } static int unit_is_enabled(int argc, char *argv[], void *userdata) { @@ -5783,7 +5722,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_STATIC, - UNIT_FILE_INDIRECT)) + UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED)) enabled = true; if (!arg_quiet) @@ -5818,7 +5758,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect")) + if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated")) enabled = true; if (!arg_quiet) @@ -5826,7 +5766,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { } } - return !enabled; + return enabled ? EXIT_SUCCESS : EXIT_FAILURE; } static int is_system_running(int argc, char *argv[], void *userdata) { @@ -5896,52 +5836,32 @@ static int create_edit_temp_file(const char *new_path, const char *original_path return 0; } -static int get_file_to_edit(const char *name, const char *user_home, const char *user_runtime, char **ret_path) { - _cleanup_free_ char *path = NULL, *path2 = NULL, *run = NULL; +static int get_file_to_edit( + const LookupPaths *paths, + const char *name, + char **ret_path) { + + _cleanup_free_ char *path = NULL, *run = NULL; assert(name); assert(ret_path); - switch (arg_scope) { - case UNIT_FILE_SYSTEM: - path = path_join(arg_root, SYSTEM_CONFIG_UNIT_PATH, name); - if (arg_runtime) - run = path_join(arg_root, "/run/systemd/system/", name); - break; - case UNIT_FILE_GLOBAL: - path = path_join(arg_root, USER_CONFIG_UNIT_PATH, name); - if (arg_runtime) - run = path_join(arg_root, "/run/systemd/user/", name); - break; - case UNIT_FILE_USER: - assert(user_home); - assert(user_runtime); - - path = path_join(arg_root, user_home, name); - if (arg_runtime) { - path2 = path_join(arg_root, USER_CONFIG_UNIT_PATH, name); - if (!path2) - return log_oom(); - run = path_join(arg_root, user_runtime, name); - } - break; - default: - assert_not_reached("Invalid scope"); - } - if (!path || (arg_runtime && !run)) + path = strjoin(paths->persistent_config, "/", name, NULL); + if (!path) return log_oom(); if (arg_runtime) { + run = strjoin(paths->runtime_config, name, NULL); + if (!run) + return log_oom(); + } + + if (arg_runtime) { if (access(path, F_OK) >= 0) { log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path); return -EEXIST; } - if (path2 && access(path2, F_OK) >= 0) { - log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path2); - return -EEXIST; - } - *ret_path = run; run = NULL; } else { @@ -5952,7 +5872,12 @@ static int get_file_to_edit(const char *name, const char *user_home, const char return 0; } -static int unit_file_create_dropin(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) { +static int unit_file_create_dropin( + const LookupPaths *paths, + const char *unit_name, + char **ret_new_path, + char **ret_tmp_path) { + char *tmp_new_path, *tmp_tmp_path, *ending; int r; @@ -5961,7 +5886,7 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home, assert(ret_tmp_path); ending = strjoina(unit_name, ".d/override.conf"); - r = get_file_to_edit(ending, user_home, user_runtime, &tmp_new_path); + r = get_file_to_edit(paths, ending, &tmp_new_path); if (r < 0) return r; @@ -5978,10 +5903,9 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home, } static int unit_file_create_copy( + const LookupPaths *paths, const char *unit_name, const char *fragment_path, - const char *user_home, - const char *user_runtime, char **ret_new_path, char **ret_tmp_path) { @@ -5993,7 +5917,7 @@ static int unit_file_create_copy( assert(ret_new_path); assert(ret_tmp_path); - r = get_file_to_edit(unit_name, user_home, user_runtime, &tmp_new_path); + r = get_file_to_edit(paths, unit_name, &tmp_new_path); if (r < 0) return r; @@ -6108,8 +6032,6 @@ static int run_editor(char **paths) { } static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { - _cleanup_free_ char *user_home = NULL; - _cleanup_free_ char *user_runtime = NULL; _cleanup_lookup_paths_free_ LookupPaths lp = {}; char **name; int r; @@ -6117,13 +6039,12 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { assert(names); assert(paths); - r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp); + r = lookup_paths_init(&lp, arg_scope, 0, arg_root); if (r < 0) return r; STRV_FOREACH(name, names) { - _cleanup_free_ char *path = NULL; - char *new_path, *tmp_path; + _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL; r = unit_find_paths(bus, *name, &lp, &path, NULL); if (r < 0) @@ -6137,15 +6058,16 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { } if (arg_full) - r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path); + r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path); else - r = unit_file_create_dropin(*name, user_home, user_runtime, &new_path, &tmp_path); + r = unit_file_create_dropin(&lp, *name, &new_path, &tmp_path); if (r < 0) return r; r = strv_push_pair(paths, new_path, tmp_path); if (r < 0) return log_oom(); + new_path = tmp_path = NULL; } return 0; @@ -6324,6 +6246,8 @@ static void systemctl_help(void) { " unmask NAME... Unmask one or more units\n" " link PATH... Link one or more units files into\n" " the search path\n" + " revert NAME... Revert one or more unit files to vendor\n" + " version\n" " add-wants TARGET NAME... Add 'Wants' dependency for the target\n" " on specified one or more units\n" " add-requires TARGET NAME... Add 'Requires' dependency for the target\n" @@ -6751,7 +6675,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); + r = parse_path_argument_and_warn(optarg, false, &arg_root); if (r < 0) return r; break; @@ -6992,7 +6916,7 @@ static int halt_parse_argv(int argc, char *argv[]) { } if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { - r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL); + r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL); if (r < 0) return r; } else if (optind < argc) { @@ -7447,6 +7371,7 @@ static int systemctl_main(int argc, char *argv[]) { { "mask", 2, VERB_ANY, 0, enable_unit }, { "unmask", 2, VERB_ANY, 0, enable_unit }, { "link", 2, VERB_ANY, 0, enable_unit }, + { "revert", 2, VERB_ANY, 0, enable_unit }, { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root }, { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies }, { "set-default", 2, 2, 0, set_default }, @@ -7493,6 +7418,7 @@ static int start_with_fallback(void) { } static int halt_now(enum action a) { + int r; /* The kernel will automaticall flush ATA disks and suchlike * on reboot(), but the file systems need to be synce'd @@ -7519,9 +7445,14 @@ static int halt_now(enum action a) { case ACTION_REBOOT: { _cleanup_free_ char *param = NULL; - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { + r = read_one_line_file("/run/systemd/reboot-param", ¶m); + if (r < 0) + log_warning_errno(r, "Failed to read reboot parameter file: %m"); + + if (!isempty(param)) { log_info("Rebooting with argument '%s'.", param); (void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); } log_info("Rebooting."); diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index fa6ab9ad3b..5772d5794a 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -33,20 +33,18 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_lldp sd_lldp; typedef struct sd_lldp_neighbor sd_lldp_neighbor; -#define SD_LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } - /* IEEE 802.3AB Clause 9: TLV Types */ enum { - SD_LLDP_TYPE_END = 0, - SD_LLDP_TYPE_CHASSIS_ID = 1, - SD_LLDP_TYPE_PORT_ID = 2, - SD_LLDP_TYPE_TTL = 3, - SD_LLDP_TYPE_PORT_DESCRIPTION = 4, - SD_LLDP_TYPE_SYSTEM_NAME = 5, - SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, - SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, - SD_LLDP_TYPE_MGMT_ADDRESS = 8, - SD_LLDP_TYPE_PRIVATE = 127, + SD_LLDP_TYPE_END = 0, + SD_LLDP_TYPE_CHASSIS_ID = 1, + SD_LLDP_TYPE_PORT_ID = 2, + SD_LLDP_TYPE_TTL = 3, + SD_LLDP_TYPE_PORT_DESCRIPTION = 4, + SD_LLDP_TYPE_SYSTEM_NAME = 5, + SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, + SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, + SD_LLDP_TYPE_MGMT_ADDRESS = 8, + SD_LLDP_TYPE_PRIVATE = 127, }; /* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ @@ -63,28 +61,28 @@ enum { /* IEEE 802.3AB Clause 9.5.3: Port subtype */ enum { - SD_LLDP_PORT_SUBTYPE_RESERVED = 0, - SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, - SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, - SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, - SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, - SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, - SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, - SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, + SD_LLDP_PORT_SUBTYPE_RESERVED = 0, + SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, + SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, + SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, + SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, + SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, + SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, + SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, }; enum { - SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, - SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, - SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, - SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, - SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, + SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, + SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, + SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, + SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, + SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, }; #define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1) @@ -104,13 +102,13 @@ enum { #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } enum { - SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, - SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, - SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, - SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, - SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, - SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, - SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, + SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, + SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, + SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, + SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, + SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, + SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, + SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, }; typedef enum sd_lldp_event { diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 59e1a3e921..fe4bbeeb75 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -729,14 +729,50 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { return 0; } +static int acquire_search_path(const char *def, const char *envvar, char ***ret) { + _cleanup_strv_free_ char **l = NULL; + const char *e; + int r; + + assert(def); + assert(envvar); + + e = getenv(envvar); + if (e) { + r = path_split_and_make_absolute(e, &l); + if (r < 0) + return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar); + } + + if (strv_isempty(l)) { + strv_free(l); + + l = strv_new(def, NULL); + if (!l) + return log_oom(); + } + + if (!path_strv_resolve_uniq(l, NULL)) + return log_oom(); + + *ret = l; + l = NULL; + + return 0; +} + static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { + _cleanup_strv_free_ char **sysvinit_path = NULL; char **path; int r; assert(lp); - assert(all_services); - STRV_FOREACH(path, lp->sysvinit_path) { + r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path); + if (r < 0) + return r; + + STRV_FOREACH(path, sysvinit_path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -770,11 +806,11 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL); - if (r < 0 && r != -ENOENT) { + r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name); + if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) { log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name); continue; - } else if (r >= 0) { + } else if (r != 0) { log_debug("Native unit for %s already exists, skipping.", name); continue; } @@ -806,6 +842,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) { Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; _cleanup_set_free_ Set *shutdown_services = NULL; + _cleanup_strv_free_ char **sysvrcnd_path = NULL; SysvStub *service; unsigned i; Iterator j; @@ -814,7 +851,11 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic assert(lp); - STRV_FOREACH(p, lp->sysvrcnd_path) { + r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path); + if (r < 0) + return r; + + STRV_FOREACH(p, sysvrcnd_path) { for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { _cleanup_closedir_ DIR *d = NULL; @@ -963,7 +1004,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL); + r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); if (r < 0) { log_error_errno(r, "Failed to find lookup paths: %m"); goto finish; diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index ad15075a5b..4eb8fcd773 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -21,7 +21,9 @@ #include "macro.h" #include "manager.h" +#include "rm-rf.h" #include "test-helper.h" +#include "tests.h" #include "unit.h" static int test_cgroup_mask(void) { @@ -33,7 +35,7 @@ static int test_cgroup_mask(void) { /* Prepare the manager. */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (r == -EPERM || r == -EACCES) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; @@ -107,7 +109,11 @@ static int test_cgroup_mask(void) { } int main(int argc, char* argv[]) { + _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL; int rc = 0; + + assert_se(runtime_dir = setup_fake_runtime_dir()); TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask()); + return rc; } diff --git a/src/test/test-engine.c b/src/test/test-engine.c index ca66f5b684..361d1e7b0b 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -23,9 +23,12 @@ #include "bus-util.h" #include "manager.h" +#include "rm-rf.h" #include "test-helper.h" +#include "tests.h" int main(int argc, char *argv[]) { + _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL; _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; Manager *m = NULL; Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL; @@ -34,9 +37,11 @@ int main(int argc, char *argv[]) { Job *j; int r; + assert_se(runtime_dir = setup_fake_runtime_dir()); + /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 901cc44af6..77ef4e8b2a 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -291,14 +291,14 @@ static void test_exec_spec_interpolation(Manager *m) { test(m, "exec-spec-interpolation.service", 0, CLD_EXITED); } -static int run_tests(ManagerRunningAs running_as, test_function_t *tests) { +static int run_tests(UnitFileScope scope, test_function_t *tests) { test_function_t *test = NULL; Manager *m = NULL; int r; assert_se(tests); - r = manager_new(running_as, true, &m); + r = manager_new(scope, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; @@ -366,9 +366,9 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); - r = run_tests(MANAGER_USER, user_tests); + r = run_tests(UNIT_FILE_USER, user_tests); if (r != 0) return r; - return run_tests(MANAGER_SYSTEM, system_tests); + return run_tests(UNIT_FILE_SYSTEM, system_tests); } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index cd250ca7b8..2d73c9743b 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -30,6 +30,8 @@ static void test_basic_mask_and_enable(const char *root) { UnitFileChange *changes = NULL; unsigned n_changes = 0; + log_set_max_level(LOG_DEBUG); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT); @@ -78,7 +80,7 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); /* Enabling a masked unit should fail! */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ERFKILL); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -105,7 +107,7 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); /* Enabling it again should succeed but be a NOP */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -628,6 +630,57 @@ static void test_preset_and_list(const char *root) { assert_se(got_yes && got_no); } +static void test_revert(const char *root) { + const char *p; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + + assert(root); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/xx.service"); + assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); + + /* Initially there's nothing to revert */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service"); + assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0); + + /* Revert the override file */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf"); + assert_se(mkdir_parents(p, 0755) >= 0); + assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0); + + /* Revert the dropin file */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d"); + assert_se(changes[1].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[1].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + int main(int argc, char *argv[]) { char root[] = "/tmp/rootXXXXXX"; const char *p; @@ -656,6 +709,7 @@ int main(int argc, char *argv[]) { test_template_enable(root); test_indirect(root); test_preset_and_list(root); + test_revert(root); assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); diff --git a/src/test/test-install.c b/src/test/test-install.c index 874d617621..50315c1d9a 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -46,6 +46,9 @@ int main(int argc, char* argv[]) { unsigned n_changes = 0; UnitFileState state = 0; + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + h = hashmap_new(&string_hash_ops); r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); assert_se(r == 0); @@ -65,12 +68,12 @@ int main(int argc, char* argv[]) { unit_file_list_free(h); - log_error("enable"); + log_info("/*** enable **/"); r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); - log_error("enable2"); + log_info("/*** enable2 **/"); r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); @@ -82,8 +85,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); - log_error("disable"); - + log_info("/*** disable ***/"); changes = NULL; n_changes = 0; @@ -97,13 +99,13 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); - log_error("mask"); + log_info("/*** mask ***/"); changes = NULL; n_changes = 0; r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); - log_error("mask2"); + log_info("/*** mask2 ***/"); r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); @@ -114,13 +116,13 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); - log_error("unmask"); + log_info("/*** unmask ***/"); changes = NULL; n_changes = 0; r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); - log_error("unmask2"); + log_info("/*** unmask2 ***/"); r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); @@ -131,7 +133,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); - log_error("mask"); + log_info("/*** mask ***/"); changes = NULL; n_changes = 0; @@ -145,13 +147,13 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); - log_error("disable"); + log_info("/*** disable ***/"); changes = NULL; n_changes = 0; r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); - log_error("disable2"); + log_info("/*** disable2 ***/"); r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); @@ -162,7 +164,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); - log_error("umask"); + log_info("/*** umask ***/"); changes = NULL; n_changes = 0; @@ -176,7 +178,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); - log_error("enable files2"); + log_info("/*** enable files2 ***/"); changes = NULL; n_changes = 0; @@ -190,7 +192,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); - log_error("disable files2"); + log_info("/*** disable files2 ***/"); changes = NULL; n_changes = 0; @@ -203,7 +205,7 @@ int main(int argc, char* argv[]) { r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); - log_error("link files2"); + log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; @@ -217,7 +219,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); - log_error("disable files2"); + log_info("/*** disable files2 ***/"); changes = NULL; n_changes = 0; @@ -230,7 +232,7 @@ int main(int argc, char* argv[]) { r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); - log_error("link files2"); + log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; @@ -244,7 +246,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); - log_error("reenable files2"); + log_info("/*** reenable files2 ***/"); changes = NULL; n_changes = 0; @@ -258,7 +260,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); - log_error("disable files2"); + log_info("/*** disable files2 ***/"); changes = NULL; n_changes = 0; @@ -270,7 +272,7 @@ int main(int argc, char* argv[]) { r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); - log_error("preset files"); + log_info("/*** preset files ***/"); changes = NULL; n_changes = 0; diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index 0b2f9e9173..ff9f35cecd 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -69,8 +69,10 @@ static void test_netns(void) { int r, n = 0; siginfo_t si; - if (geteuid() > 0) - return; + if (geteuid() > 0) { + log_info("Skipping test: not root"); + exit(EXIT_TEST_SKIP); + } assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, s) >= 0); @@ -124,6 +126,9 @@ int main(int argc, char *argv[]) { char boot_id[SD_ID128_STRING_MAX]; _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL; + log_parse_environment(); + log_open(); + assert_se(sd_id128_get_boot(&bid) >= 0); sd_id128_to_string(bid, boot_id); diff --git a/src/test/test-nss.c b/src/test/test-nss.c index 9c13288d2e..55af592287 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -381,12 +381,18 @@ static void test_byaddr(void *handle, #ifdef HAVE_MYHOSTNAME # define MODULE1 "myhostname\0" +#else +# define MODULE1 #endif #ifdef HAVE_RESOLVED # define MODULE2 "resolve\0" +#else +# define MODULE2 #endif #ifdef HAVE_MACHINED # define MODULE3 "mymachines\0" +#else +# define MODULE3 #endif #define MODULE4 "dns\0" diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index 268da002a9..096326d176 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -26,41 +26,38 @@ #include "string-util.h" #include "strv.h" -static void test_paths(ManagerRunningAs running_as, bool personal) { +static void test_paths(UnitFileScope scope) { char template[] = "/tmp/test-path-lookup.XXXXXXX"; _cleanup_lookup_paths_free_ LookupPaths lp_without_env = {}; _cleanup_lookup_paths_free_ LookupPaths lp_with_env = {}; - char *exists, *not, *systemd_unit_path; + char *systemd_unit_path; assert_se(mkdtemp(template)); - exists = strjoina(template, "/exists"); - assert_se(mkdir(exists, 0755) == 0); - not = strjoina(template, "/not"); assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0); - assert_se(lookup_paths_init(&lp_without_env, running_as, personal, NULL, exists, not, not) == 0); - - assert_se(!strv_isempty(lp_without_env.unit_path)); - assert_se(strv_contains(lp_without_env.unit_path, exists)); - assert_se(strv_contains(lp_without_env.unit_path, not)); + assert_se(lookup_paths_init(&lp_without_env, scope, 0, NULL) >= 0); + assert_se(!strv_isempty(lp_without_env.search_path)); + assert_se(lookup_paths_reduce(&lp_without_env) >= 0); systemd_unit_path = strjoina(template, "/systemd-unit-path"); assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0); - assert_se(lookup_paths_init(&lp_with_env, running_as, personal, NULL, exists, not, not) == 0); - assert_se(strv_length(lp_with_env.unit_path) == 1); - assert_se(streq(lp_with_env.unit_path[0], systemd_unit_path)); + assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0); + assert_se(strv_length(lp_with_env.search_path) == 1); + assert_se(streq(lp_with_env.search_path[0], systemd_unit_path)); + assert_se(lookup_paths_reduce(&lp_with_env) >= 0); + assert_se(strv_length(lp_with_env.search_path) == 0); assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } -static void print_generator_paths(ManagerRunningAs running_as) { +static void print_generator_binary_paths(UnitFileScope scope) { _cleanup_strv_free_ char **paths; char **dir; - log_info("Generators dirs (%s):", running_as == MANAGER_SYSTEM ? "system" : "user"); + log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); - paths = generator_paths(running_as); + paths = generator_binary_paths(scope); STRV_FOREACH(dir, paths) log_info(" %s", *dir); } @@ -70,13 +67,12 @@ int main(int argc, char **argv) { log_parse_environment(); log_open(); - test_paths(MANAGER_SYSTEM, false); - test_paths(MANAGER_SYSTEM, true); - test_paths(MANAGER_USER, false); - test_paths(MANAGER_USER, true); + test_paths(UNIT_FILE_SYSTEM); + test_paths(UNIT_FILE_USER); + test_paths(UNIT_FILE_GLOBAL); - print_generator_paths(MANAGER_SYSTEM); - print_generator_paths(MANAGER_USER); + print_generator_binary_paths(UNIT_FILE_SYSTEM); + print_generator_binary_paths(UNIT_FILE_USER); return EXIT_SUCCESS; } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index d376dd56c5..5d77e2959c 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -90,6 +90,18 @@ static void test_path(void) { assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc")); assert_se(path_equal(path_kill_slashes(p3), "/./")); } + + assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo")); + assert_se(PATH_IN_SET("/bin", "/bin")); + assert_se(PATH_IN_SET("/bin", "/foo/bar", "/bin")); + assert_se(PATH_IN_SET("/", "/", "/", "/foo/bar")); + assert_se(!PATH_IN_SET("/", "/abc", "/def")); + + assert_se(path_equal_ptr(NULL, NULL)); + assert_se(path_equal_ptr("/a", "/a")); + assert_se(!path_equal_ptr("/a", "/b")); + assert_se(!path_equal_ptr("/a", NULL)); + assert_se(!path_equal_ptr(NULL, "/a")); } static void test_find_binary(const char *self) { diff --git a/src/test/test-path.c b/src/test/test-path.c index 1e704a03dc..435cafd83a 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -30,6 +30,7 @@ #include "string-util.h" #include "strv.h" #include "test-helper.h" +#include "tests.h" #include "unit.h" #include "util.h" @@ -44,7 +45,7 @@ static int setup_test(Manager **m) { assert_se(m); - r = manager_new(MANAGER_USER, true, &tmp); + r = manager_new(UNIT_FILE_USER, true, &tmp); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return -EXIT_TEST_SKIP; @@ -243,7 +244,7 @@ static void test_path_makedirectory_directorymode(Manager *m) { } int main(int argc, char *argv[]) { - test_function_t tests[] = { + static const test_function_t tests[] = { test_path_exists, test_path_existsglob, test_path_changed, @@ -253,12 +254,15 @@ int main(int argc, char *argv[]) { test_path_makedirectory_directorymode, NULL, }; - test_function_t *test = NULL; + + _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL; + const test_function_t *test = NULL; Manager *m = NULL; log_parse_environment(); log_open(); + assert_se(runtime_dir = setup_fake_runtime_dir()); assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0); for (test = tests; test && *test; test++) { diff --git a/src/test/test-rbtree.c b/src/test/test-rbtree.c deleted file mode 100644 index 8ae416c557..0000000000 --- a/src/test/test-rbtree.c +++ /dev/null @@ -1,362 +0,0 @@ -/*** - This file is part of systemd. See COPYING for details. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -/* - * Tests for RB-Tree - */ - -#undef NDEBUG -#include <assert.h> -#include <stddef.h> -#include <stdlib.h> -#include "c-rbtree.h" - -/* verify that all API calls are exported */ -static void test_api(void) { - CRBTree t = {}; - CRBNode n = C_RBNODE_INIT(n); - - assert(!c_rbnode_is_linked(&n)); - - /* init, is_linked, add, remove, remove_init */ - - c_rbtree_add(&t, NULL, &t.root, &n); - assert(c_rbnode_is_linked(&n)); - - c_rbtree_remove_init(&t, &n); - assert(!c_rbnode_is_linked(&n)); - - c_rbtree_add(&t, NULL, &t.root, &n); - assert(c_rbnode_is_linked(&n)); - - c_rbtree_remove(&t, &n); - assert(c_rbnode_is_linked(&n)); /* @n wasn't touched */ - - c_rbnode_init(&n); - assert(!c_rbnode_is_linked(&n)); - - /* first, last, leftmost, rightmost, next, prev */ - - assert(!c_rbtree_first(&t)); - assert(!c_rbtree_last(&t)); - assert(&n == c_rbnode_leftmost(&n)); - assert(&n == c_rbnode_rightmost(&n)); - assert(!c_rbnode_next(&n)); - assert(!c_rbnode_prev(&n)); -} - -/* copied from c-rbtree.c, relies on internal representation */ -static inline _Bool c_rbnode_is_red(CRBNode *n) { - return !((unsigned long)n->__parent_and_color & 1UL); -} - -/* copied from c-rbtree.c, relies on internal representation */ -static inline _Bool c_rbnode_is_black(CRBNode *n) { - return !!((unsigned long)n->__parent_and_color & 1UL); -} - -static size_t validate(CRBTree *t) { - unsigned int i_black, n_black; - CRBNode *n, *p, *o; - size_t count = 0; - - assert(t); - assert(!t->root || c_rbnode_is_black(t->root)); - - /* traverse to left-most child, count black nodes */ - i_black = 0; - n = t->root; - while (n && n->left) { - if (c_rbnode_is_black(n)) - ++i_black; - n = n->left; - } - n_black = i_black; - - /* - * Traverse tree and verify correctness: - * 1) A node is either red or black - * 2) The root is black - * 3) All leaves are black - * 4) Every red node must have two black child nodes - * 5) Every path to a leaf contains the same number of black nodes - * - * Note that NULL nodes are considered black, which is why we don't - * check for 3). - */ - o = NULL; - while (n) { - ++count; - - /* verify natural order */ - assert(n > o); - o = n; - - /* verify consistency */ - assert(!n->right || c_rbnode_parent(n->right) == n); - assert(!n->left || c_rbnode_parent(n->left) == n); - - /* verify 2) */ - if (!c_rbnode_parent(n)) - assert(c_rbnode_is_black(n)); - - if (c_rbnode_is_red(n)) { - /* verify 4) */ - assert(!n->left || c_rbnode_is_black(n->left)); - assert(!n->right || c_rbnode_is_black(n->right)); - } else { - /* verify 1) */ - assert(c_rbnode_is_black(n)); - } - - /* verify 5) */ - if (!n->left && !n->right) - assert(i_black == n_black); - - /* get next node */ - if (n->right) { - n = n->right; - if (c_rbnode_is_black(n)) - ++i_black; - - while (n->left) { - n = n->left; - if (c_rbnode_is_black(n)) - ++i_black; - } - } else { - while ((p = c_rbnode_parent(n)) && n == p->right) { - n = p; - if (c_rbnode_is_black(p->right)) - --i_black; - } - - n = p; - if (p && c_rbnode_is_black(p->left)) - --i_black; - } - } - - return count; -} - -static void insert(CRBTree *t, CRBNode *n) { - CRBNode **i, *p; - - assert(t); - assert(n); - assert(!c_rbnode_is_linked(n)); - - i = &t->root; - p = NULL; - while (*i) { - p = *i; - if (n < *i) { - i = &(*i)->left; - } else { - assert(n > *i); - i = &(*i)->right; - } - } - - c_rbtree_add(t, p, i, n); -} - -static void shuffle(void **nodes, size_t n_memb) { - unsigned int i, j; - void *t; - - for (i = 0; i < n_memb; ++i) { - j = rand() % n_memb; - t = nodes[j]; - nodes[j] = nodes[i]; - nodes[i] = t; - } -} - -/* run some pseudo-random tests on the tree */ -static void test_shuffle(void) { - CRBNode *nodes[256]; - CRBTree t = {}; - unsigned int i, j; - size_t n; - - /* allocate and initialize all nodes */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - nodes[i] = malloc(sizeof(*nodes[i])); - assert(nodes[i]); - c_rbnode_init(nodes[i]); - } - - /* shuffle nodes and validate *empty* tree */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - n = validate(&t); - assert(n == 0); - - /* add all nodes and validate after each insertion */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - insert(&t, nodes[i]); - n = validate(&t); - assert(n == i + 1); - } - - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove all nodes (in different order) and validate on each round */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - c_rbtree_remove(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); - c_rbnode_init(nodes[i]); - } - - /* shuffle nodes and validate *empty* tree again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - n = validate(&t); - assert(n == 0); - - /* add all nodes again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - insert(&t, nodes[i]); - n = validate(&t); - assert(n == i + 1); - } - - /* 4 times, remove half of the nodes and add them again */ - for (j = 0; j < 4; ++j) { - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove half of the nodes */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) { - c_rbtree_remove(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); - c_rbnode_init(nodes[i]); - } - - /* shuffle the removed half */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes) / 2); - - /* add the removed half again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) { - insert(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1); - } - } - - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove all */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - c_rbtree_remove(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); - c_rbnode_init(nodes[i]); - } - - /* free nodes again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) - free(nodes[i]); -} - -typedef struct { - unsigned long key; - CRBNode rb; -} Node; - -#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb))) - -static int compare(CRBTree *t, void *k, CRBNode *n) { - unsigned long key = (unsigned long)k; - Node *node = node_from_rb(n); - - return (key < node->key) ? -1 : (key > node->key) ? 1 : 0; -} - -/* run tests against the c_rbtree_find*() helpers */ -static void test_map(void) { - CRBNode **slot, *p; - CRBTree t = {}; - Node *nodes[2048]; - unsigned long i; - - /* allocate and initialize all nodes */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - nodes[i] = malloc(sizeof(*nodes[i])); - assert(nodes[i]); - nodes[i]->key = i; - c_rbnode_init(&nodes[i]->rb); - } - - /* shuffle nodes */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* add all nodes, and verify that each node is linked */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - assert(!c_rbnode_is_linked(&nodes[i]->rb)); - assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - - slot = c_rbtree_find_slot(&t, compare, (void *)nodes[i]->key, &p); - assert(slot); - c_rbtree_add(&t, p, slot, &nodes[i]->rb); - - assert(c_rbnode_is_linked(&nodes[i]->rb)); - assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - } - - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove all nodes (in different order) */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - assert(c_rbnode_is_linked(&nodes[i]->rb)); - assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - - c_rbtree_remove_init(&t, &nodes[i]->rb); - - assert(!c_rbnode_is_linked(&nodes[i]->rb)); - assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - } - - /* free nodes again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) - free(nodes[i]); -} - -int main(int argc, char **argv) { - unsigned int i; - - /* we want stable tests, so use fixed seed */ - srand(0xdeadbeef); - - test_api(); - - /* - * The tests are pseudo random; run them multiple times, each run will - * have different orders and thus different results. - */ - for (i = 0; i < 4; ++i) { - test_shuffle(); - test_map(); - } - - return 0; -} diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 7f515b53d8..3e9caafc71 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -21,9 +21,12 @@ #include "macro.h" #include "manager.h" +#include "rm-rf.h" #include "test-helper.h" +#include "tests.h" int main(int argc, char *argv[]) { + _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL; Manager *m = NULL; Unit *idle_ok, *idle_bad, *rr_ok, *rr_bad, *rr_sched; Service *ser; @@ -31,9 +34,11 @@ int main(int argc, char *argv[]) { FDSet *fdset = NULL; int r; + assert_se(runtime_dir = setup_fake_runtime_dir()); + /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; diff --git a/src/test/test-strv.c b/src/test/test-strv.c index fea1f848cd..fc01dcfaf1 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -358,7 +358,7 @@ static void test_strv_extend_strv_concat(void) { } static void test_strv_extend_strv(void) { - _cleanup_strv_free_ char **a = NULL, **b = NULL; + _cleanup_strv_free_ char **a = NULL, **b = NULL, **n = NULL; a = strv_new("abc", "def", "ghi", NULL); b = strv_new("jkl", "mno", "abc", "pqr", NULL); @@ -373,8 +373,14 @@ static void test_strv_extend_strv(void) { assert_se(streq(a[3], "jkl")); assert_se(streq(a[4], "mno")); assert_se(streq(a[5], "pqr")); - assert_se(strv_length(a) == 6); + + assert_se(strv_extend_strv(&n, b, false) >= 0); + assert_se(streq(n[0], "jkl")); + assert_se(streq(n[1], "mno")); + assert_se(streq(n[2], "abc")); + assert_se(streq(n[3], "pqr")); + assert_se(strv_length(n) == 4); } static void test_strv_extend(void) { diff --git a/src/test/test-udev.c b/src/test/test-udev.c index 8522e9925c..e965b4494a 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -27,6 +27,7 @@ #include <unistd.h> #include "fs-util.h" +#include "log.h" #include "missing.h" #include "selinux-util.h" #include "signal-util.h" @@ -39,39 +40,31 @@ static int fake_filesystems(void) { const char *src; const char *target; const char *error; + bool ignore_mount_error; } fakefss[] = { - { "test/tmpfs/sys", "/sys", "failed to mount test /sys" }, - { "test/tmpfs/dev", "/dev", "failed to mount test /dev" }, - { "test/run", "/run", "failed to mount test /run" }, - { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d" }, - { "test/run", UDEVLIBEXECDIR "/rules.d","failed to mount empty " UDEVLIBEXECDIR "/rules.d" }, + { "test/tmpfs/sys", "/sys", "failed to mount test /sys", false }, + { "test/tmpfs/dev", "/dev", "failed to mount test /dev", false }, + { "test/run", "/run", "failed to mount test /run", false }, + { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d", true }, + { "test/run", UDEVLIBEXECDIR "/rules.d", "failed to mount empty " UDEVLIBEXECDIR "/rules.d", true }, }; unsigned int i; - int err; - err = unshare(CLONE_NEWNS); - if (err < 0) { - err = -errno; - fprintf(stderr, "failed to call unshare(): %m\n"); - goto out; - } + if (unshare(CLONE_NEWNS) < 0) + return log_error_errno(errno, "failed to call unshare(): %m"); - if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0) { - err = -errno; - fprintf(stderr, "failed to mount / as private: %m\n"); - goto out; - } + if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0) + return log_error_errno(errno, "failed to mount / as private: %m"); for (i = 0; i < ELEMENTSOF(fakefss); i++) { - err = mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL); - if (err < 0) { - err = -errno; - fprintf(stderr, "%s %m\n", fakefss[i].error); - return err; + if (mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL) < 0) { + log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "%s: %m", fakefss[i].error); + if (!fakefss[i].ignore_mount_error) + return -errno; } } -out: - return err; + + return 0; } int main(int argc, char *argv[]) { @@ -84,6 +77,9 @@ int main(int argc, char *argv[]) { const char *action; int err; + log_parse_environment(); + log_open(); + err = fake_filesystems(); if (err < 0) return EXIT_FAILURE; diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index cc6c61ba63..114ddf8478 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -35,10 +35,12 @@ #include "install.h" #include "load-fragment.h" #include "macro.h" +#include "rm-rf.h" #include "specifier.h" #include "string-util.h" #include "strv.h" #include "test-helper.h" +#include "tests.h" #include "user-util.h" #include "util.h" @@ -113,7 +115,7 @@ static void test_config_parse_exec(void) { Manager *m = NULL; Unit *u = NULL; - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return; @@ -840,11 +842,14 @@ static void test_config_parse_pass_environ(void) { } int main(int argc, char *argv[]) { + _cleanup_(rm_rf_and_freep) char *runtime_dir = NULL; int r; log_parse_environment(); log_open(); + assert_se(runtime_dir = setup_fake_runtime_dir()); + r = test_unit_file_get_set(); test_config_parse_exec(); test_config_parse_capability_set(); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 3de94ef425..2fd83f321c 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -209,7 +209,7 @@ static int test_unit_printf(void) { assert_se(get_home_dir(&home) >= 0); assert_se(get_shell(&shell) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index efd264b34d..2053d35a67 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -94,6 +94,7 @@ typedef enum ItemType { /* These ones take globs */ WRITE_FILE = 'w', + EMPTY_DIRECTORY = 'e', SET_XATTR = 't', RECURSIVE_SET_XATTR = 'T', SET_ACL = 'a', @@ -179,6 +180,7 @@ static bool needs_glob(ItemType t) { IGNORE_DIRECTORY_PATH, REMOVE_PATH, RECURSIVE_REMOVE_PATH, + EMPTY_DIRECTORY, ADJUST_MODE, RELABEL_PATH, RECURSIVE_RELABEL_PATH, @@ -195,6 +197,7 @@ static bool takes_ownership(ItemType t) { CREATE_FILE, TRUNCATE_FILE, CREATE_DIRECTORY, + EMPTY_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, @@ -1217,7 +1220,6 @@ static int create_item(Item *i) { case CREATE_SUBVOLUME: case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: - RUN_WITH_UMASK(0000) mkdir_parents_label(i->path, 0755); @@ -1276,11 +1278,11 @@ static int create_item(Item *i) { if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); if (r == -ENOTTY) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path); + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); else if (r == -EROFS) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path); + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); else if (r == -ENOPROTOOPT) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path); + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); else if (r < 0) q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); else if (r > 0) @@ -1289,6 +1291,9 @@ static int create_item(Item *i) { log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); } + /* fall through */ + + case EMPTY_DIRECTORY: r = path_set_perms(i, i->path); if (q < 0) return q; @@ -1298,7 +1303,6 @@ static int create_item(Item *i) { break; case CREATE_FIFO: - RUN_WITH_UMASK(0000) { mac_selinux_create_file_prepare(i->path, S_IFIFO); r = mkfifo(i->path, i->mode); @@ -1535,47 +1539,20 @@ static int remove_item_instance(Item *i, const char *instance) { } static int remove_item(Item *i) { - int r = 0; - assert(i); log_debug("Running remove action for entry %c %s", (char) i->type, i->path); switch (i->type) { - case CREATE_FILE: - case TRUNCATE_FILE: - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - case CREATE_FIFO: - case CREATE_SYMLINK: - case CREATE_CHAR_DEVICE: - case CREATE_BLOCK_DEVICE: - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case ADJUST_MODE: - case RELABEL_PATH: - case RECURSIVE_RELABEL_PATH: - case WRITE_FILE: - case COPY_FILES: - case SET_XATTR: - case RECURSIVE_SET_XATTR: - case SET_ACL: - case RECURSIVE_SET_ACL: - case SET_ATTRIBUTE: - case RECURSIVE_SET_ATTRIBUTE: - break; - case REMOVE_PATH: case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - r = glob_item(i, remove_item_instance, false); - break; - } + return glob_item(i, remove_item_instance, false); - return r; + default: + return 0; + } } static int clean_item_instance(Item *i, const char* instance) { @@ -1630,8 +1607,6 @@ static int clean_item_instance(Item *i, const char* instance) { } static int clean_item(Item *i) { - int r = 0; - assert(i); log_debug("Running clean action for entry %c %s", (char) i->type, i->path); @@ -1641,19 +1616,17 @@ static int clean_item(Item *i) { case CREATE_SUBVOLUME: case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: case TRUNCATE_DIRECTORY: case IGNORE_PATH: case COPY_FILES: clean_item_instance(i, i->path); - break; + return 0; case IGNORE_DIRECTORY_PATH: - r = glob_item(i, clean_item_instance, false); - break; + return glob_item(i, clean_item_instance, false); default: - break; + return 0; } - - return r; } static int process_item_array(ItemArray *array); @@ -1879,6 +1852,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { case CREATE_SUBVOLUME: case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: @@ -2198,7 +2172,8 @@ static int parse_argv(int argc, char *argv[]) { } static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; + _cleanup_fclose_ FILE *_f = NULL; + FILE *f; char line[LINE_MAX]; Iterator iterator; unsigned v = 0; @@ -2207,16 +2182,23 @@ static int read_config_file(const char *fn, bool ignore_enoent) { assert(fn); - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) { - log_debug_errno(r, "Failed to open \"%s\": %m", fn); - return 0; - } + if (streq(fn, "-")) { + log_debug("Reading config from stdin."); + fn = "<stdin>"; + f = stdin; + } else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) { + log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); + return 0; + } - return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); + return log_error_errno(r, "Failed to open '%s': %m", fn); + } + log_debug("Reading config file \"%s\".", fn); + f = _f; } - log_debug("Reading config file \"%s\".", fn); FOREACH_LINE(line, f, break) { char *l; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index b5f7f0d512..c0ef073476 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -40,7 +40,7 @@ static void sig_handler(int signum) { static void print_device(struct udev_device *device, const char *source, int prop) { struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n", source, ts.tv_sec, ts.tv_nsec/1000, diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 243df7386f..e9dd2f47c7 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -400,10 +400,11 @@ static void worker_spawn(Manager *manager, struct event *event) { goto out; } - /* request TERM signal if parent exits */ - prctl(PR_SET_PDEATHSIG, SIGTERM); + /* Request TERM signal if parent exits. + Ignore error, not much we can do in that case. */ + (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - /* reset OOM score, we only protect the main daemon */ + /* Reset OOM score, we only protect the main daemon. */ write_string_file("/proc/self/oom_score_adj", "0", 0); for (;;) { |