diff options
author | Martin Pitt <martin.pitt@ubuntu.com> | 2016-04-12 18:20:13 +0200 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2016-04-12 18:20:13 +0200 |
commit | 025ef1d2264b24e77283d312dede8af01fa050f6 (patch) | |
tree | 3dd77d23d19943926f36cb60b1685a4fb657bc89 /src | |
parent | 1116e14c49ac1291eb5e46fcf5e691ef57267b54 (diff) | |
parent | 3c6d8e57e870ed56dbb53ce62f0a3d32de101cd8 (diff) |
Merge pull request #2973 from poettering/search-path
Many fixes, in particular to the install logic
Diffstat (limited to 'src')
80 files changed, 2250 insertions, 2609 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/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/util.c b/src/basic/util.c index f1e3bd5b48..957b0e1ff1 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" @@ -777,15 +778,24 @@ 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 (param) { - r = write_string_file(REBOOT_PARAM_FILE, 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); + 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"); + } + + return 0; + } + + RUN_WITH_UMASK(0022) + r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); + if (r < 0) + 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..2392ed6c16 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; @@ -1613,6 +1607,19 @@ fail: return r; } +static int install_error(sd_bus_error *error, int c) { + assert(c < 0); + + if (c == -ESHUTDOWN) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked."); + if (c == -EADDRNOTAVAIL) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit file is transient or generated."); + if (c == -ELOOP) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, "Refusing to operate on linked unit file."); + + return c; +} + static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, @@ -1624,7 +1631,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 +1650,11 @@ 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"); - if (r < 0) - return r; + r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes); + if (r < 0) { + unit_file_changes_free(changes, n_changes); + return install_error(error, r); + } return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes); } @@ -1686,7 +1690,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,11 +1718,11 @@ 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); - if (r < 0) - return r; + r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes); + if (r < 0) { + unit_file_changes_free(changes, n_changes); + return install_error(error, r); + } return reply_unit_file_changes_and_free(m, message, r, changes, n_changes); } @@ -1734,7 +1737,6 @@ static int method_disable_unit_files_generic( _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int r, runtime; assert(message); @@ -1748,17 +1750,17 @@ 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); - if (r < 0) - return r; + r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes); + if (r < 0) { + unit_file_changes_free(changes, n_changes); + return install_error(error, r); + } return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1771,11 +1773,39 @@ 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) { + unit_file_changes_free(changes, n_changes); + return install_error(error, r); + } + + 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 +1826,11 @@ 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); - if (r < 0) - return r; + r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes); + if (r < 0) { + unit_file_changes_free(changes, n_changes); + return install_error(error, r); + } return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1810,7 +1840,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,12 +1868,10 @@ 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); + r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes); if (r < 0) { unit_file_changes_free(changes, n_changes); - return r; + return install_error(error, r); } return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); @@ -1855,7 +1882,6 @@ 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; @@ -1882,13 +1908,11 @@ 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"); - if (r < 0) - return r; + r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); + if (r < 0) { + unit_file_changes_free(changes, n_changes); + return install_error(error, r); + } return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1924,7 +1948,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 +2049,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-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 a54f0d764c..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; @@ -3583,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 diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 7b25349c07..86da16c31e 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -259,9 +259,8 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) { /* Hmm, we couldn't write it? So let's write it to * /run/machine-id as a replacement */ - RUN_WITH_UMASK(0022) { + 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); diff --git a/src/core/main.c b/src/core/main.c index 56df32426a..2912608435 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"); @@ -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.c b/src/core/mount.c index 0fd880df5d..74ab54bfd0 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); @@ -400,7 +399,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 @@ -1396,7 +1395,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 +1423,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 426c4ad299..5e7b3eb234 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -318,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 c5cbf0f152..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. */ @@ -920,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 @@ -1211,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; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 96679c920f..e14755d84e 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -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..a51a67ea11 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) diff --git a/src/core/transaction.c b/src/core/transaction.c index c894001cf9..dad387749c 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", */ 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 70175557f7..fb88260d23 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; - p = dirname_malloc(*i); - if (p) - (void) rmdir(p); + /* Only drop transient drop-ins */ + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; + + (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; @@ -2884,7 +2909,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; @@ -3040,8 +3065,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; @@ -3158,7 +3182,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); @@ -3174,7 +3198,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)); @@ -3225,7 +3249,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); @@ -3237,7 +3261,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) || @@ -3315,59 +3339,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; - r = write_drop_in(dir, u->id, 50, name, data); + 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 = 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); @@ -3398,7 +3425,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); @@ -3410,9 +3437,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, NULL); return unit_write_drop_in(u, mode, name, ndata); } @@ -3440,24 +3465,50 @@ 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/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/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-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 841358ed03..9fd744768d 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; 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-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..c7ff0efac8 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( diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 1d3264a1de..35177aa29e 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" @@ -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" @@ -2635,6 +2680,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-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-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/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 155be9946f..b3018e8239 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -230,7 +230,6 @@ static void test_dnssec_verify_rrset2(void) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; DnssecResult result; - int r; nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); assert_se(nsec); 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 0f08137f19..8d9f96553b 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; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(ret); +static const char* skip_root(const LookupPaths *p, const char *path) { + char *e; - /* 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(p->generator, parent) || + path_equal(p->generator_early, parent) || + path_equal(p->generator_late, parent); +} - 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(p->transient, parent); } -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(parent, p->persistent_control) || + path_equal(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(parent, p->persistent_config) || + path_equal(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; + + return path_equal(parent, p->runtime_config) || + path_equal(parent, p->generator) || + path_equal(parent, p->generator_early) || + path_equal(parent, p->generator_late) || + path_equal(parent, p->transient) || + path_equal(parent, p->runtime_control); +} - assert(root_dir); +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( @@ -353,6 +356,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 +369,7 @@ static int remove_marked_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(restart); d = fdopendir(fd); @@ -400,12 +405,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; @@ -417,19 +423,16 @@ static int remove_marked_symlinks_fd( return -ENOMEM; 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) || @@ -439,7 +442,7 @@ static int remove_marked_symlinks_fd( if (!found) continue; - if (unlink(p) < 0 && errno != ENOENT) { + if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { if (r == 0) r = -errno; continue; @@ -450,7 +453,11 @@ static int remove_marked_symlinks_fd( 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 +471,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 +480,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 +498,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 +512,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 +523,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 +557,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 +635,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 +652,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 +679,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 +746,23 @@ 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) { + 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) + return -ESHUTDOWN; + if (path_is_generator(paths, i->path)) + return -EADDRNOTAVAIL; + if (path_is_transient(paths, i->path)) + return -EADDRNOTAVAIL; + + return 0; +} + static int install_info_add( InstallContext *c, const char *name, @@ -804,20 +825,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 +907,6 @@ static int unit_file_load( InstallContext *c, UnitFileInstallInfo *info, const char *path, - const char *root_dir, SearchFlags flags) { const ConfigTableItem items[] = { @@ -921,8 +927,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 +987,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 +1033,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 +1052,6 @@ static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, - const char *root_dir, SearchFlags flags) { char **p; @@ -1056,18 +1066,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 +1100,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 +1153,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 +1166,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 +1177,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 +1219,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 +1233,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 +1267,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 +1286,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 +1301,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 +1313,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 +1327,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 +1348,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 +1363,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 +1377,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,7 +1390,7 @@ static int install_info_symlink_link( assert(config_path); assert(i->path); - r = in_search_path(i->path, paths->unit_path); + r = in_search_path(paths, i->path); if (r != 0) return r; @@ -1356,14 +1398,15 @@ static int install_info_symlink_link( 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,17 +1420,17 @@ 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); + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); if (r == 0) r = q; @@ -1399,7 +1442,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,14 +1469,14 @@ 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; @@ -1451,8 +1493,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 +1517,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 +1541,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 +1565,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 +1585,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 +1631,28 @@ 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) + if (errno != ENOENT && r >= 0) r = -errno; - } else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - if (q < 0) - return q; - 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 +1669,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 +1696,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 +1709,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 +1725,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 +1741,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 +1930,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 +1945,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); 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); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); @@ -1776,7 +1987,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 +2001,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 +2009,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); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); } @@ -1825,7 +2031,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 +2044,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 +2067,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 +2112,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); + 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, name, 0, &i); if (r < 0) return r; - - r = get_config_path(scope, false, root_dir, &config_path); + r = install_info_may_process(i, &paths); if (r < 0) return r; - r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i); - 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 +2157,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); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; n = strdup(i->name); if (!n) @@ -1986,9 +2176,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 +2193,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 +2204,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 +2263,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 +2298,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 +2380,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 +2396,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,7 +2408,7 @@ 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; @@ -2210,7 +2425,6 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - const char *root_dir, UnitFilePresetMode mode, const char *name) { @@ -2221,19 +2435,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); + 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 +2465,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 +2473,22 @@ int unit_file_preset( 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, 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); 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 +2502,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 +2510,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); - 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, 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 +2538,13 @@ 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); + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name); 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 +2579,15 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - 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; - - 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 +2613,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 +2642,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", }; diff --git a/src/shared/install.h b/src/shared/install.h index c1a43e23e7..6f8b45d4f6 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 @@ -126,17 +128,18 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { 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_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_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret); 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); 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..ca69a0aef2 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,299 @@ 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/", NULL); + 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 */ + char **l = 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; - /* First priority is whatever has been passed to us via env - * vars */ + 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 */ 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, &l); if (r < 0) return r; } else - p->unit_path = NULL; + l = NULL; - if (!p->unit_path || append) { + if (!l || 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 +523,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 +571,251 @@ 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; + + case UNIT_FILE_USER: + add = user_dirs(persistent_config, runtime_config, + generator, generator_early, generator_late, + transient, + persistent_config, runtime_control); + break; - if (!unit_path) + default: + assert_not_reached("Hmm, unexpected scope?"); + } + + if (!add) return -ENOMEM; - r = strv_extend_strv(&p->unit_path, unit_path, false); - if (r < 0) - return r; + if (l) { + r = strv_extend_strv(&l, add, false); + if (r < 0) + return r; + } else { + l = add; + add = NULL; + } } - 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(l, 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(l); + l = 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; - if (strv_isempty(p->sysvrcnd_path)) { - strv_free(p->sysvrcnd_path); + return 0; +} - p->sysvrcnd_path = strv_new( - SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */ - NULL); - if (!p->sysvrcnd_path) - return -ENOMEM; +void lookup_paths_free(LookupPaths *p) { + if (!p) + return; + + 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); - zero(*paths); + /* 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); - return lookup_paths_init(paths, - scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, - scope == UNIT_FILE_USER, - root_dir, - NULL, NULL, NULL); + /* 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); + + 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); + + 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/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..f0e788a508 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1993,7 +1993,7 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha 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); + log_info("Removed %s.", changes[i].path); } } @@ -2295,7 +2295,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 +2395,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; } @@ -3136,7 +3136,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; @@ -4780,34 +4780,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 +4798,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 +4810,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) @@ -5253,8 +5223,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 +5240,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 +5271,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; - - path = path_join(arg_root, *k, name); - if (!path) - return log_oom(); + j = unit_file_exists(arg_scope, &paths, name); + if (j < 0 && !IN_SET(j, -ELOOP, -ESHUTDOWN, -EADDRNOTAVAIL)) + return log_error_errno(j, "Failed to lookup unit file state: %m"); + found_native = j != 0; - 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 +5291,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 +5306,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 +5318,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 +5340,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 +5415,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,11 +5437,17 @@ 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."); + if (r == -EADDRNOTAVAIL) + return log_error_errno(r, "Unit file is transient or generated."); + if (r == -ELOOP) + return log_error_errno(r, "Refusing to operate on linked unit file."); if (r < 0) return log_error_errno(r, "Operation failed: %m"); @@ -5484,7 +5459,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { _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 +5494,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 +5520,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); @@ -5639,6 +5619,8 @@ static int add_dependency(int argc, char *argv[], void *userdata) { 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 == -EADDRNOTAVAIL) + return log_error_errno(r, "Unit file is transient or generated."); if (r < 0) return log_error_errno(r, "Can't add dependency: %m"); @@ -5783,7 +5765,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 +5801,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 +5809,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 +5879,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 +5915,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 +5929,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 +5946,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 +5960,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 +6075,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,7 +6082,7 @@ 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; @@ -6137,9 +6102,9 @@ 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; @@ -6324,6 +6289,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" @@ -6992,7 +6959,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 +7414,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 +7461,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 +7488,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..9392a3d2b5 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, -ESHUTDOWN, -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..2138655e29 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); @@ -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-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.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-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; |